'use strict';

const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');

//#region lib/rules/block-tag-newline.js
/**
* @fileoverview Enforce line breaks style after opening and before closing block-level tags.
* @author Yosuke Ota
*/
var require_block_tag_newline = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
	const utils = require_index.default;
	/**
	* @typedef { 'always' | 'never' | 'consistent' | 'ignore' } OptionType
	* @typedef { { singleline?: OptionType, multiline?: OptionType, maxEmptyLines?: number } } ContentsOptions
	* @typedef { ContentsOptions & { blocks?: { [element: string]: ContentsOptions } } } Options
	* @typedef { Required<ContentsOptions> } ArgsOptions
	*/
	/**
	* @param {string} text Source code as a string.
	* @returns {number}
	*/
	function getLinebreakCount(text) {
		return text.split(/\r\n|[\r\n\u2028\u2029]/gu).length - 1;
	}
	/**
	* @param {number} lineBreaks
	*/
	function getPhrase(lineBreaks) {
		switch (lineBreaks) {
			case 1: return "1 line break";
			default: return `${lineBreaks} line breaks`;
		}
	}
	const ENUM_OPTIONS = { enum: [
		"always",
		"never",
		"consistent",
		"ignore"
	] };
	module.exports = {
		meta: {
			type: "layout",
			docs: {
				description: "enforce line breaks after opening and before closing block-level tags",
				categories: void 0,
				url: "https://eslint.vuejs.org/rules/block-tag-newline.html"
			},
			fixable: "whitespace",
			schema: [{
				type: "object",
				properties: {
					singleline: ENUM_OPTIONS,
					multiline: ENUM_OPTIONS,
					maxEmptyLines: {
						type: "number",
						minimum: 0
					},
					blocks: {
						type: "object",
						patternProperties: { "^(?:\\S+)$": {
							type: "object",
							properties: {
								singleline: ENUM_OPTIONS,
								multiline: ENUM_OPTIONS,
								maxEmptyLines: {
									type: "number",
									minimum: 0
								}
							},
							additionalProperties: false
						} },
						additionalProperties: false
					}
				},
				additionalProperties: false
			}],
			messages: {
				unexpectedOpeningLinebreak: "There should be no line break after '<{{tag}}>'.",
				expectedOpeningLinebreak: "Expected {{expected}} after '<{{tag}}>', but {{actual}} found.",
				expectedClosingLinebreak: "Expected {{expected}}  before '</{{tag}}>', but {{actual}} found.",
				missingOpeningLinebreak: "A line break is required after '<{{tag}}>'.",
				missingClosingLinebreak: "A line break is required before '</{{tag}}>'."
			}
		},
		create(context) {
			const sourceCode = context.sourceCode;
			const df = sourceCode.parserServices.getDocumentFragment && sourceCode.parserServices.getDocumentFragment();
			if (!df) return {};
			/**
			* @param {VStartTag} startTag
			* @param {string} beforeText
			* @param {number} beforeLinebreakCount
			* @param {'always' | 'never'} beforeOption
			* @param {number} maxEmptyLines
			* @returns {void}
			*/
			function verifyBeforeSpaces(startTag, beforeText, beforeLinebreakCount, beforeOption, maxEmptyLines) {
				if (beforeOption === "always") {
					if (beforeLinebreakCount === 0) context.report({
						loc: {
							start: startTag.loc.end,
							end: startTag.loc.end
						},
						messageId: "missingOpeningLinebreak",
						data: { tag: startTag.parent.name },
						fix(fixer) {
							return fixer.insertTextAfter(startTag, "\n");
						}
					});
					else if (maxEmptyLines < beforeLinebreakCount - 1) context.report({
						loc: {
							start: startTag.loc.end,
							end: sourceCode.getLocFromIndex(startTag.range[1] + beforeText.length)
						},
						messageId: "expectedOpeningLinebreak",
						data: {
							tag: startTag.parent.name,
							expected: getPhrase(maxEmptyLines + 1),
							actual: getPhrase(beforeLinebreakCount)
						},
						fix(fixer) {
							return fixer.replaceTextRange([startTag.range[1], startTag.range[1] + beforeText.length], "\n".repeat(maxEmptyLines + 1));
						}
					});
				} else if (beforeLinebreakCount > 0) context.report({
					loc: {
						start: startTag.loc.end,
						end: sourceCode.getLocFromIndex(startTag.range[1] + beforeText.length)
					},
					messageId: "unexpectedOpeningLinebreak",
					data: { tag: startTag.parent.name },
					fix(fixer) {
						return fixer.removeRange([startTag.range[1], startTag.range[1] + beforeText.length]);
					}
				});
			}
			/**
			* @param {VEndTag} endTag
			* @param {string} afterText
			* @param {number} afterLinebreakCount
			* @param {'always' | 'never'} afterOption
			* @param {number} maxEmptyLines
			* @returns {void}
			*/
			function verifyAfterSpaces(endTag, afterText, afterLinebreakCount, afterOption, maxEmptyLines) {
				if (afterOption === "always") {
					if (afterLinebreakCount === 0) context.report({
						loc: {
							start: endTag.loc.start,
							end: endTag.loc.start
						},
						messageId: "missingClosingLinebreak",
						data: { tag: endTag.parent.name },
						fix(fixer) {
							return fixer.insertTextBefore(endTag, "\n");
						}
					});
					else if (maxEmptyLines < afterLinebreakCount - 1) context.report({
						loc: {
							start: sourceCode.getLocFromIndex(endTag.range[0] - afterText.length),
							end: endTag.loc.start
						},
						messageId: "expectedClosingLinebreak",
						data: {
							tag: endTag.parent.name,
							expected: getPhrase(maxEmptyLines + 1),
							actual: getPhrase(afterLinebreakCount)
						},
						fix(fixer) {
							return fixer.replaceTextRange([endTag.range[0] - afterText.length, endTag.range[0]], "\n".repeat(maxEmptyLines + 1));
						}
					});
				} else if (afterLinebreakCount > 0) context.report({
					loc: {
						start: sourceCode.getLocFromIndex(endTag.range[0] - afterText.length),
						end: endTag.loc.start
					},
					messageId: "unexpectedOpeningLinebreak",
					data: { tag: endTag.parent.name },
					fix(fixer) {
						return fixer.removeRange([endTag.range[0] - afterText.length, endTag.range[0]]);
					}
				});
			}
			/**
			* @param {VElement} element
			* @param {ArgsOptions} options
			* @returns {void}
			*/
			function verifyElement(element, options) {
				const { startTag, endTag } = element;
				if (startTag.selfClosing || endTag == null) return;
				const text = sourceCode.text.slice(startTag.range[1], endTag.range[0]);
				if (!text.trim()) return;
				const option = options.multiline !== options.singleline && /[\n\r\u2028\u2029]/u.test(text.trim()) ? options.multiline : options.singleline;
				if (option === "ignore") return;
				const beforeText = /^\s*/u.exec(text)[0];
				const afterText = /\s*$/u.exec(text)[0];
				const beforeLinebreakCount = getLinebreakCount(beforeText);
				const afterLinebreakCount = getLinebreakCount(afterText);
				/** @type {'always' | 'never'} */
				let beforeOption;
				/** @type {'always' | 'never'} */
				let afterOption;
				if (option === "always" || option === "never") {
					beforeOption = option;
					afterOption = option;
				} else {
					if (beforeLinebreakCount > 0 === afterLinebreakCount > 0) return;
					beforeOption = "always";
					afterOption = "always";
				}
				verifyBeforeSpaces(startTag, beforeText, beforeLinebreakCount, beforeOption, options.maxEmptyLines);
				verifyAfterSpaces(endTag, afterText, afterLinebreakCount, afterOption, options.maxEmptyLines);
			}
			/**
			* Normalizes a given option value.
			* @param { Options | undefined } option An option value to parse.
			* @returns { (element: VElement) => void } Verify function.
			*/
			function normalizeOptionValue(option) {
				if (!option) return normalizeOptionValue({});
				/** @type {ContentsOptions} */
				const contentsOptions = option;
				/** @type {ArgsOptions} */
				const options = {
					singleline: contentsOptions.singleline || "consistent",
					multiline: contentsOptions.multiline || "always",
					maxEmptyLines: contentsOptions.maxEmptyLines || 0
				};
				const { blocks } = option;
				if (!blocks) return (element) => verifyElement(element, options);
				return (element) => {
					const { name } = element;
					const elementsOptions = blocks[name];
					if (elementsOptions) normalizeOptionValue({
						singleline: elementsOptions.singleline || options.singleline,
						multiline: elementsOptions.multiline || options.multiline,
						maxEmptyLines: elementsOptions.maxEmptyLines == null ? options.maxEmptyLines : elementsOptions.maxEmptyLines
					})(element);
					else verifyElement(element, options);
				};
			}
			const documentFragment = df;
			const verify = normalizeOptionValue(context.options[0]);
			return utils.defineTemplateBodyVisitor(context, {}, { Program(node) {
				if (utils.hasInvalidEOF(node)) return;
				for (const element of documentFragment.children) if (utils.isVElement(element)) verify(element);
			} });
		}
	};
}));

//#endregion
Object.defineProperty(exports, 'default', {
  enumerable: true,
  get: function () {
    return require_block_tag_newline();
  }
});