UNPKG

eslint-plugin-vue

Version:

Official ESLint plugin for Vue.js

237 lines (234 loc) 9.69 kB
'use strict'; const require_runtime = require('../_virtual/_rolldown/runtime.js'); const require_index = require('../utils/index.js'); //#region lib/rules/padding-lines-in-component-definition.js /** * @author ItMaga <https://github.com/ItMaga> * See LICENSE file in root directory for full license. */ var require_padding_lines_in_component_definition = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => { /** * @typedef {import('../utils').ComponentProp} ComponentProp * @typedef {import('../utils').ComponentEmit} ComponentEmit * @typedef {import('../utils').GroupName} GroupName */ const utils = require_index.default; const { isCommentToken } = require("@eslint-community/eslint-utils"); const AvailablePaddingOptions = { Never: "never", Always: "always", Ignore: "ignore" }; const OptionKeys = { BetweenOptions: "betweenOptions", WithinOption: "withinOption", BetweenItems: "betweenItems", WithinEach: "withinEach", GroupSingleLineProperties: "groupSingleLineProperties" }; /** * @param {Token} node */ function isComma(node) { return node.type === "Punctuator" && node.value === ","; } /** * @typedef {Exclude<ComponentProp | ComponentEmit, {type:'infer-type'}> & { node: {type: 'Property' | 'SpreadElement'} }} ValidComponentPropOrEmit */ /** * @template {ComponentProp | ComponentEmit} T * @param {T} propOrEmit * @returns {propOrEmit is ValidComponentPropOrEmit & T} */ function isValidProperties(propOrEmit) { return Boolean(propOrEmit.type !== "infer-type" && propOrEmit.node && ["Property", "SpreadElement"].includes(propOrEmit.node.type)); } /** * Split the source code into multiple lines based on the line delimiters. * @param {string} text Source code as a string. * @returns {string[]} Array of source code lines. */ function splitLines(text) { return text.split(/\r\n|[\r\n\u2028\u2029]/gu); } /** * @param {any} initialOption * @param {string} optionKey * @private * */ function parseOption(initialOption, optionKey) { return typeof initialOption === "string" ? initialOption : initialOption[optionKey]; } /** * @param {any} initialOption * @param {string} optionKey * @private * */ function parseBooleanOption(initialOption, optionKey) { if (typeof initialOption === "string") { if (initialOption === AvailablePaddingOptions.Always) return true; if (initialOption === AvailablePaddingOptions.Never) return false; } return initialOption[optionKey]; } /** * @param {(Property | SpreadElement)} currentProperty * @param {(Property | SpreadElement)} nextProperty * @param {boolean} option * @returns {boolean} * @private * */ function needGroupSingleLineProperties(currentProperty, nextProperty, option) { const isSingleCurrentProperty = currentProperty.loc.start.line === currentProperty.loc.end.line; const isSingleNextProperty = nextProperty.loc.start.line === nextProperty.loc.end.line; return isSingleCurrentProperty && isSingleNextProperty && option; } module.exports = { meta: { type: "layout", docs: { description: "require or disallow padding lines in component definition", categories: void 0, url: "https://eslint.vuejs.org/rules/padding-lines-in-component-definition.html" }, fixable: "whitespace", schema: [{ oneOf: [{ enum: [AvailablePaddingOptions.Always, AvailablePaddingOptions.Never] }, { type: "object", additionalProperties: false, properties: { [OptionKeys.BetweenOptions]: { enum: Object.values(AvailablePaddingOptions) }, [OptionKeys.WithinOption]: { oneOf: [{ enum: Object.values(AvailablePaddingOptions) }, { type: "object", patternProperties: { "^[a-zA-Z]*$": { oneOf: [{ enum: Object.values(AvailablePaddingOptions) }, { type: "object", properties: { [OptionKeys.BetweenItems]: { enum: Object.values(AvailablePaddingOptions) }, [OptionKeys.WithinEach]: { enum: Object.values(AvailablePaddingOptions) } }, additionalProperties: false }] } }, minProperties: 1, additionalProperties: false }] }, [OptionKeys.GroupSingleLineProperties]: { type: "boolean" } } }] }], messages: { never: "Unexpected blank line before this definition.", always: "Expected blank line before this definition.", groupSingleLineProperties: "Unexpected blank line between single line properties." } }, create(context) { const options = context.options[0] || AvailablePaddingOptions.Always; const sourceCode = context.sourceCode; /** * @param {(Property | SpreadElement)} currentProperty * @param {(Property | SpreadElement | Token)} nextProperty * @param {RuleFixer} fixer * */ function replaceLines(currentProperty, nextProperty, fixer) { const commaToken = sourceCode.getTokenAfter(currentProperty, isComma); const start = commaToken ? commaToken.range[1] : currentProperty.range[1]; const end = nextProperty.range[0]; const newText = `\n${splitLines(sourceCode.text.slice(start, end)).pop()}`; return fixer.replaceTextRange([start, end], newText); } /** * @param {(Property | SpreadElement)} currentProperty * @param {(Property | SpreadElement | Token)} nextProperty * @param {RuleFixer} fixer * @param {number} betweenLinesRange * */ function insertLines(currentProperty, nextProperty, fixer, betweenLinesRange) { const commaToken = sourceCode.getTokenAfter(currentProperty, isComma); const lineBeforeNextProperty = sourceCode.lines[nextProperty.loc.start.line - 1]; const lastSpaces = /^\s*/.exec(lineBeforeNextProperty)[0]; const newText = betweenLinesRange === 0 ? `\n\n${lastSpaces}` : "\n"; return fixer.insertTextAfter(commaToken || currentProperty, newText); } /** * @param {(Property | SpreadElement)[]} properties * @param {any} option * @param {any} nextOption * */ function verify(properties, option, nextOption) { const groupSingleLineProperties = parseBooleanOption(options, OptionKeys.GroupSingleLineProperties); for (const [i, currentProperty] of properties.entries()) { const nextProperty = properties[i + 1]; if (nextProperty && option !== AvailablePaddingOptions.Ignore) { const tokenBeforeNext = sourceCode.getTokenBefore(nextProperty, { includeComments: true }); const reportNode = isCommentToken(tokenBeforeNext) ? tokenBeforeNext : nextProperty; const betweenLinesRange = reportNode.loc.start.line - currentProperty.loc.end.line; if (needGroupSingleLineProperties(currentProperty, nextProperty, groupSingleLineProperties)) { if (betweenLinesRange > 1) context.report({ node: reportNode, messageId: "groupSingleLineProperties", loc: reportNode.loc, fix(fixer) { return replaceLines(currentProperty, reportNode, fixer); } }); continue; } if (betweenLinesRange <= 1 && option === AvailablePaddingOptions.Always) context.report({ node: reportNode, messageId: "always", loc: reportNode.loc, fix(fixer) { return insertLines(currentProperty, reportNode, fixer, betweenLinesRange); } }); else if (betweenLinesRange > 1 && option === AvailablePaddingOptions.Never) context.report({ node: reportNode, messageId: "never", loc: reportNode.loc, fix(fixer) { return replaceLines(currentProperty, reportNode, fixer); } }); } if (!nextOption) return; const name = currentProperty.type === "Property" && utils.getStaticPropertyName(currentProperty); if (!name) continue; const propertyOption = parseOption(nextOption, name); if (!propertyOption) continue; const nestedProperties = currentProperty.type === "Property" && currentProperty.value.type === "ObjectExpression" && currentProperty.value.properties; if (!nestedProperties) continue; verify(nestedProperties, parseOption(propertyOption, OptionKeys.BetweenItems), parseOption(propertyOption, OptionKeys.WithinEach)); } } return utils.compositingVisitors(utils.defineVueVisitor(context, { onVueObjectEnter(node) { verify(node.properties, parseOption(options, OptionKeys.BetweenOptions), parseOption(options, OptionKeys.WithinOption)); } }), utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(_, props) { const propNodes = props.filter(isValidProperties).map((prop) => prop.node); const withinOption = parseOption(options, OptionKeys.WithinOption); const propsOption = withinOption && parseOption(withinOption, "props"); if (!propsOption) return; verify(propNodes, parseOption(propsOption, OptionKeys.BetweenItems), parseOption(propsOption, OptionKeys.WithinEach)); }, onDefineEmitsEnter(_, emits) { const emitNodes = emits.filter(isValidProperties).map((emit) => emit.node); const withinOption = parseOption(options, OptionKeys.WithinOption); const emitsOption = withinOption && parseOption(withinOption, "emits"); if (!emitsOption) return; verify(emitNodes, parseOption(emitsOption, OptionKeys.BetweenItems), parseOption(emitsOption, OptionKeys.WithinEach)); }, onDefineOptionsEnter(node) { if (node.arguments.length === 0) return; const define = node.arguments[0]; if (define.type !== "ObjectExpression") return; verify(define.properties, parseOption(options, OptionKeys.BetweenOptions), parseOption(options, OptionKeys.WithinOption)); } })); } }; })); //#endregion Object.defineProperty(exports, 'default', { enumerable: true, get: function () { return require_padding_lines_in_component_definition(); } });