routie dev init since i didn't adhere to any proper guidance up until now

This commit is contained in:
2026-04-29 22:27:29 -06:00
commit e1dabb71e2
15301 changed files with 3562618 additions and 0 deletions
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/array-bracket-newline.js
/**
* @author Yosuke Ota
*/
var require_array_bracket_newline = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("array-bracket-newline", { skipDynamicArguments: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_array_bracket_newline();
}
});
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/array-bracket-spacing.js
/**
* @author Toru Nagashima
*/
var require_array_bracket_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("array-bracket-spacing", { skipDynamicArguments: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_array_bracket_spacing();
}
});
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/array-element-newline.js
/**
* @author alshyra
*/
var require_array_element_newline = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("array-element-newline", { skipDynamicArguments: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_array_element_newline();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/arrow-spacing.js
/**
* @author Yosuke Ota
*/
var require_arrow_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("arrow-spacing");
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_arrow_spacing();
}
});
@@ -0,0 +1,100 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
const require_regexp = require('../utils/regexp.js');
const require_svg_attributes_weird_case = require('../utils/svg-attributes-weird-case.js');
//#region lib/rules/attribute-hyphenation.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
function getAttributeName(node) {
if (!node.directive) return node.key.rawName;
if ((node.key.name.name === "bind" || node.key.name.name === "model") && node.key.argument && node.key.argument.type === "VIdentifier") return node.key.argument.rawName;
return null;
}
var attribute_hyphenation_default = {
meta: {
type: "suggestion",
docs: {
description: "enforce attribute naming style on custom components in template",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/attribute-hyphenation.html"
},
fixable: "code",
schema: [{ enum: ["always", "never"] }, {
type: "object",
properties: {
ignore: {
type: "array",
items: { allOf: [
{ type: "string" },
{ not: {
type: "string",
pattern: ":exit$"
} },
{ not: {
type: "string",
pattern: String.raw`^\s*$`
} }
] },
uniqueItems: true,
additionalItems: false
},
ignoreTags: {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
}
},
additionalProperties: false
}],
messages: {
mustBeHyphenated: "Attribute '{{text}}' must be hyphenated.",
cannotBeHyphenated: "Attribute '{{text}}' can't be hyphenated."
}
},
create(context) {
const sourceCode = context.sourceCode;
const option = context.options[0];
const optionsPayload = context.options[1];
const useHyphenated = option !== "never";
const isIgnoredTagName = require_regexp.toRegExpGroupMatcher(optionsPayload?.ignoreTags);
const ignoredAttributes = [
"data-",
"aria-",
"slot-scope",
...require_svg_attributes_weird_case.default
];
if (optionsPayload && optionsPayload.ignore) ignoredAttributes.push(...optionsPayload.ignore);
const caseConverter = require_casing.getExactConverter(useHyphenated ? "kebab-case" : "camelCase");
function reportIssue(node, name) {
const text = sourceCode.getText(node.key);
context.report({
node: node.key,
loc: node.loc,
messageId: useHyphenated ? "mustBeHyphenated" : "cannotBeHyphenated",
data: { text },
fix: (fixer) => {
if (text.includes("_")) return null;
if (text.endsWith(".sync")) return null;
if (/^[A-Z]/.test(name)) return null;
return fixer.replaceText(node.key, text.replace(name, caseConverter(name)));
}
});
}
function isIgnoredAttribute(value) {
if (ignoredAttributes.some((attr) => value.includes(attr))) return true;
return useHyphenated ? value.toLowerCase() === value : !/-/.test(value);
}
return import_utils.default.defineTemplateBodyVisitor(context, { VAttribute(node) {
const element = node.parent.parent;
if (!import_utils.default.isCustomComponent(element) && element.name !== "slot" || isIgnoredTagName(element.rawName)) return;
const name = getAttributeName(node);
if (name === null || isIgnoredAttribute(name)) return;
reportIssue(node, name);
} });
}
};
//#endregion
exports.default = attribute_hyphenation_default;
+338
View File
@@ -0,0 +1,338 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/attributes-order.js
/**
* @fileoverview enforce ordering of attributes
* @author Erin Depew
*/
var require_attributes_order = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef { VDirective & { key: VDirectiveKey & { name: VIdentifier & { name: 'bind' } } } } VBindDirective
*/
const ATTRS = {
DEFINITION: "DEFINITION",
LIST_RENDERING: "LIST_RENDERING",
CONDITIONALS: "CONDITIONALS",
RENDER_MODIFIERS: "RENDER_MODIFIERS",
GLOBAL: "GLOBAL",
UNIQUE: "UNIQUE",
SLOT: "SLOT",
TWO_WAY_BINDING: "TWO_WAY_BINDING",
OTHER_DIRECTIVES: "OTHER_DIRECTIVES",
OTHER_ATTR: "OTHER_ATTR",
ATTR_STATIC: "ATTR_STATIC",
ATTR_DYNAMIC: "ATTR_DYNAMIC",
ATTR_SHORTHAND_BOOL: "ATTR_SHORTHAND_BOOL",
EVENTS: "EVENTS",
CONTENT: "CONTENT"
};
/**
* Check whether the given attribute is `v-bind` directive.
* @param {VAttribute | VDirective | undefined | null} node
* @returns { node is VBindDirective }
*/
function isVBind(node) {
return Boolean(node && node.directive && node.key.name.name === "bind");
}
/**
* Check whether the given attribute is `v-model` directive.
* @param {VAttribute | VDirective | undefined | null} node
* @returns { node is VDirective }
*/
function isVModel(node) {
return Boolean(node && node.directive && node.key.name.name === "model");
}
/**
* Check whether the given attribute is plain attribute.
* @param {VAttribute | VDirective | undefined | null} node
* @returns { node is VAttribute }
*/
function isVAttribute(node) {
return Boolean(node && !node.directive);
}
/**
* Check whether the given attribute is plain attribute, `v-bind` directive or `v-model` directive.
* @param {VAttribute | VDirective | undefined | null} node
* @returns { node is VAttribute }
*/
function isVAttributeOrVBindOrVModel(node) {
return isVAttribute(node) || isVBind(node) || isVModel(node);
}
/**
* Check whether the given attribute is `v-bind="..."` directive.
* @param {VAttribute | VDirective | undefined | null} node
* @returns { node is VBindDirective }
*/
function isVBindObject(node) {
return isVBind(node) && node.key.argument == null;
}
/**
* Check whether the given attribute is a shorthand boolean like `selected`.
* @param {VAttribute | VDirective | undefined | null} node
* @returns { node is VAttribute }
*/
function isVShorthandBoolean(node) {
return isVAttribute(node) && !node.value;
}
/**
* @param {VAttribute | VDirective} attribute
* @param {SourceCode} sourceCode
*/
function getAttributeName(attribute, sourceCode) {
if (attribute.directive) if (isVBind(attribute)) return attribute.key.argument ? sourceCode.getText(attribute.key.argument) : "";
else return getDirectiveKeyName(attribute.key, sourceCode);
else return attribute.key.name;
}
/**
* @param {VDirectiveKey} directiveKey
* @param {SourceCode} sourceCode
*/
function getDirectiveKeyName(directiveKey, sourceCode) {
let text = `v-${directiveKey.name.name}`;
if (directiveKey.argument) text += `:${sourceCode.getText(directiveKey.argument)}`;
for (const modifier of directiveKey.modifiers) text += `.${modifier.name}`;
return text;
}
/**
* @param {VAttribute | VDirective} attribute
*/
function getAttributeType(attribute) {
let propName;
if (attribute.directive) {
if (!isVBind(attribute)) switch (attribute.key.name.name) {
case "for": return ATTRS.LIST_RENDERING;
case "if":
case "else-if":
case "else":
case "show":
case "cloak": return ATTRS.CONDITIONALS;
case "pre":
case "once": return ATTRS.RENDER_MODIFIERS;
case "model": return ATTRS.TWO_WAY_BINDING;
case "on": return ATTRS.EVENTS;
case "html":
case "text": return ATTRS.CONTENT;
case "slot": return ATTRS.SLOT;
case "is": return ATTRS.DEFINITION;
default: return ATTRS.OTHER_DIRECTIVES;
}
propName = attribute.key.argument && attribute.key.argument.type === "VIdentifier" ? attribute.key.argument.rawName : "";
} else propName = attribute.key.name;
switch (propName) {
case "is": return ATTRS.DEFINITION;
case "id": return ATTRS.GLOBAL;
case "ref":
case "key": return ATTRS.UNIQUE;
case "slot":
case "slot-scope": return ATTRS.SLOT;
default:
if (isVBind(attribute)) return ATTRS.ATTR_DYNAMIC;
if (isVShorthandBoolean(attribute)) return ATTRS.ATTR_SHORTHAND_BOOL;
return ATTRS.ATTR_STATIC;
}
}
/**
* @param {VAttribute | VDirective} attribute
* @param { { [key: string]: number } } attributePosition
* @returns {number | null} If the value is null, the order is omitted. Do not force the order.
*/
function getPosition(attribute, attributePosition) {
const attributeType = getAttributeType(attribute);
return attributePosition[attributeType] == null ? null : attributePosition[attributeType];
}
/**
* @param {VAttribute | VDirective} prevNode
* @param {VAttribute | VDirective} currNode
* @param {SourceCode} sourceCode
*/
function isAlphabetical(prevNode, currNode, sourceCode) {
const prevName = getAttributeName(prevNode, sourceCode);
const currName = getAttributeName(currNode, sourceCode);
if (prevName === currName) return isVBind(prevNode) <= isVBind(currNode);
return prevName < currName;
}
/**
* @param {RuleContext} context - The rule context.
* @returns {RuleListener} AST event handlers.
*/
function create(context) {
const sourceCode = context.sourceCode;
const otherAttrs = [
ATTRS.ATTR_DYNAMIC,
ATTRS.ATTR_STATIC,
ATTRS.ATTR_SHORTHAND_BOOL
];
let attributeOrder = [
ATTRS.DEFINITION,
ATTRS.LIST_RENDERING,
ATTRS.CONDITIONALS,
ATTRS.RENDER_MODIFIERS,
ATTRS.GLOBAL,
[ATTRS.UNIQUE, ATTRS.SLOT],
ATTRS.TWO_WAY_BINDING,
ATTRS.OTHER_DIRECTIVES,
otherAttrs,
ATTRS.EVENTS,
ATTRS.CONTENT
];
if (context.options[0] && context.options[0].order) {
attributeOrder = [...context.options[0].order];
for (const item of attributeOrder.flat()) if (item === ATTRS.OTHER_ATTR) {
for (const attribute of attributeOrder.flat()) if (otherAttrs.includes(attribute)) throw new Error(`Value "${ATTRS.OTHER_ATTR}" is not allowed with "${attribute}".`);
}
for (const [index, item] of attributeOrder.entries()) if (item === ATTRS.OTHER_ATTR) attributeOrder[index] = otherAttrs;
else if (Array.isArray(item) && item.includes(ATTRS.OTHER_ATTR)) {
const attributes = item.filter((i) => i !== ATTRS.OTHER_ATTR);
attributes.push(...otherAttrs);
attributeOrder[index] = attributes;
}
}
const alphabetical = Boolean(context.options[0] && context.options[0].alphabetical);
const sortLineLength = Boolean(context.options[0] && context.options[0].sortLineLength);
const ignoreVBindObject = Boolean(context.options[0] && context.options[0].ignoreVBindObject);
/** @type { { [key: string]: number } } */
const attributePosition = {};
for (const [i, item] of attributeOrder.entries()) if (Array.isArray(item)) for (const attr of item) attributePosition[attr] = i;
else attributePosition[item] = i;
/**
* @param {VAttribute | VDirective} node
* @param {VAttribute | VDirective} previousNode
*/
function reportIssue(node, previousNode) {
const currentNode = sourceCode.getText(node.key);
const prevNode = sourceCode.getText(previousNode.key);
context.report({
node,
messageId: "expectedOrder",
data: {
currentNode,
prevNode
},
fix(fixer) {
const attributes = node.parent.attributes;
/** @type { (node: VAttribute | VDirective | undefined) => boolean } */
let isMoveUp;
if (isVBindObject(node)) isMoveUp = isVAttributeOrVBindOrVModel;
else if (isVAttributeOrVBindOrVModel(node)) isMoveUp = isVBindObject;
else isMoveUp = () => false;
const previousNodes = attributes.slice(attributes.indexOf(previousNode), attributes.indexOf(node));
const moveNodes = [node];
for (const node of previousNodes) if (isMoveUp(node)) moveNodes.unshift(node);
else moveNodes.push(node);
return moveNodes.map((moveNode, index) => {
const text = sourceCode.getText(moveNode);
return fixer.replaceText(previousNodes[index] || node, text);
});
}
});
}
return utils.defineTemplateBodyVisitor(context, { VStartTag(node) {
const attributeAndPositions = getAttributeAndPositionList(node);
if (attributeAndPositions.length <= 1) return;
let { attr: previousNode, position: previousPosition } = attributeAndPositions[0];
for (let index = 1; index < attributeAndPositions.length; index++) {
const { attr, position } = attributeAndPositions[index];
let valid = previousPosition <= position;
if (valid && previousPosition === position) {
let sortedByLength = false;
if (sortLineLength) {
const prevText = sourceCode.getText(previousNode);
const currText = sourceCode.getText(attr);
if (prevText.length !== currText.length) {
valid = prevText.length < currText.length;
sortedByLength = true;
}
}
if (alphabetical && !sortedByLength) valid = isAlphabetical(previousNode, attr, sourceCode);
}
if (valid) {
previousNode = attr;
previousPosition = position;
} else reportIssue(attr, previousNode);
}
} });
/**
* @param {VStartTag} node
* @returns { { attr: ( VAttribute | VDirective ), position: number }[] }
*/
function getAttributeAndPositionList(node) {
const attributes = node.attributes.filter((node, index, attributes) => {
if (ignoreVBindObject && isVBindObject(node)) return false;
if (isVBindObject(node) && (isVAttributeOrVBindOrVModel(attributes[index - 1]) || isVAttributeOrVBindOrVModel(attributes[index + 1]))) return false;
return true;
});
const results = [];
for (const [index, attr] of attributes.entries()) {
const position = getPositionFromAttrIndex(index);
if (position == null) continue;
results.push({
attr,
position
});
}
return results;
/**
* @param {number} index
* @returns {number | null}
*/
function getPositionFromAttrIndex(index) {
const node = attributes[index];
if (isVBindObject(node)) {
const len = attributes.length;
for (let nextIndex = index + 1; nextIndex < len; nextIndex++) {
const next = attributes[nextIndex];
if (isVAttributeOrVBindOrVModel(next) && !isVBindObject(next)) return getPositionFromAttrIndex(nextIndex);
}
}
return getPosition(node, attributePosition);
}
}
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce order of attributes",
categories: ["vue3-recommended", "vue2-recommended"],
url: "https://eslint.vuejs.org/rules/attributes-order.html"
},
fixable: "code",
schema: [{
type: "object",
properties: {
order: {
type: "array",
items: { oneOf: [{ enum: Object.values(ATTRS) }, {
type: "array",
items: {
enum: Object.values(ATTRS),
uniqueItems: true,
additionalItems: false
}
}] },
uniqueItems: true,
additionalItems: false
},
alphabetical: { type: "boolean" },
sortLineLength: { type: "boolean" },
ignoreVBindObject: { type: "boolean" }
},
additionalProperties: false
}],
messages: { expectedOrder: `Attribute "{{currentNode}}" should go before "{{prevNode}}".` }
},
create
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_attributes_order();
}
});
+177
View File
@@ -0,0 +1,177 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/block-lang.js
/**
* @fileoverview Disallow use other than available `lang`
* @author Yosuke Ota
*/
var require_block_lang = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef {object} BlockOptions
* @property {Set<string>} lang
* @property {boolean} allowNoLang
*/
/**
* @typedef { { [element: string]: BlockOptions | undefined } } Options
*/
/**
* @typedef {object} UserBlockOptions
* @property {string[] | string} [lang]
* @property {boolean} [allowNoLang]
*/
/**
* @typedef { { [element: string]: UserBlockOptions | undefined } } UserOptions
*/
/**
* https://vuejs.github.io/vetur/guide/highlighting.html
* <template lang="html"></template>
* <style lang="css"></style>
* <script lang="js"><\/script>
* <script lang="javascript"><\/script>
* @type {Record<string, string[] | undefined>}
*/
const DEFAULT_LANGUAGES = {
template: ["html"],
style: ["css"],
script: ["js", "javascript"]
};
/**
* @param {NonNullable<BlockOptions['lang']>} lang
*/
function getAllowsLangPhrase(lang) {
const langs = [...lang].map((s) => `'${s}'`);
switch (langs.length) {
case 1: return langs[0];
default: return `${langs.slice(0, -1).join(", ")}, and ${langs.at(-1)}`;
}
}
/**
* Normalizes a given option.
* @param {string} blockName The block name.
* @param {UserBlockOptions} option An option to parse.
* @returns {BlockOptions} Normalized option.
*/
function normalizeOption(blockName, option) {
/** @type {Set<string>} */
let lang;
if (Array.isArray(option.lang)) lang = new Set(option.lang);
else if (typeof option.lang === "string") lang = new Set([option.lang]);
else lang = /* @__PURE__ */ new Set();
let hasDefault = false;
for (const def of DEFAULT_LANGUAGES[blockName] || []) if (lang.has(def)) {
lang.delete(def);
hasDefault = true;
}
if (lang.size === 0) return {
lang,
allowNoLang: true
};
return {
lang,
allowNoLang: hasDefault || Boolean(option.allowNoLang)
};
}
/**
* Normalizes a given options.
* @param { UserOptions } options An option to parse.
* @returns {Options} Normalized option.
*/
function normalizeOptions(options) {
if (!options) return {};
/** @type {Options} */
const normalized = {};
for (const blockName of Object.keys(options)) {
const value = options[blockName];
if (value) normalized[blockName] = normalizeOption(blockName, value);
}
return normalized;
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow use other than available `lang`",
categories: void 0,
url: "https://eslint.vuejs.org/rules/block-lang.html"
},
schema: [{
type: "object",
patternProperties: { "^(?:\\S+)$": { oneOf: [{
type: "object",
properties: {
lang: { oneOf: [{ type: "string" }, {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
}] },
allowNoLang: { type: "boolean" }
},
additionalProperties: false
}] } },
minProperties: 1,
additionalProperties: false
}],
messages: {
expected: "Only {{allows}} can be used for the 'lang' attribute of '<{{tag}}>'.",
missing: "The 'lang' attribute of '<{{tag}}>' is missing.",
unexpected: "Do not specify the 'lang' attribute of '<{{tag}}>'.",
useOrNot: "Only {{allows}} can be used for the 'lang' attribute of '<{{tag}}>'. Or, not specifying the 'lang' attribute is allowed.",
unexpectedDefault: "Do not explicitly specify the default language for the 'lang' attribute of '<{{tag}}>'."
}
},
create(context) {
const options = normalizeOptions(context.options[0] || {
script: { allowNoLang: true },
template: { allowNoLang: true },
style: { allowNoLang: true }
});
if (Object.keys(options).length === 0) return {};
/**
* @param {VElement} element
* @returns {void}
*/
function verify(element) {
const tag = element.name;
const option = options[tag];
if (!option) return;
const lang = utils.getAttribute(element, "lang");
if (lang == null || lang.value == null) {
if (!option.allowNoLang) context.report({
node: element.startTag,
messageId: "missing",
data: { tag }
});
return;
}
if (!option.lang.has(lang.value.value)) {
let messageId;
if (!option.allowNoLang) messageId = "expected";
else if (option.lang.size === 0) messageId = (DEFAULT_LANGUAGES[tag] || []).includes(lang.value.value) ? "unexpectedDefault" : "unexpected";
else messageId = "useOrNot";
context.report({
node: lang,
messageId,
data: {
tag,
allows: getAllowsLangPhrase(option.lang)
}
});
}
}
return utils.defineDocumentVisitor(context, { "VDocumentFragment > VElement": verify });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_block_lang();
}
});
+104
View File
@@ -0,0 +1,104 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_selector = require('../utils/selector.js');
//#region lib/rules/block-order.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const DEFAULT_ORDER = Object.freeze([["script", "template"], "style"]);
function getAttributeString(element) {
return element.startTag.attributes.map((attribute) => {
if (attribute.value && attribute.value.type !== "VLiteral") return "";
return `${attribute.key.name}${attribute.value && attribute.value.value ? `=${attribute.value.value}` : ""}`;
}).join(" ");
}
var block_order_default = {
meta: {
type: "suggestion",
docs: {
description: "enforce order of component top-level elements",
categories: ["vue3-recommended", "vue2-recommended"],
url: "https://eslint.vuejs.org/rules/block-order.html"
},
fixable: "code",
schema: [{
type: "object",
properties: { order: {
type: "array",
items: { oneOf: [{ type: "string" }, {
type: "array",
items: { type: "string" },
uniqueItems: true
}] },
uniqueItems: true,
additionalItems: false
} },
additionalProperties: false
}],
messages: { unexpected: "'<{{elementName}}{{elementAttributes}}>' should be above '<{{firstUnorderedName}}{{firstUnorderedAttributes}}>' on line {{line}}." }
},
create(context) {
const orders = [];
const orderOptions = context.options[0] && context.options[0].order || DEFAULT_ORDER;
for (const [index, selectorOrSelectors] of orderOptions.entries()) if (Array.isArray(selectorOrSelectors)) for (const selector of selectorOrSelectors) orders.push({
selectorText: selector,
selector: require_selector.parseSelector(selector, context),
index
});
else orders.push({
selectorText: selectorOrSelectors,
selector: require_selector.parseSelector(selectorOrSelectors, context),
index
});
function getOrderElement(element) {
return orders.find((o) => o.selector.test(element));
}
const sourceCode = context.sourceCode;
const documentFragment = sourceCode.parserServices.getDocumentFragment && sourceCode.parserServices.getDocumentFragment();
function getTopLevelHTMLElements() {
if (documentFragment) return documentFragment.children.filter(import_utils.default.isVElement);
return [];
}
return { Program(node) {
if (import_utils.default.hasInvalidEOF(node)) return;
const elements = getTopLevelHTMLElements();
const elementsWithOrder = elements.flatMap((element) => {
const order = getOrderElement(element);
return order ? [{
order,
element
}] : [];
});
for (const [index, elementWithOrders] of elementsWithOrder.entries()) {
const { order: expected, element } = elementWithOrders;
const firstUnordered = elementsWithOrder.slice(0, index).filter(({ order }) => expected.index < order.index).sort((e1, e2) => e1.order.index - e2.order.index)[0];
if (firstUnordered) {
const firstUnorderedAttributes = getAttributeString(firstUnordered.element);
const elementAttributes = getAttributeString(element);
context.report({
node: element,
loc: element.loc,
messageId: "unexpected",
data: {
elementName: element.name,
elementAttributes: elementAttributes ? ` ${elementAttributes}` : "",
firstUnorderedName: firstUnordered.element.name,
firstUnorderedAttributes: firstUnorderedAttributes ? ` ${firstUnorderedAttributes}` : "",
line: firstUnordered.element.loc.start.line
},
*fix(fixer) {
const fixedElements = elements.flatMap((it) => {
if (it === firstUnordered.element) return [element, it];
else if (it === element) return [];
return [it];
});
for (let i = elements.length - 1; i >= 0; i--) if (elements[i] !== fixedElements[i]) yield fixer.replaceTextRange(elements[i].range, sourceCode.text.slice(...fixedElements[i].range));
}
});
}
}
} };
}
};
//#endregion
exports.default = block_order_default;
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/block-spacing.js
/**
* @author Yosuke Ota
*/
var require_block_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("block-spacing", { skipDynamicArguments: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_block_spacing();
}
});
+261
View File
@@ -0,0 +1,261 @@
'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();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/brace-style.js
/**
* @author Yosuke Ota
*/
var require_brace_style = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("brace-style", { skipDynamicArguments: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_brace_style();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/camelcase.js
/**
* @author Yosuke Ota
*/
var require_camelcase = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapCoreRule } = require_index.default;
module.exports = wrapCoreRule("camelcase");
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_camelcase();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/comma-dangle.js
/**
* @author Yosuke Ota
*/
var require_comma_dangle = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("comma-dangle");
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_comma_dangle();
}
});
+25
View File
@@ -0,0 +1,25 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/comma-spacing.js
/**
* @author Yosuke Ota
*/
var require_comma_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("comma-spacing", {
skipDynamicArguments: true,
skipDynamicArgumentsReport: true,
applyDocument: true
});
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_comma_spacing();
}
});
+25
View File
@@ -0,0 +1,25 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/comma-style.js
/**
* @author Yosuke Ota
*/
var require_comma_style = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("comma-style", { create(_context, { baseHandlers }) {
return { VSlotScopeExpression(node) {
if (baseHandlers.FunctionExpression) baseHandlers.FunctionExpression(node);
} };
} });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_comma_style();
}
});
+298
View File
@@ -0,0 +1,298 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/comment-directive.js
/**
* @author Toru Nagashima <https://github.com/mysticatea>
*/
var require_comment_directive = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef {object} RuleAndLocation
* @property {string} RuleAndLocation.ruleId
* @property {number} RuleAndLocation.index
* @property {string} [RuleAndLocation.key]
*/
const COMMENT_DIRECTIVE_B = /^\s*(eslint-(?:en|dis)able)(?:\s+|$)/;
const COMMENT_DIRECTIVE_L = /^\s*(eslint-disable(?:-next)?-line)(?:\s+|$)/;
/**
* Remove the ignored part from a given directive comment and trim it.
* @param {string} value The comment text to strip.
* @returns {string} The stripped text.
*/
function stripDirectiveComment(value) {
return value.split(/\s-{2,}\s/u)[0];
}
/**
* Parse a given comment.
* @param {RegExp} pattern The RegExp pattern to parse.
* @param {string} comment The comment value to parse.
* @returns {({type:string,rules:RuleAndLocation[]})|null} The parsing result.
*/
function parse(pattern, comment) {
const text = stripDirectiveComment(comment);
const match = pattern.exec(text);
if (match == null) return null;
const type = match[1];
/** @type {RuleAndLocation[]} */
const rules = [];
const rulesRe = /([^\s,]+)[\s,]*/g;
let startIndex = match[0].length;
rulesRe.lastIndex = startIndex;
let res;
while (res = rulesRe.exec(text)) {
const ruleId = res[1].trim();
rules.push({
ruleId,
index: startIndex
});
startIndex = rulesRe.lastIndex;
}
return {
type,
rules
};
}
/**
* Enable rules.
* @param {RuleContext} context The rule context.
* @param {{line:number,column:number}} loc The location information to enable.
* @param { 'block' | 'line' } group The group to enable.
* @param {string | null} rule The rule ID to enable.
* @returns {void}
*/
function enable(context, loc, group, rule) {
if (rule) context.report({
loc,
messageId: group === "block" ? "enableBlockRule" : "enableLineRule",
data: { rule }
});
else context.report({
loc,
messageId: group === "block" ? "enableBlock" : "enableLine"
});
}
/**
* Disable rules.
* @param {RuleContext} context The rule context.
* @param {{line:number,column:number}} loc The location information to disable.
* @param { 'block' | 'line' } group The group to disable.
* @param {string | null} rule The rule ID to disable.
* @param {string} key The disable directive key.
* @returns {void}
*/
function disable(context, loc, group, rule, key) {
if (rule) context.report({
loc,
messageId: group === "block" ? "disableBlockRule" : "disableLineRule",
data: {
rule,
key
}
});
else context.report({
loc,
messageId: group === "block" ? "disableBlock" : "disableLine",
data: { key }
});
}
/**
* Process a given comment token.
* If the comment is `eslint-disable` or `eslint-enable` then it reports the comment.
* @param {RuleContext} context The rule context.
* @param {Token} comment The comment token to process.
* @param {boolean} reportUnusedDisableDirectives To report unused eslint-disable comments.
* @returns {void}
*/
function processBlock(context, comment, reportUnusedDisableDirectives) {
const parsed = parse(COMMENT_DIRECTIVE_B, comment.value);
if (parsed === null) return;
if (parsed.type === "eslint-disable") if (parsed.rules.length > 0) {
const rules = reportUnusedDisableDirectives ? reportUnusedRules(context, comment, parsed.type, parsed.rules) : parsed.rules;
for (const rule of rules) disable(context, comment.loc.start, "block", rule.ruleId, rule.key || "*");
} else {
const key = reportUnusedDisableDirectives ? reportUnused(context, comment, parsed.type) : "";
disable(context, comment.loc.start, "block", null, key);
}
else if (parsed.rules.length > 0) for (const rule of parsed.rules) enable(context, comment.loc.start, "block", rule.ruleId);
else enable(context, comment.loc.start, "block", null);
}
/**
* Process a given comment token.
* If the comment is `eslint-disable-line` or `eslint-disable-next-line` then it reports the comment.
* @param {RuleContext} context The rule context.
* @param {Token} comment The comment token to process.
* @param {boolean} reportUnusedDisableDirectives To report unused eslint-disable comments.
* @returns {void}
*/
function processLine(context, comment, reportUnusedDisableDirectives) {
const parsed = parse(COMMENT_DIRECTIVE_L, comment.value);
if (parsed != null && comment.loc.start.line === comment.loc.end.line) {
const line = comment.loc.start.line + (parsed.type === "eslint-disable-line" ? 0 : 1);
const column = -1;
if (parsed.rules.length > 0) {
const rules = reportUnusedDisableDirectives ? reportUnusedRules(context, comment, parsed.type, parsed.rules) : parsed.rules;
for (const rule of rules) {
disable(context, {
line,
column
}, "line", rule.ruleId, rule.key || "");
enable(context, {
line: line + 1,
column
}, "line", rule.ruleId);
}
} else {
const key = reportUnusedDisableDirectives ? reportUnused(context, comment, parsed.type) : "";
disable(context, {
line,
column
}, "line", null, key);
enable(context, {
line: line + 1,
column
}, "line", null);
}
}
}
/**
* Reports unused disable directive.
* Do not check the use of directives here. Filter the directives used with postprocess.
* @param {RuleContext} context The rule context.
* @param {Token} comment The comment token to report.
* @param {string} kind The comment directive kind.
* @returns {string} The report key
*/
function reportUnused(context, comment, kind) {
const loc = comment.loc;
context.report({
loc,
messageId: "unused",
data: { kind }
});
return locToKey(loc.start);
}
/**
* Reports unused disable directive rules.
* Do not check the use of directives here. Filter the directives used with postprocess.
* @param {RuleContext} context The rule context.
* @param {Token} comment The comment token to report.
* @param {string} kind The comment directive kind.
* @param {RuleAndLocation[]} rules To report rule.
* @returns { { ruleId: string, key: string }[] }
*/
function reportUnusedRules(context, comment, kind, rules) {
const sourceCode = context.sourceCode;
const commentStart = comment.range[0] + 4;
return rules.map((rule) => {
const start = sourceCode.getLocFromIndex(commentStart + rule.index);
const end = sourceCode.getLocFromIndex(commentStart + rule.index + rule.ruleId.length);
context.report({
loc: {
start,
end
},
messageId: "unusedRule",
data: {
rule: rule.ruleId,
kind
}
});
return {
ruleId: rule.ruleId,
key: locToKey(start)
};
});
}
/**
* Gets the key of location
* @param {Position} location The location
* @returns {string} The key
*/
function locToKey(location) {
return `line:${location.line},column${location.column}`;
}
/**
* Extracts the top-level elements in document fragment.
* @param {VDocumentFragment} documentFragment The document fragment.
* @returns {VElement[]} The top-level elements
*/
function extractTopLevelHTMLElements(documentFragment) {
return documentFragment.children.filter(utils.isVElement);
}
/**
* Extracts the top-level comments in document fragment.
* @param {VDocumentFragment} documentFragment The document fragment.
* @returns {Token[]} The top-level comments
*/
function extractTopLevelDocumentFragmentComments(documentFragment) {
const elements = extractTopLevelHTMLElements(documentFragment);
return documentFragment.comments.filter((comment) => elements.every((element) => comment.range[1] <= element.range[0] || element.range[1] <= comment.range[0]));
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "support comment-directives in `<template>`",
categories: ["base"],
url: "https://eslint.vuejs.org/rules/comment-directive.html"
},
schema: [{
type: "object",
properties: { reportUnusedDisableDirectives: { type: "boolean" } },
additionalProperties: false
}],
messages: {
disableBlock: "--block {{key}}",
enableBlock: "++block",
disableLine: "--line {{key}}",
enableLine: "++line",
disableBlockRule: "-block {{rule}} {{key}}",
enableBlockRule: "+block {{rule}}",
disableLineRule: "-line {{rule}} {{key}}",
enableLineRule: "+line {{rule}}",
clear: "clear",
unused: "Unused {{kind}} directive (no problems were reported).",
unusedRule: "Unused {{kind}} directive (no problems were reported from '{{rule}}')."
}
},
create(context) {
/** @type {boolean} */
const reportUnusedDisableDirectives = (context.options[0] || {}).reportUnusedDisableDirectives;
const sourceCode = context.sourceCode;
const documentFragment = sourceCode.parserServices.getDocumentFragment && sourceCode.parserServices.getDocumentFragment();
return { Program(node) {
if (node.templateBody) {
for (const comment of node.templateBody.comments) {
processBlock(context, comment, reportUnusedDisableDirectives);
processLine(context, comment, reportUnusedDisableDirectives);
}
context.report({
loc: node.templateBody.loc.end,
messageId: "clear"
});
}
if (documentFragment) {
for (const comment of extractTopLevelDocumentFragmentComments(documentFragment)) {
processBlock(context, comment, reportUnusedDisableDirectives);
processLine(context, comment, reportUnusedDisableDirectives);
}
for (const element of extractTopLevelHTMLElements(documentFragment)) context.report({
loc: element.loc.end,
messageId: "clear"
});
}
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_comment_directive();
}
});
@@ -0,0 +1,240 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/component-api-style.js
/**
* @author Yosuke Ota <https://github.com/ota-meshi>
* See LICENSE file in root directory for full license.
*/
var require_component_api_style = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef { 'script-setup' | 'composition' | 'composition-vue2' | 'options' } PreferOption
*
* @typedef {PreferOption[]} UserPreferOption
*
* @typedef {object} NormalizeOptions
* @property {object} allowsSFC
* @property {boolean} [allowsSFC.scriptSetup]
* @property {boolean} [allowsSFC.composition]
* @property {boolean} [allowsSFC.compositionVue2]
* @property {boolean} [allowsSFC.options]
* @property {object} allowsOther
* @property {boolean} [allowsOther.composition]
* @property {boolean} [allowsOther.compositionVue2]
* @property {boolean} [allowsOther.options]
*/
/** @type {PreferOption[]} */
const STYLE_OPTIONS = [
"script-setup",
"composition",
"composition-vue2",
"options"
];
/**
* Normalize options.
* @param {any[]} options The options user configured.
* @returns {NormalizeOptions} The normalized options.
*/
function parseOptions(options) {
/** @type {NormalizeOptions} */
const opts = {
allowsSFC: {},
allowsOther: {}
};
/** @type {UserPreferOption} */
const preferOptions = options[0] || ["script-setup", "composition"];
for (const prefer of preferOptions) switch (prefer) {
case "script-setup":
opts.allowsSFC.scriptSetup = true;
break;
case "composition":
opts.allowsSFC.composition = true;
opts.allowsOther.composition = true;
break;
case "composition-vue2":
opts.allowsSFC.compositionVue2 = true;
opts.allowsOther.compositionVue2 = true;
break;
case "options":
opts.allowsSFC.options = true;
opts.allowsOther.options = true;
break;
}
if (!opts.allowsOther.composition && !opts.allowsOther.compositionVue2 && !opts.allowsOther.options) {
opts.allowsOther.composition = true;
opts.allowsOther.compositionVue2 = true;
opts.allowsOther.options = true;
}
return opts;
}
const OPTIONS_API_OPTIONS = new Set([
"mixins",
"extends",
"data",
"computed",
"methods",
"watch",
"provide",
"inject",
"beforeCreate",
"created",
"beforeMount",
"mounted",
"beforeUpdate",
"updated",
"activated",
"deactivated",
"beforeDestroy",
"beforeUnmount",
"destroyed",
"unmounted",
"render",
"renderTracked",
"renderTriggered",
"errorCaptured",
"expose"
]);
const COMPOSITION_API_OPTIONS = new Set(["setup"]);
const COMPOSITION_API_VUE2_OPTIONS = new Set([
"setup",
"render",
"renderTracked",
"renderTriggered"
]);
const LIFECYCLE_HOOK_OPTIONS = new Set([
"beforeCreate",
"created",
"beforeMount",
"mounted",
"beforeUpdate",
"updated",
"activated",
"deactivated",
"beforeDestroy",
"beforeUnmount",
"destroyed",
"unmounted",
"renderTracked",
"renderTriggered",
"errorCaptured"
]);
/**
* @typedef { 'script-setup' | 'composition' | 'options' } ApiStyle
*/
/**
* @param {object} allowsOpt
* @param {boolean} [allowsOpt.scriptSetup]
* @param {boolean} [allowsOpt.composition]
* @param {boolean} [allowsOpt.compositionVue2]
* @param {boolean} [allowsOpt.options]
*/
function buildAllowedPhrase(allowsOpt) {
const phrases = [];
if (allowsOpt.scriptSetup) phrases.push("`<script setup>`");
if (allowsOpt.composition) phrases.push("Composition API");
if (allowsOpt.compositionVue2) phrases.push("Composition API (Vue 2)");
if (allowsOpt.options) phrases.push("Options API");
return phrases.length > 2 ? `${phrases.slice(0, -1).join(", ")} or ${phrases.at(-1)}` : phrases.join(" or ");
}
/**
* @param {object} allowsOpt
* @param {boolean} [allowsOpt.scriptSetup]
* @param {boolean} [allowsOpt.composition]
* @param {boolean} [allowsOpt.compositionVue2]
* @param {boolean} [allowsOpt.options]
*/
function isPreferScriptSetup(allowsOpt) {
if (!allowsOpt.scriptSetup || allowsOpt.composition || allowsOpt.compositionVue2 || allowsOpt.options) return false;
return true;
}
/**
* @param {string} name
*/
function buildOptionPhrase(name) {
if (LIFECYCLE_HOOK_OPTIONS.has(name)) return `\`${name}\` lifecycle hook`;
return name === "setup" || name === "render" ? `\`${name}\` function` : `\`${name}\` option`;
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce component API style",
categories: void 0,
url: "https://eslint.vuejs.org/rules/component-api-style.html"
},
fixable: null,
schema: [{
type: "array",
items: {
enum: STYLE_OPTIONS,
uniqueItems: true,
additionalItems: false
},
minItems: 1
}],
messages: {
disallowScriptSetup: "`<script setup>` is not allowed in your project. Use {{allowedApis}} instead.",
disallowComponentOption: "{{disallowedApi}} is not allowed in your project. {{optionPhrase}} is part of the {{disallowedApi}}. Use {{allowedApis}} instead.",
disallowComponentOptionPreferScriptSetup: "{{disallowedApi}} is not allowed in your project. Use `<script setup>` instead."
}
},
create(context) {
const options = parseOptions(context.options);
return utils.compositingVisitors({ Program() {
if (options.allowsSFC.scriptSetup) return;
const scriptSetup = utils.getScriptSetupElement(context);
if (scriptSetup) context.report({
node: scriptSetup.startTag,
messageId: "disallowScriptSetup",
data: { allowedApis: buildAllowedPhrase(options.allowsSFC) }
});
} }, utils.defineVueVisitor(context, { onVueObjectEnter(node) {
const allows = utils.isSFCObject(context, node) ? options.allowsSFC : options.allowsOther;
if ((allows.composition || allows.compositionVue2) && allows.options) return;
const apis = [
{
allow: allows.composition,
options: COMPOSITION_API_OPTIONS,
apiName: "Composition API"
},
{
allow: allows.options,
options: OPTIONS_API_OPTIONS,
apiName: "Options API"
},
{
allow: allows.compositionVue2,
options: COMPOSITION_API_VUE2_OPTIONS,
apiName: "Composition API (Vue 2)"
}
];
for (const prop of node.properties) {
if (prop.type !== "Property") continue;
const name = utils.getStaticPropertyName(prop);
if (!name) continue;
const disallowApi = !apis.some((api) => api.allow && api.options.has(name)) && apis.find((api) => !api.allow && api.options.has(name));
if (disallowApi) context.report({
node: prop.key,
messageId: isPreferScriptSetup(allows) ? "disallowComponentOptionPreferScriptSetup" : "disallowComponentOption",
data: {
disallowedApi: disallowApi.apiName,
optionPhrase: buildOptionPhrase(name),
allowedApis: buildAllowedPhrase(allows)
}
});
}
} }));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_component_api_style();
}
});
@@ -0,0 +1,70 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
//#region lib/rules/component-definition-name-casing.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const allowedCaseOptions = ["PascalCase", "kebab-case"];
function canConvert(node) {
return node.type === "Literal" || node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1;
}
var component_definition_name_casing_default = {
meta: {
type: "suggestion",
docs: {
description: "enforce specific casing for component definition name",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/component-definition-name-casing.html"
},
fixable: "code",
schema: [{ enum: allowedCaseOptions }],
messages: { incorrectCase: "Property name \"{{value}}\" is not {{caseType}}." }
},
create(context) {
const options = context.options[0];
const caseType = allowedCaseOptions.includes(options) ? options : "PascalCase";
function convertName(node) {
let nodeValue;
let range;
if (node.type === "TemplateLiteral") {
const quasis = node.quasis[0];
nodeValue = quasis.value.cooked;
range = quasis.range;
} else {
nodeValue = `${node.value}`;
range = node.range;
}
if (!require_casing.getChecker(caseType)(nodeValue)) context.report({
node,
messageId: "incorrectCase",
data: {
value: nodeValue,
caseType
},
fix: (fixer) => fixer.replaceTextRange([range[0] + 1, range[1] - 1], require_casing.getExactConverter(caseType)(nodeValue))
});
}
return import_utils.default.compositingVisitors(import_utils.default.executeOnCallVueComponent(context, (node) => {
if (node.arguments.length === 2) {
const argument = node.arguments[0];
if (canConvert(argument)) convertName(argument);
}
}), import_utils.default.executeOnVue(context, (obj) => {
const node = import_utils.default.findProperty(obj, "name");
if (!node) return;
if (!canConvert(node.value)) return;
convertName(node.value);
}), import_utils.default.defineScriptSetupVisitor(context, { onDefineOptionsEnter(node) {
if (node.arguments.length === 0) return;
const define = node.arguments[0];
if (define.type !== "ObjectExpression") return;
const nameNode = import_utils.default.findProperty(define, "name");
if (!nameNode) return;
if (!canConvert(nameNode.value)) return;
convertName(nameNode.value);
} }));
}
};
//#endregion
exports.default = component_definition_name_casing_default;
@@ -0,0 +1,120 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
const require_regexp = require('../utils/regexp.js');
//#region lib/rules/component-name-in-template-casing.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const allowedCaseOptions = ["PascalCase", "kebab-case"];
const defaultCase = "PascalCase";
/**
* Checks whether the given variable is the type-only import object.
*/
function isTypeOnlyImport(variable) {
if (variable.defs.length === 0) return false;
return variable.defs.every((def) => {
if (def.type !== "ImportBinding") return false;
if (def.parent.importKind === "type") return true;
if (def.node.type === "ImportSpecifier" && def.node.importKind === "type") return true;
return false;
});
}
var component_name_in_template_casing_default = {
meta: {
type: "suggestion",
docs: {
description: "enforce specific casing for the component naming style in template",
categories: void 0,
url: "https://eslint.vuejs.org/rules/component-name-in-template-casing.html"
},
fixable: "code",
schema: [{ enum: allowedCaseOptions }, {
type: "object",
properties: {
globals: {
type: "array",
items: { type: "string" },
uniqueItems: true
},
ignores: {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
},
registeredComponentsOnly: { type: "boolean" }
},
additionalProperties: false
}],
messages: { incorrectCase: "Component name \"{{name}}\" is not {{caseType}}." }
},
create(context) {
const caseOption = context.options[0];
const options = context.options[1] || {};
const caseType = allowedCaseOptions.includes(caseOption) ? caseOption : defaultCase;
const isIgnored = require_regexp.toRegExpGroupMatcher(options.ignores);
const globalStrings = [];
const globalPatterns = [];
for (const global of options.globals || []) if (require_regexp.isRegExp(global)) globalPatterns.push(global);
else globalStrings.push(global);
const isGlobalPattern = require_regexp.toRegExpGroupMatcher(globalPatterns);
const registeredComponentsOnly = options.registeredComponentsOnly !== false;
const sourceCode = context.sourceCode;
const tokens = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
const registeredComponents = new Set(globalStrings.map(require_casing.pascalCase));
if (import_utils.default.isScriptSetup(context)) {
const globalScope = context.sourceCode.scopeManager.globalScope;
if (globalScope) {
const moduleScope = globalScope.childScopes.find((scope) => scope.type === "module");
for (const variable of moduleScope && moduleScope.variables || []) if (!isTypeOnlyImport(variable)) registeredComponents.add(variable.name);
}
}
/**
* Checks whether the given node is the verification target node.
*/
function isVerifyTarget(node) {
if (isIgnored(node.rawName)) return false;
if (!import_utils.default.isHtmlElementNode(node) && !import_utils.default.isSvgElementNode(node) && !import_utils.default.isMathElementNode(node) || import_utils.default.isHtmlWellKnownElementName(node.rawName) || import_utils.default.isSvgWellKnownElementName(node.rawName) || import_utils.default.isMathWellKnownElementName(node.rawName) || import_utils.default.isVueBuiltInElementName(node.rawName)) return false;
if (!registeredComponentsOnly) return true;
return registeredComponents.has(require_casing.pascalCase(node.rawName)) || isGlobalPattern(node.rawName);
}
let hasInvalidEOF = false;
return import_utils.default.defineTemplateBodyVisitor(context, { VElement(node) {
if (hasInvalidEOF) return;
if (!isVerifyTarget(node)) return;
const name = node.rawName;
if (!require_casing.getChecker(caseType)(name)) {
const startTag = node.startTag;
const open = tokens.getFirstToken(startTag);
const casingName = require_casing.getExactConverter(caseType)(name);
context.report({
node: open,
loc: open.loc,
messageId: "incorrectCase",
data: {
name,
caseType
},
*fix(fixer) {
yield fixer.replaceText(open, `<${casingName}`);
const endTag = node.endTag;
if (endTag) {
const endTagOpen = tokens.getFirstToken(endTag);
yield fixer.replaceText(endTagOpen, `</${casingName}`);
}
}
});
}
} }, {
Program(node) {
hasInvalidEOF = import_utils.default.hasInvalidEOF(node);
},
...registeredComponentsOnly ? import_utils.default.executeOnVue(context, (obj) => {
for (const n of import_utils.default.getRegisteredComponents(obj)) registeredComponents.add(n.name);
}) : {}
});
}
};
//#endregion
exports.default = component_name_in_template_casing_default;
@@ -0,0 +1,67 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
//#region lib/rules/component-options-name-casing.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
function getOptionsComponentName(node) {
if (node.type === "Identifier") return node.name;
if (node.type === "Literal") return typeof node.value === "string" ? node.value : null;
return null;
}
var component_options_name_casing_default = {
meta: {
type: "suggestion",
docs: {
description: "enforce the casing of component name in `components` options",
categories: void 0,
url: "https://eslint.vuejs.org/rules/component-options-name-casing.html"
},
fixable: "code",
hasSuggestions: true,
schema: [{ enum: require_casing.allowedCaseOptions }],
messages: {
caseNotMatched: "Component name \"{{component}}\" is not {{caseType}}.",
possibleRenaming: "Rename component name to be in {{caseType}}."
}
},
create(context) {
const caseType = context.options[0] || "PascalCase";
const canAutoFix = caseType === "PascalCase";
const checkCase = require_casing.getChecker(caseType);
const convert = require_casing.getConverter(caseType);
return import_utils.default.executeOnVue(context, (obj) => {
const node = import_utils.default.findProperty(obj, "components");
if (!node || node.value.type !== "ObjectExpression") return;
for (const property of node.value.properties) {
if (property.type !== "Property") continue;
const name = getOptionsComponentName(property.key);
if (!name || checkCase(name)) continue;
context.report({
node: property.key,
messageId: "caseNotMatched",
data: {
component: name,
caseType
},
fix: canAutoFix ? (fixer) => {
const converted = convert(name);
return property.shorthand ? fixer.replaceText(property, `${converted}: ${name}`) : fixer.replaceText(property.key, converted);
} : void 0,
suggest: canAutoFix ? void 0 : [{
messageId: "possibleRenaming",
data: { caseType },
fix: (fixer) => {
const converted = convert(name);
if (caseType === "kebab-case") return property.shorthand ? fixer.replaceText(property, `'${converted}': ${name}`) : fixer.replaceText(property.key, `'${converted}'`);
return property.shorthand ? fixer.replaceText(property, `${converted}: ${name}`) : fixer.replaceText(property.key, converted);
}
}]
});
}
});
}
};
//#endregion
exports.default = component_options_name_casing_default;
@@ -0,0 +1,158 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
const require_regexp = require('../utils/regexp.js');
let _eslint_community_eslint_utils = require("@eslint-community/eslint-utils");
//#region lib/rules/custom-event-name-casing.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const ALLOWED_CASE_OPTIONS = ["kebab-case", "camelCase"];
const DEFAULT_CASE = "camelCase";
/**
* Get the name param node from the given CallExpression
*/
function getNameParamNode(node) {
const nameLiteralNode = node.arguments[0];
if (nameLiteralNode && import_utils.default.isStringLiteral(nameLiteralNode)) {
const name = import_utils.default.getStringLiteralValue(nameLiteralNode);
if (name != null) return {
name,
loc: nameLiteralNode.loc
};
}
return null;
}
/**
* Get the callee member node from the given CallExpression
*/
function getCalleeMemberNode(node) {
const callee = import_utils.default.skipChainExpression(node.callee);
if (callee.type === "MemberExpression") {
const name = import_utils.default.getStaticPropertyName(callee);
if (name) return {
name,
member: callee
};
}
return null;
}
var custom_event_name_casing_default = {
meta: {
type: "suggestion",
docs: {
description: "enforce specific casing for custom event name",
categories: void 0,
url: "https://eslint.vuejs.org/rules/custom-event-name-casing.html"
},
fixable: null,
schema: [{ enum: ALLOWED_CASE_OPTIONS }, {
type: "object",
properties: { ignores: {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
} },
additionalProperties: false
}],
messages: { unexpected: "Custom event name '{{name}}' must be {{caseType}}." }
},
create(context) {
const setupContexts = /* @__PURE__ */ new Map();
let emitParamName = "";
const caseType = context.options[0] || DEFAULT_CASE;
const objectOption = context.options[1] || {};
const caseChecker = require_casing.getChecker(caseType);
const isIgnored = require_regexp.toRegExpGroupMatcher(objectOption.ignores);
/**
* Check whether the given event name is valid.
*/
function isValidEventName(name) {
return caseChecker(name) || name.startsWith("update:");
}
function verify(nameWithLoc) {
const name = nameWithLoc.name;
if (isValidEventName(name) || isIgnored(name)) return;
context.report({
loc: nameWithLoc.loc,
messageId: "unexpected",
data: {
name,
caseType
}
});
}
const programNode = context.sourceCode.ast;
const callVisitor = { CallExpression(node, info) {
const nameWithLoc = getNameParamNode(node);
if (!nameWithLoc) return;
const setupContext = setupContexts.get(info ? info.node : programNode);
if (setupContext) {
const { contextReferenceIds, emitReferenceIds } = setupContext;
if (node.callee.type === "Identifier" && emitReferenceIds.has(node.callee)) verify(nameWithLoc);
else {
const emit = getCalleeMemberNode(node);
if (emit && emit.name === "emit" && emit.member.object.type === "Identifier" && contextReferenceIds.has(emit.member.object)) verify(nameWithLoc);
}
}
} };
return import_utils.default.defineTemplateBodyVisitor(context, { CallExpression(node) {
const callee = node.callee;
const nameWithLoc = getNameParamNode(node);
if (!nameWithLoc) return;
if (callee.type === "Identifier" && (callee.name === "$emit" || callee.name === emitParamName)) verify(nameWithLoc);
} }, import_utils.default.compositingVisitors(import_utils.default.defineScriptSetupVisitor(context, {
onDefineEmitsEnter(node) {
if (!node.parent || node.parent.type !== "VariableDeclarator" || node.parent.init !== node) return;
const emitParam = node.parent.id;
if (emitParam.type !== "Identifier") return;
emitParamName = emitParam.name;
const variable = (0, _eslint_community_eslint_utils.findVariable)(import_utils.default.getScope(context, emitParam), emitParam);
if (!variable) return;
const emitReferenceIds = /* @__PURE__ */ new Set();
for (const reference of variable.references) emitReferenceIds.add(reference.identifier);
setupContexts.set(programNode, {
contextReferenceIds: /* @__PURE__ */ new Set(),
emitReferenceIds
});
},
...callVisitor
}), import_utils.default.defineVueVisitor(context, {
onSetupFunctionEnter(node, { node: vueNode }) {
const contextParam = import_utils.default.skipDefaultParamValue(node.params[1]);
if (!contextParam) return;
if (contextParam.type === "RestElement" || contextParam.type === "ArrayPattern") return;
const contextReferenceIds = /* @__PURE__ */ new Set();
const emitReferenceIds = /* @__PURE__ */ new Set();
if (contextParam.type === "ObjectPattern") {
const emitProperty = import_utils.default.findAssignmentProperty(contextParam, "emit");
if (!emitProperty || emitProperty.value.type !== "Identifier") return;
const emitParam = emitProperty.value;
const variable = (0, _eslint_community_eslint_utils.findVariable)(import_utils.default.getScope(context, emitParam), emitParam);
if (!variable) return;
for (const reference of variable.references) emitReferenceIds.add(reference.identifier);
} else {
const variable = (0, _eslint_community_eslint_utils.findVariable)(import_utils.default.getScope(context, contextParam), contextParam);
if (!variable) return;
for (const reference of variable.references) contextReferenceIds.add(reference.identifier);
}
setupContexts.set(vueNode, {
contextReferenceIds,
emitReferenceIds
});
},
...callVisitor,
onVueObjectExit(node) {
setupContexts.delete(node);
}
}), { CallExpression(node) {
const nameLiteralNode = getNameParamNode(node);
if (!nameLiteralNode) return;
const emit = getCalleeMemberNode(node);
if (emit && emit.name === "$emit") verify(nameLiteralNode);
} }));
}
};
//#endregion
exports.default = custom_event_name_casing_default;
@@ -0,0 +1,93 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/define-emits-declaration.js
/**
* @author Amorites
* See LICENSE file in root directory for full license.
*/
var require_define_emits_declaration = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef {import('@typescript-eslint/types').TSESTree.TypeNode} TypeNode
*
*/
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce declaration style of `defineEmits`",
categories: void 0,
url: "https://eslint.vuejs.org/rules/define-emits-declaration.html"
},
fixable: null,
schema: [{ enum: [
"type-based",
"type-literal",
"runtime"
] }],
messages: {
hasArg: "Use type based declaration instead of runtime declaration.",
hasTypeArg: "Use runtime declaration instead of type based declaration.",
hasTypeCallArg: "Use new type literal declaration instead of the old call signature declaration."
}
},
create(context) {
const scriptSetup = utils.getScriptSetupElement(context);
if (!scriptSetup || !utils.hasAttribute(scriptSetup, "lang", "ts")) return {};
const defineType = context.options[0] || "type-based";
return utils.defineScriptSetupVisitor(context, { onDefineEmitsEnter(node) {
switch (defineType) {
case "type-based":
if (node.arguments.length > 0) context.report({
node,
messageId: "hasArg"
});
break;
case "type-literal":
verifyTypeLiteral(node);
break;
case "runtime": {
const typeArguments = "typeArguments" in node ? node.typeArguments : node.typeParameters;
if (typeArguments && typeArguments.params.length > 0) context.report({
node,
messageId: "hasTypeArg"
});
break;
}
}
} });
/** @param {CallExpression} node */
function verifyTypeLiteral(node) {
if (node.arguments.length > 0) {
context.report({
node,
messageId: "hasArg"
});
return;
}
const param = (node.typeArguments || node.typeParameters)?.params[0];
if (!param) return;
if (param.type === "TSTypeLiteral") {
for (const memberNode of param.members) if (memberNode.type !== "TSPropertySignature") context.report({
node: memberNode,
messageId: "hasTypeCallArg"
});
} else if (param.type === "TSFunctionType") context.report({
node: param,
messageId: "hasTypeCallArg"
});
}
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_define_emits_declaration();
}
});
@@ -0,0 +1,287 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/define-macros-order.js
/**
* @author Eduard Deisling
* See LICENSE file in root directory for full license.
*/
var require_define_macros_order = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const MACROS_EMITS = "defineEmits";
const MACROS_PROPS = "defineProps";
const MACROS_OPTIONS = "defineOptions";
const MACROS_SLOTS = "defineSlots";
const MACROS_MODEL = "defineModel";
const MACROS_EXPOSE = "defineExpose";
const KNOWN_MACROS = new Set([
MACROS_EMITS,
MACROS_PROPS,
MACROS_OPTIONS,
MACROS_SLOTS,
MACROS_MODEL,
MACROS_EXPOSE
]);
const DEFAULT_ORDER = [MACROS_PROPS, MACROS_EMITS];
/**
* @param {VElement} scriptSetup
* @param {ASTNode} node
*/
function inScriptSetup(scriptSetup, node) {
return scriptSetup.range[0] <= node.range[0] && node.range[1] <= scriptSetup.range[1];
}
/**
* @param {ASTNode} node
*/
function isDeclareStatement(node) {
return "declare" in node && node.declare === true;
}
/**
* @param {ASTNode} node
*/
function isUseStrictStatement(node) {
return node.type === "ExpressionStatement" && node.expression.type === "Literal" && node.expression.value === "use strict";
}
/**
* Get an index of the first statement after imports and interfaces in order
* to place defineEmits and defineProps before this statement
* @param {VElement} scriptSetup
* @param {Program} program
*/
function getTargetStatementPosition(scriptSetup, program) {
const skipStatements = new Set([
"ImportDeclaration",
"TSEnumDeclaration",
"TSModuleDeclaration",
"TSInterfaceDeclaration",
"TSTypeAliasDeclaration",
"DebuggerStatement",
"EmptyStatement",
"ExportNamedDeclaration"
]);
for (const [index, item] of program.body.entries()) if (inScriptSetup(scriptSetup, item) && !skipStatements.has(item.type) && !isDeclareStatement(item) && !isUseStrictStatement(item)) return index;
return -1;
}
/**
* We need to handle cases like "const props = defineProps(...)"
* Define macros must be used only on top, so we can look for "Program" type
* inside node.parent.type
* @param {CallExpression|ASTNode} node
* @return {ASTNode}
*/
function getDefineMacrosStatement(node) {
if (!node.parent) throw new Error("Node has no parent");
if (node.parent.type === "Program") return node;
return getDefineMacrosStatement(node.parent);
}
/** @param {RuleContext} context */
function create(context) {
const scriptSetup = utils.getScriptSetupElement(context);
if (!scriptSetup) return {};
const sourceCode = context.sourceCode;
const options = context.options;
/** @type {[string, string]} */
const order = options[0] && options[0].order || DEFAULT_ORDER;
/** @type {boolean} */
const defineExposeLast = options[0] && options[0].defineExposeLast || false;
/** @type {Map<string, ASTNode[]>} */
const macrosNodes = /* @__PURE__ */ new Map();
/** @type {ASTNode} */
let defineExposeNode;
if (order.includes(MACROS_EXPOSE) && defineExposeLast) throw new Error("`defineExpose` macro can't be in the `order` array if `defineExposeLast` is true.");
return utils.compositingVisitors(utils.defineScriptSetupVisitor(context, {
onDefinePropsExit(node) {
macrosNodes.set(MACROS_PROPS, [getDefineMacrosStatement(node)]);
},
onDefineEmitsExit(node) {
macrosNodes.set(MACROS_EMITS, [getDefineMacrosStatement(node)]);
},
onDefineOptionsExit(node) {
macrosNodes.set(MACROS_OPTIONS, [getDefineMacrosStatement(node)]);
},
onDefineSlotsExit(node) {
macrosNodes.set(MACROS_SLOTS, [getDefineMacrosStatement(node)]);
},
onDefineModelExit(node) {
const currentModelMacros = macrosNodes.get(MACROS_MODEL) ?? [];
currentModelMacros.push(getDefineMacrosStatement(node));
macrosNodes.set(MACROS_MODEL, currentModelMacros);
},
onDefineExposeExit(node) {
defineExposeNode = getDefineMacrosStatement(node);
},
"Program > ExpressionStatement > CallExpression, Program > VariableDeclaration > VariableDeclarator > CallExpression"(node) {
if (node.callee && node.callee.type === "Identifier" && order.includes(node.callee.name) && !KNOWN_MACROS.has(node.callee.name)) macrosNodes.set(node.callee.name, [getDefineMacrosStatement(node)]);
}
}), { "Program:exit"(program) {
/**
* @typedef {object} OrderedData
* @property {string} name
* @property {ASTNode} node
*/
const firstStatementIndex = getTargetStatementPosition(scriptSetup, program);
const orderedList = order.flatMap((name) => {
return (macrosNodes.get(name) ?? []).map((node) => ({
name,
node
}));
}).filter(
/** @returns {data is OrderedData} */
(data) => utils.isDef(data.node)
);
if (defineExposeLast) {
const lastNode = program.body.at(-1);
if (defineExposeNode && lastNode && lastNode !== defineExposeNode) reportExposeNotOnBottom(defineExposeNode, lastNode);
}
for (const [index, should] of orderedList.entries()) {
const targetStatement = program.body[firstStatementIndex + index];
if (should.node !== targetStatement) {
let moveTargetNodes = orderedList.slice(index).map(({ node }) => node);
const targetStatementIndex = moveTargetNodes.indexOf(targetStatement);
if (targetStatementIndex !== -1) moveTargetNodes = moveTargetNodes.slice(0, targetStatementIndex);
reportNotOnTop(should.name, moveTargetNodes, targetStatement);
return;
}
}
} });
/**
* @param {string} macro
* @param {ASTNode[]} nodes
* @param {ASTNode} before
*/
function reportNotOnTop(macro, nodes, before) {
const beforeMacro = order.find((macroName) => (macrosNodes.get(macroName) ?? []).includes(before));
const messageId = beforeMacro ? "macrosUnordered" : "macrosNotAtTop";
const data = beforeMacro ? {
macro,
before: beforeMacro
} : { macro };
context.report({
node: nodes[0],
loc: nodes[0].loc,
messageId,
data,
*fix(fixer) {
for (const node of nodes) yield* moveNodeBefore(fixer, node, before);
}
});
}
/**
* @param {ASTNode} node
* @param {ASTNode} lastNode
*/
function reportExposeNotOnBottom(node, lastNode) {
context.report({
node,
loc: node.loc,
messageId: "defineExposeNotTheLast",
suggest: [{
messageId: "putExposeAtTheLast",
fix(fixer) {
return moveNodeToLast(fixer, node, lastNode);
}
}]
});
}
/**
* Move all lines of "node" with its comments to after the "target"
* @param {RuleFixer} fixer
* @param {ASTNode} node
* @param {ASTNode} target
*/
function moveNodeToLast(fixer, node, target) {
const beforeNodeToken = sourceCode.getTokenBefore(node);
const nodeComment = sourceCode.getTokenAfter(beforeNodeToken, { includeComments: true });
const nextNodeComment = sourceCode.getTokenAfter(node, { includeComments: true });
const cutStart = getLineStartIndex(nodeComment, beforeNodeToken);
const cutEnd = getLineStartIndex(nextNodeComment, node);
const textNode = sourceCode.getText(node, node.range[0] - beforeNodeToken.range[1]);
return [fixer.insertTextAfter(target, textNode), fixer.removeRange([cutStart, cutEnd])];
}
/**
* Move all lines of "node" with its comments to before the "target"
* @param {RuleFixer} fixer
* @param {ASTNode} node
* @param {ASTNode} target
*/
function moveNodeBefore(fixer, node, target) {
const beforeNodeToken = sourceCode.getTokenBefore(node);
const nodeComment = sourceCode.getTokenAfter(beforeNodeToken, { includeComments: true });
const nextNodeComment = sourceCode.getTokenAfter(node, { includeComments: true });
const cutStart = getLineStartIndex(nodeComment, beforeNodeToken);
const cutEnd = getLineStartIndex(nextNodeComment, node);
const beforeTargetToken = sourceCode.getTokenBefore(target);
const targetComment = sourceCode.getTokenAfter(beforeTargetToken, { includeComments: true });
const insertText = getInsertText(sourceCode.getText(node, node.range[0] - nodeComment.range[0]), target);
return [fixer.insertTextBefore(targetComment, insertText), fixer.removeRange([cutStart, cutEnd])];
}
/**
* Get result text to insert
* @param {string} textNode
* @param {ASTNode} target
*/
function getInsertText(textNode, target) {
const afterTargetComment = sourceCode.getTokenAfter(target, { includeComments: true });
const afterText = sourceCode.text.slice(target.range[1], afterTargetComment.range[0]);
const invalidResult = !textNode.endsWith(";") && !afterText.includes("\n");
return textNode + afterText + (invalidResult ? ";" : "");
}
/**
* Get position of the beginning of the token's line(or prevToken end if no line)
* @param {ASTNode|Token} token
* @param {ASTNode|Token} prevToken
*/
function getLineStartIndex(token, prevToken) {
if (token.loc.start.line === prevToken.loc.end.line) return prevToken.range[1];
return sourceCode.getIndexFromLoc({
line: token.loc.start.line,
column: 0
});
}
}
module.exports = {
meta: {
type: "layout",
docs: {
description: "enforce order of compiler macros (`defineProps`, `defineEmits`, etc.)",
categories: void 0,
url: "https://eslint.vuejs.org/rules/define-macros-order.html"
},
fixable: "code",
hasSuggestions: true,
schema: [{
type: "object",
properties: {
order: {
type: "array",
items: {
type: "string",
minLength: 1
},
uniqueItems: true,
additionalItems: false
},
defineExposeLast: { type: "boolean" }
},
additionalProperties: false
}],
messages: {
macrosNotAtTop: "{{macro}} should be placed at the top of `<script setup>` (after any potential import statements or type definitions).",
macrosUnordered: "{{macro}} should be above {{before}}.",
defineExposeNotTheLast: "`defineExpose` should be the last statement in `<script setup>`.",
putExposeAtTheLast: "Put `defineExpose` as the last statement in `<script setup>`."
}
},
create
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_define_macros_order();
}
});
@@ -0,0 +1,60 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/define-props-declaration.js
/**
* @author Amorites
* See LICENSE file in root directory for full license.
*/
var require_define_props_declaration = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce declaration style of `defineProps`",
categories: void 0,
url: "https://eslint.vuejs.org/rules/define-props-declaration.html"
},
fixable: null,
schema: [{ enum: ["type-based", "runtime"] }],
messages: {
hasArg: "Use type-based declaration instead of runtime declaration.",
hasTypeArg: "Use runtime declaration instead of type-based declaration."
}
},
create(context) {
const scriptSetup = utils.getScriptSetupElement(context);
if (!scriptSetup || !utils.hasAttribute(scriptSetup, "lang", "ts")) return {};
const defineType = context.options[0] || "type-based";
return utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node) {
switch (defineType) {
case "type-based":
if (node.arguments.length > 0) context.report({
node,
messageId: "hasArg"
});
break;
case "runtime": {
const typeArguments = "typeArguments" in node ? node.typeArguments : node.typeParameters;
if (typeArguments && typeArguments.params.length > 0) context.report({
node,
messageId: "hasTypeArg"
});
break;
}
}
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_define_props_declaration();
}
});
@@ -0,0 +1,73 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/define-props-destructuring.js
/**
* @author Wayne Zhang
* See LICENSE file in root directory for full license.
*/
var require_define_props_destructuring = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce consistent style for props destructuring",
categories: void 0,
url: "https://eslint.vuejs.org/rules/define-props-destructuring.html"
},
fixable: null,
schema: [{
type: "object",
properties: { destructure: { enum: [
"only-when-assigned",
"always",
"never"
] } },
additionalProperties: false
}],
messages: {
preferDestructuring: "Prefer destructuring from `defineProps` directly.",
avoidDestructuring: "Avoid destructuring from `defineProps`.",
avoidWithDefaults: "Avoid using `withDefaults` with destructuring."
}
},
create(context) {
const destructurePreference = (context.options[0] || {}).destructure || "only-when-assigned";
return utils.compositingVisitors(utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node, props) {
if (!props.some((prop) => prop.propName || prop.type === "unknown" && prop.node)) return;
const hasDestructure = utils.isUsingPropsDestructure(node);
const hasWithDefaults = utils.hasWithDefaults(node);
const hasAssigned = !!utils.getLeftOfDefineProps(node);
if (destructurePreference === "never") {
if (hasDestructure) context.report({
node,
messageId: "avoidDestructuring"
});
return;
}
if (!hasDestructure && (destructurePreference === "always" || hasAssigned)) {
context.report({
node,
messageId: "preferDestructuring"
});
return;
}
if (hasWithDefaults) context.report({
node: node.parent.callee,
messageId: "avoidWithDefaults"
});
} }));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_define_props_destructuring();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/dot-location.js
/**
* @author Yosuke Ota
*/
var require_dot_location = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("dot-location");
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_dot_location();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/dot-notation.js
/**
* @author Yosuke Ota
*/
var require_dot_notation = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("dot-notation", { applyDocument: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_dot_notation();
}
});
@@ -0,0 +1,126 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/enforce-style-attribute.js
/**
* @author Mussin Benarbia
* See LICENSE file in root directory for full license.
*/
var require_enforce_style_attribute = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { isVElement } = require_index.default;
/**
* check whether a tag has the `scoped` attribute
* @param {VElement} componentBlock
*/
function isScoped(componentBlock) {
return componentBlock.startTag.attributes.some((attribute) => !attribute.directive && attribute.key.name === "scoped");
}
/**
* check whether a tag has the `module` attribute
* @param {VElement} componentBlock
*/
function isModule(componentBlock) {
return componentBlock.startTag.attributes.some((attribute) => !attribute.directive && attribute.key.name === "module");
}
/**
* check if a tag doesn't have either the `scoped` nor `module` attribute
* @param {VElement} componentBlock
*/
function isPlain(componentBlock) {
return !isScoped(componentBlock) && !isModule(componentBlock);
}
/** @param {RuleContext} context */
function getUserDefinedAllowedAttrs(context) {
if (context.options[0] && context.options[0].allow) return context.options[0].allow;
return [];
}
const defaultAllowedAttrs = ["scoped"];
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce or forbid the use of the `scoped` and `module` attributes in SFC top level style tags",
categories: void 0,
url: "https://eslint.vuejs.org/rules/enforce-style-attribute.html"
},
fixable: null,
schema: [{
type: "object",
properties: { allow: {
type: "array",
minItems: 1,
uniqueItems: true,
items: {
type: "string",
enum: [
"plain",
"scoped",
"module"
]
}
} },
additionalProperties: false
}],
messages: {
notAllowedScoped: "The scoped attribute is not allowed. Allowed: {{ allowedAttrsString }}.",
notAllowedModule: "The module attribute is not allowed. Allowed: {{ allowedAttrsString }}.",
notAllowedPlain: "Plain <style> tags are not allowed. Allowed: {{ allowedAttrsString }}."
}
},
create(context) {
const sourceCode = context.sourceCode;
if (!sourceCode.parserServices.getDocumentFragment) return {};
const documentFragment = sourceCode.parserServices.getDocumentFragment();
if (!documentFragment) return {};
const topLevelStyleTags = documentFragment.children.filter(
/** @returns {element is VElement} */
(element) => isVElement(element) && element.rawName === "style"
);
if (topLevelStyleTags.length === 0) return {};
const userDefinedAllowedAttrs = getUserDefinedAllowedAttrs(context);
const allowedAttrs = userDefinedAllowedAttrs.length > 0 ? userDefinedAllowedAttrs : defaultAllowedAttrs;
const allowsPlain = allowedAttrs.includes("plain");
const allowsScoped = allowedAttrs.includes("scoped");
const allowsModule = allowedAttrs.includes("module");
const allowedAttrsString = [...allowedAttrs].sort().join(", ");
return { Program() {
for (const styleTag of topLevelStyleTags) {
if (!allowsPlain && isPlain(styleTag)) {
context.report({
node: styleTag,
messageId: "notAllowedPlain",
data: { allowedAttrsString }
});
return;
}
if (!allowsScoped && isScoped(styleTag)) {
context.report({
node: styleTag,
messageId: "notAllowedScoped",
data: { allowedAttrsString }
});
return;
}
if (!allowsModule && isModule(styleTag)) {
context.report({
node: styleTag,
messageId: "notAllowedModule",
data: { allowedAttrsString }
});
return;
}
}
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_enforce_style_attribute();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/eqeqeq.js
/**
* @author Toru Nagashima
*/
var require_eqeqeq = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapCoreRule } = require_index.default;
module.exports = wrapCoreRule("eqeqeq", { applyDocument: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_eqeqeq();
}
});
@@ -0,0 +1,86 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/first-attribute-linebreak.js
/**
* @fileoverview Enforce the location of first attribute
* @author Yosuke Ota
*/
var require_first_attribute_linebreak = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "layout",
docs: {
description: "enforce the location of first attribute",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/first-attribute-linebreak.html"
},
fixable: "whitespace",
schema: [{
type: "object",
properties: {
multiline: { enum: [
"below",
"beside",
"ignore"
] },
singleline: { enum: [
"below",
"beside",
"ignore"
] }
},
additionalProperties: false
}],
messages: {
expected: "Expected a linebreak before this attribute.",
unexpected: "Expected no linebreak before this attribute."
}
},
create(context) {
/** @type {"below" | "beside" | "ignore"} */
const singleline = context.options[0] && context.options[0].singleline || "ignore";
/** @type {"below" | "beside" | "ignore"} */
const multiline = context.options[0] && context.options[0].multiline || "below";
const sourceCode = context.sourceCode;
const template = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
/**
* Report attribute
* @param {VAttribute | VDirective} firstAttribute
* @param { "below" | "beside"} location
*/
function report(firstAttribute, location) {
context.report({
node: firstAttribute,
messageId: location === "beside" ? "unexpected" : "expected",
fix(fixer) {
const prevToken = template.getTokenBefore(firstAttribute, { includeComments: true });
return fixer.replaceTextRange([prevToken.range[1], firstAttribute.range[0]], location === "beside" ? " " : "\n");
}
});
}
return utils.defineTemplateBodyVisitor(context, { VStartTag(node) {
const firstAttribute = node.attributes[0];
const lastAttribute = node.attributes.at(-1);
if (!firstAttribute || !lastAttribute) return;
const location = firstAttribute.loc.start.line === lastAttribute.loc.end.line ? singleline : multiline;
if (location === "ignore") return;
if (location === "beside") {
if (node.loc.start.line === firstAttribute.loc.start.line) return;
} else if (node.loc.start.line < firstAttribute.loc.start.line) return;
report(firstAttribute, location);
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_first_attribute_linebreak();
}
});
@@ -0,0 +1,28 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/func-call-spacing.js
/**
* @author Yosuke Ota
*/
var require_func_call_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule({
core: "func-call-spacing",
stylistic: "function-call-spacing",
vue: "func-call-spacing"
}, {
skipDynamicArguments: true,
applyDocument: true
});
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_func_call_spacing();
}
});
@@ -0,0 +1,115 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/html-button-has-type.js
/**
* @fileoverview Disallow usage of button without an explicit type attribute
* @author Jonathan Santerre <jonathan@santerre.dev>
*/
var require_html_button_has_type = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
*
* @param {string} type
* @returns {type is 'button' | 'submit' | 'reset'}
*/
function isButtonType(type) {
return type === "button" || type === "submit" || type === "reset";
}
const optionDefaults = {
button: true,
submit: true,
reset: true
};
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow usage of button without an explicit type attribute",
categories: null,
url: "https://eslint.vuejs.org/rules/html-button-has-type.html"
},
fixable: null,
schema: [{
type: "object",
properties: {
button: { type: "boolean" },
submit: { type: "boolean" },
reset: { type: "boolean" }
},
additionalProperties: false
}],
messages: {
missingTypeAttribute: "Missing an explicit type attribute for button.",
invalidTypeAttribute: "{{value}} is an invalid value for button type attribute.",
forbiddenTypeAttribute: "{{value}} is a forbidden value for button type attribute.",
emptyTypeAttribute: "A value must be set for button type attribute."
}
},
create(context) {
/**
* @typedef {object} Configuration
* @property {boolean} button
* @property {boolean} submit
* @property {boolean} reset
*/
/** @type {Configuration} */
const configuration = Object.assign({}, optionDefaults, context.options[0]);
/**
* @param {ASTNode} node
* @param {string} messageId
* @param {any} [data]
*/
function report(node, messageId, data) {
context.report({
node,
messageId,
data
});
}
/**
* @param {VAttribute} attribute
*/
function validateAttribute(attribute) {
const value = attribute.value;
if (!value || !value.value) {
report(value || attribute, "emptyTypeAttribute");
return;
}
const strValue = value.value;
if (!isButtonType(strValue)) report(value, "invalidTypeAttribute", { value: strValue });
else if (!configuration[strValue]) report(value, "forbiddenTypeAttribute", { value: strValue });
}
/**
* @param {VDirective} directive
*/
function validateDirective(directive) {
const value = directive.value;
if (!value || !value.expression) report(value || directive, "emptyTypeAttribute");
}
return utils.defineTemplateBodyVisitor(context, { "VElement[rawName='button']"(node) {
const typeAttr = utils.getAttribute(node, "type");
if (typeAttr) {
validateAttribute(typeAttr);
return;
}
const typeDir = utils.getDirective(node, "bind", "type");
if (typeDir) {
validateDirective(typeDir);
return;
}
report(node.startTag, "missingTypeAttribute");
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_html_button_has_type();
}
});
@@ -0,0 +1,121 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/html-closing-bracket-newline.js
/**
* @author Toru Nagashima
* @copyright 2016 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
var require_html_closing_bracket_newline = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @param {number} lineBreaks
*/
function getPhrase(lineBreaks) {
switch (lineBreaks) {
case 0: return "no line breaks";
case 1: return "1 line break";
default: return `${lineBreaks} line breaks`;
}
}
/**
* @typedef LineBreakBehavior
* @type {('always'|'never')}
*/
/**
* @typedef LineType
* @type {('singleline'|'multiline')}
*/
/**
* @typedef RuleOptions
* @type {object}
* @property {LineBreakBehavior} singleline - The behavior for single line tags.
* @property {LineBreakBehavior} multiline - The behavior for multiline tags.
* @property {object} selfClosingTag
* @property {LineBreakBehavior} selfClosingTag.singleline - The behavior for single line self closing tags.
* @property {LineBreakBehavior} selfClosingTag.multiline - The behavior for multiline self closing tags.
*/
/**
* @param {VStartTag | VEndTag} node - The node representing a start or end tag.
* @param {RuleOptions} options - The options for line breaks.
* @param {LineType} type - The type of line break.
* @returns {number} - The expected line breaks.
*/
function getExpectedLineBreaks(node, options, type) {
if (node.type === "VStartTag" && node.selfClosing && options.selfClosingTag && options.selfClosingTag[type]) return options.selfClosingTag[type] === "always" ? 1 : 0;
return options[type] === "always" ? 1 : 0;
}
module.exports = {
meta: {
type: "layout",
docs: {
description: "require or disallow a line break before tag's closing brackets",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/html-closing-bracket-newline.html"
},
fixable: "whitespace",
schema: [{
type: "object",
properties: {
singleline: { enum: ["always", "never"] },
multiline: { enum: ["always", "never"] },
selfClosingTag: {
type: "object",
properties: {
singleline: { enum: ["always", "never"] },
multiline: { enum: ["always", "never"] }
},
additionalProperties: false,
minProperties: 1
}
},
additionalProperties: false
}],
messages: { expectedBeforeClosingBracket: "Expected {{expected}} before closing bracket, but {{actual}} found." }
},
create(context) {
const options = Object.assign({}, {
singleline: "never",
multiline: "always"
}, context.options[0] || {});
const sourceCode = context.sourceCode;
const template = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
return utils.defineDocumentVisitor(context, { "VStartTag, VEndTag"(node) {
const closingBracketToken = template.getLastToken(node);
if (closingBracketToken.type !== "HTMLSelfClosingTagClose" && closingBracketToken.type !== "HTMLTagClose") return;
const prevToken = template.getTokenBefore(closingBracketToken);
const expectedLineBreaks = getExpectedLineBreaks(node, options, node.loc.start.line === prevToken.loc.end.line ? "singleline" : "multiline");
const actualLineBreaks = closingBracketToken.loc.start.line - prevToken.loc.end.line;
if (actualLineBreaks !== expectedLineBreaks) context.report({
node,
loc: {
start: prevToken.loc.end,
end: closingBracketToken.loc.start
},
messageId: "expectedBeforeClosingBracket",
data: {
expected: getPhrase(expectedLineBreaks),
actual: getPhrase(actualLineBreaks)
},
fix(fixer) {
/** @type {Range} */
const range = [prevToken.range[1], closingBracketToken.range[0]];
const text = "\n".repeat(expectedLineBreaks);
return fixer.replaceTextRange(range, text);
}
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_html_closing_bracket_newline();
}
});
@@ -0,0 +1,97 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/html-closing-bracket-spacing.js
/**
* @author Toru Nagashima <https://github.com/mysticatea>
*/
var require_html_closing_bracket_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef { {startTag?:"always"|"never",endTag?:"always"|"never",selfClosingTag?:"always"|"never"} } Options
*/
/**
* Normalize options.
* @param {Options} options The options user configured.
* @param {ParserServices.TokenStore} tokens The token store of template body.
* @returns {Options & { detectType: (node: VStartTag | VEndTag) => 'never' | 'always' | null }} The normalized options.
*/
function parseOptions(options, tokens) {
const opts = Object.assign({
startTag: "never",
endTag: "never",
selfClosingTag: "always"
}, options);
return Object.assign(opts, { detectType(node) {
const openType = tokens.getFirstToken(node).type;
const closeType = tokens.getLastToken(node).type;
if (openType === "HTMLEndTagOpen" && closeType === "HTMLTagClose") return opts.endTag;
if (openType === "HTMLTagOpen" && closeType === "HTMLTagClose") return opts.startTag;
if (openType === "HTMLTagOpen" && closeType === "HTMLSelfClosingTagClose") return opts.selfClosingTag;
return null;
} });
}
module.exports = {
meta: {
type: "layout",
docs: {
description: "require or disallow a space before tag's closing brackets",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/html-closing-bracket-spacing.html"
},
fixable: "whitespace",
schema: [{
type: "object",
properties: {
startTag: { enum: ["always", "never"] },
endTag: { enum: ["always", "never"] },
selfClosingTag: { enum: ["always", "never"] }
},
additionalProperties: false
}],
messages: {
missing: "Expected a space before '{{bracket}}', but not found.",
unexpected: "Expected no space before '{{bracket}}', but found."
}
},
create(context) {
const sourceCode = context.sourceCode;
const tokens = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
const options = parseOptions(context.options[0], tokens);
return utils.defineDocumentVisitor(context, { "VStartTag, VEndTag"(node) {
const type = options.detectType(node);
const lastToken = tokens.getLastToken(node);
const prevToken = tokens.getLastToken(node, 1);
if (type == null || prevToken == null || prevToken.loc.end.line !== lastToken.loc.start.line) return;
const hasSpace = prevToken.range[1] !== lastToken.range[0];
if (type === "always" && !hasSpace) context.report({
node,
loc: lastToken.loc,
messageId: "missing",
data: { bracket: sourceCode.getText(lastToken) },
fix: (fixer) => fixer.insertTextBefore(lastToken, " ")
});
else if (type === "never" && hasSpace) context.report({
node,
loc: {
start: prevToken.loc.end,
end: lastToken.loc.end
},
messageId: "unexpected",
data: { bracket: sourceCode.getText(lastToken) },
fix: (fixer) => fixer.removeRange([prevToken.range[1], lastToken.range[0]])
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_html_closing_bracket_spacing();
}
});
@@ -0,0 +1,127 @@
const require_html_comments = require('../utils/html-comments.js');
//#region lib/rules/html-comment-content-newline.ts
function parseOption(param) {
if (param && typeof param === "string") return {
singleline: param,
multiline: param
};
return Object.assign({
singleline: "never",
multiline: "always"
}, param);
}
var html_comment_content_newline_default = {
meta: {
type: "layout",
docs: {
description: "enforce unified line break in HTML comments",
categories: void 0,
url: "https://eslint.vuejs.org/rules/html-comment-content-newline.html"
},
fixable: "whitespace",
schema: [{ oneOf: [{ enum: ["always", "never"] }, {
type: "object",
properties: {
singleline: { enum: [
"always",
"never",
"ignore"
] },
multiline: { enum: [
"always",
"never",
"ignore"
] }
},
additionalProperties: false
}] }, {
type: "object",
properties: { exceptions: {
type: "array",
items: { type: "string" }
} },
additionalProperties: false
}],
messages: {
expectedAfterHTMLCommentOpen: "Expected line break after '<!--'.",
expectedBeforeHTMLCommentOpen: "Expected line break before '-->'.",
expectedAfterExceptionBlock: "Expected line break after exception block.",
expectedBeforeExceptionBlock: "Expected line break before exception block.",
unexpectedAfterHTMLCommentOpen: "Unexpected line breaks after '<!--'.",
unexpectedBeforeHTMLCommentOpen: "Unexpected line breaks before '-->'."
}
},
create(context) {
const option = parseOption(context.options[0]);
return require_html_comments.defineVisitor(context, context.options[1], (comment) => {
const { value, openDecoration, closeDecoration } = comment;
if (!value) return;
const newlineType = (openDecoration ? openDecoration.loc.end.line : value.loc.start.line) === (closeDecoration ? closeDecoration.loc.start.line : value.loc.end.line) ? option.singleline : option.multiline;
if (newlineType === "ignore") return;
checkCommentOpen(comment, newlineType !== "never");
checkCommentClose(comment, newlineType !== "never");
});
/**
* Reports the newline before the contents of a given comment if it's invalid.
*/
function checkCommentOpen(comment, requireNewline) {
const { value, openDecoration, open } = comment;
if (!value) return;
const beforeToken = openDecoration || open;
if (requireNewline) {
if (beforeToken.loc.end.line < value.loc.start.line) return;
context.report({
loc: {
start: beforeToken.loc.end,
end: value.loc.start
},
messageId: openDecoration ? "expectedAfterExceptionBlock" : "expectedAfterHTMLCommentOpen",
fix: openDecoration ? void 0 : (fixer) => fixer.insertTextAfter(beforeToken, "\n")
});
} else {
if (beforeToken.loc.end.line === value.loc.start.line) return;
context.report({
loc: {
start: beforeToken.loc.end,
end: value.loc.start
},
messageId: "unexpectedAfterHTMLCommentOpen",
fix: (fixer) => fixer.replaceTextRange([beforeToken.range[1], value.range[0]], " ")
});
}
}
/**
* Reports the space after the contents of a given comment if it's invalid.
*/
function checkCommentClose(comment, requireNewline) {
const { value, closeDecoration, close } = comment;
if (!value) return;
const afterToken = closeDecoration || close;
if (requireNewline) {
if (value.loc.end.line < afterToken.loc.start.line) return;
context.report({
loc: {
start: value.loc.end,
end: afterToken.loc.start
},
messageId: closeDecoration ? "expectedBeforeExceptionBlock" : "expectedBeforeHTMLCommentOpen",
fix: closeDecoration ? void 0 : (fixer) => fixer.insertTextBefore(afterToken, "\n")
});
} else {
if (value.loc.end.line === afterToken.loc.start.line) return;
context.report({
loc: {
start: value.loc.end,
end: afterToken.loc.start
},
messageId: "unexpectedBeforeHTMLCommentOpen",
fix: (fixer) => fixer.replaceTextRange([value.range[1], afterToken.range[0]], " ")
});
}
}
}
};
//#endregion
exports.default = html_comment_content_newline_default;
@@ -0,0 +1,102 @@
const require_html_comments = require('../utils/html-comments.js');
//#region lib/rules/html-comment-content-spacing.ts
var html_comment_content_spacing_default = {
meta: {
type: "layout",
docs: {
description: "enforce unified spacing in HTML comments",
categories: void 0,
url: "https://eslint.vuejs.org/rules/html-comment-content-spacing.html"
},
fixable: "whitespace",
schema: [{ enum: ["always", "never"] }, {
type: "object",
properties: { exceptions: {
type: "array",
items: { type: "string" }
} },
additionalProperties: false
}],
messages: {
expectedAfterHTMLCommentOpen: "Expected space after '<!--'.",
expectedBeforeHTMLCommentOpen: "Expected space before '-->'.",
expectedAfterExceptionBlock: "Expected space after exception block.",
expectedBeforeExceptionBlock: "Expected space before exception block.",
unexpectedAfterHTMLCommentOpen: "Unexpected space after '<!--'.",
unexpectedBeforeHTMLCommentOpen: "Unexpected space before '-->'."
}
},
create(context) {
const requireSpace = context.options[0] !== "never";
return require_html_comments.defineVisitor(context, context.options[1], (comment) => {
checkCommentOpen(comment);
checkCommentClose(comment);
}, { includeDirectives: true });
/**
* Reports the space before the contents of a given comment if it's invalid.
*/
function checkCommentOpen(comment) {
const { value, openDecoration, open } = comment;
if (!value) return;
const beforeToken = openDecoration || open;
if (beforeToken.loc.end.line !== value.loc.start.line) return;
if (requireSpace) {
if (beforeToken.range[1] < value.range[0]) return;
context.report({
loc: {
start: beforeToken.loc.end,
end: value.loc.start
},
messageId: openDecoration ? "expectedAfterExceptionBlock" : "expectedAfterHTMLCommentOpen",
fix: openDecoration ? void 0 : (fixer) => fixer.insertTextAfter(beforeToken, " ")
});
} else {
if (openDecoration) return;
if (beforeToken.range[1] === value.range[0]) return;
context.report({
loc: {
start: beforeToken.loc.end,
end: value.loc.start
},
messageId: "unexpectedAfterHTMLCommentOpen",
fix: (fixer) => fixer.removeRange([beforeToken.range[1], value.range[0]])
});
}
}
/**
* Reports the space after the contents of a given comment if it's invalid.
*/
function checkCommentClose(comment) {
const { value, closeDecoration, close } = comment;
if (!value) return;
const afterToken = closeDecoration || close;
if (value.loc.end.line !== afterToken.loc.start.line) return;
if (requireSpace) {
if (value.range[1] < afterToken.range[0]) return;
context.report({
loc: {
start: value.loc.end,
end: afterToken.loc.start
},
messageId: closeDecoration ? "expectedBeforeExceptionBlock" : "expectedBeforeHTMLCommentOpen",
fix: closeDecoration ? void 0 : (fixer) => fixer.insertTextBefore(afterToken, " ")
});
} else {
if (closeDecoration) return;
if (value.range[1] === afterToken.range[0]) return;
context.report({
loc: {
start: value.loc.end,
end: afterToken.loc.start
},
messageId: "unexpectedBeforeHTMLCommentOpen",
fix: (fixer) => fixer.removeRange([value.range[1], afterToken.range[0]])
});
}
}
}
};
//#endregion
exports.default = html_comment_content_spacing_default;
@@ -0,0 +1,174 @@
const require_html_comments = require('../utils/html-comments.js');
//#region lib/rules/html-comment-indent.ts
/**
* @author Yosuke ota
* See LICENSE file in root directory for full license.
*/
function parseOptions(type) {
const ret = {
indentChar: " ",
indentSize: 2,
indentText: ""
};
if (Number.isSafeInteger(type)) ret.indentSize = Number(type);
else if (type === "tab") {
ret.indentChar = " ";
ret.indentSize = 1;
}
ret.indentText = ret.indentChar.repeat(ret.indentSize);
return ret;
}
function toDisplay(s, unitChar) {
if (s.length === 0 && unitChar) return `0 ${toUnit(unitChar)}s`;
const char = s[0];
if ((char === " " || char === " ") && [...s].every((c) => c === char)) return `${s.length} ${toUnit(char)}${s.length === 1 ? "" : "s"}`;
return JSON.stringify(s);
}
function toUnit(char) {
if (char === " ") return "tab";
if (char === " ") return "space";
return JSON.stringify(char);
}
var html_comment_indent_default = {
meta: {
type: "layout",
docs: {
description: "enforce consistent indentation in HTML comments",
categories: void 0,
url: "https://eslint.vuejs.org/rules/html-comment-indent.html"
},
fixable: "whitespace",
schema: [{ oneOf: [{
type: "integer",
minimum: 0
}, { enum: ["tab"] }] }],
messages: {
unexpectedBaseIndentation: "Expected base point indentation of {{expected}}, but found {{actual}}.",
missingBaseIndentation: "Expected base point indentation of {{expected}}, but not found.",
unexpectedIndentationCharacter: "Expected {{expected}} character, but found {{actual}} character.",
unexpectedIndentation: "Expected indentation of {{expected}} but found {{actual}}.",
unexpectedRelativeIndentation: "Expected relative indentation of {{expected}} but found {{actual}}."
}
},
create(context) {
const options = parseOptions(context.options[0]);
const sourceCode = context.sourceCode;
return require_html_comments.defineVisitor(context, null, (comment) => {
const baseIndentText = getLineIndentText(comment.open.loc.start.line);
let endLine;
if (comment.value) {
const startLine = comment.value.loc.start.line;
endLine = comment.value.loc.end.line;
const checkStartLine = comment.open.loc.end.line === startLine ? startLine + 1 : startLine;
for (let line = checkStartLine; line <= endLine; line++) validateIndentForLine(line, baseIndentText, 1);
} else endLine = comment.open.loc.end.line;
if (endLine < comment.close.loc.start.line) validateIndentForLine(comment.close.loc.start.line, baseIndentText, 0);
}, { includeDirectives: true });
/**
* Checks whether the given line is a blank line.
*
* @param line The number of line. Begins with 1.
*/
function isEmptyLine(line) {
return !sourceCode.getLines()[line - 1].trim();
}
/**
* Get the actual indentation of the given line.
*
* @param line The number of line. Begins with 1.
*/
function getLineIndentText(line) {
const lineText = sourceCode.getLines()[line - 1];
const charIndex = lineText.search(/\S/);
return lineText.slice(0, charIndex);
}
/**
* Define the function which fixes the problem.
*/
function defineFix(line, actualIndentText, expectedIndentText) {
return (fixer) => {
const start = sourceCode.getIndexFromLoc({
line,
column: 0
});
return fixer.replaceTextRange([start, start + actualIndentText.length], expectedIndentText);
};
}
/**
* Validate the indentation of a line.
*
* @param line The number of line. Begins with 1.
*/
function validateIndentForLine(line, baseIndentText, offset) {
if (isEmptyLine(line)) return;
const actualIndentText = getLineIndentText(line);
const expectedOffsetIndentText = options.indentText.repeat(offset);
const expectedIndentText = baseIndentText + expectedOffsetIndentText;
if (baseIndentText && (actualIndentText.length < baseIndentText.length || !actualIndentText.startsWith(baseIndentText))) {
context.report({
loc: {
start: {
line,
column: 0
},
end: {
line,
column: actualIndentText.length
}
},
messageId: actualIndentText ? "unexpectedBaseIndentation" : "missingBaseIndentation",
data: {
expected: toDisplay(baseIndentText),
actual: toDisplay(actualIndentText.slice(0, baseIndentText.length))
},
fix: defineFix(line, actualIndentText, expectedIndentText)
});
return;
}
const actualOffsetIndentText = actualIndentText.slice(baseIndentText.length);
for (const [i, char] of [...actualOffsetIndentText].entries()) if (char !== options.indentChar) {
context.report({
loc: {
start: {
line,
column: baseIndentText.length + i
},
end: {
line,
column: baseIndentText.length + i + 1
}
},
messageId: "unexpectedIndentationCharacter",
data: {
expected: toUnit(options.indentChar),
actual: toUnit(char)
},
fix: defineFix(line, actualIndentText, expectedIndentText)
});
return;
}
if (actualOffsetIndentText.length !== expectedOffsetIndentText.length) context.report({
loc: {
start: {
line,
column: baseIndentText.length
},
end: {
line,
column: actualIndentText.length
}
},
messageId: baseIndentText ? "unexpectedRelativeIndentation" : "unexpectedIndentation",
data: {
expected: toDisplay(expectedOffsetIndentText, options.indentChar),
actual: toDisplay(actualOffsetIndentText, options.indentChar)
},
fix: defineFix(line, actualIndentText, expectedIndentText)
});
}
}
};
//#endregion
exports.default = html_comment_indent_default;
+54
View File
@@ -0,0 +1,54 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/html-end-tags.js
/**
* @author Toru Nagashima
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
var require_html_end_tags = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce end tag style",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/html-end-tags.html"
},
fixable: "code",
schema: [],
messages: { missingEndTag: "'<{{name}}>' should have end tag." }
},
create(context) {
let hasInvalidEOF = false;
return utils.defineTemplateBodyVisitor(context, { VElement(node) {
if (hasInvalidEOF) return;
const name = node.name;
const isVoid = utils.isHtmlVoidElementName(name);
const isSelfClosing = node.startTag.selfClosing;
const hasEndTag = node.endTag != null;
if (!isVoid && !hasEndTag && !isSelfClosing) context.report({
node: node.startTag,
loc: node.startTag.loc,
messageId: "missingEndTag",
data: { name },
fix: (fixer) => fixer.insertTextAfter(node, `</${name}>`)
});
} }, { Program(node) {
hasInvalidEOF = utils.hasInvalidEOF(node);
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_html_end_tags();
}
});
+89
View File
@@ -0,0 +1,89 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_indent_common = require('../utils/indent-common.js');
//#region lib/rules/html-indent.ts
/**
* @author Toru Nagashima
* @copyright 2016 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
var html_indent_default = {
create(context) {
const sourceCode = context.sourceCode;
const visitor = require_indent_common.defineVisitor(context, sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore(), { baseIndent: 1 });
return import_utils.default.defineTemplateBodyVisitor(context, visitor);
},
meta: {
type: "layout",
docs: {
description: "enforce consistent indentation in `<template>`",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/html-indent.html"
},
fixable: "whitespace",
schema: [{ oneOf: [{
type: "integer",
minimum: 1
}, { enum: ["tab"] }] }, {
type: "object",
properties: {
attribute: {
type: "integer",
minimum: 0
},
baseIndent: {
type: "integer",
minimum: 0
},
closeBracket: { oneOf: [{
type: "integer",
minimum: 0
}, {
type: "object",
properties: {
startTag: {
type: "integer",
minimum: 0
},
endTag: {
type: "integer",
minimum: 0
},
selfClosingTag: {
type: "integer",
minimum: 0
}
},
additionalProperties: false
}] },
switchCase: {
type: "integer",
minimum: 0
},
alignAttributesVertically: { type: "boolean" },
ignores: {
type: "array",
items: { allOf: [
{ type: "string" },
{ not: {
type: "string",
pattern: ":exit$"
} },
{ not: {
type: "string",
pattern: String.raw`^\s*$`
} }
] },
uniqueItems: true,
additionalItems: false
}
},
additionalProperties: false
}]
}
};
//#endregion
exports.default = html_indent_default;
+78
View File
@@ -0,0 +1,78 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/html-quotes.js
/**
* @author Toru Nagashima
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
var require_html_quotes = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "layout",
docs: {
description: "enforce quotes style of HTML attributes",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/html-quotes.html"
},
fixable: "code",
schema: [{ enum: ["double", "single"] }, {
type: "object",
properties: { avoidEscape: { type: "boolean" } },
additionalProperties: false
}],
messages: { expected: "Expected to be enclosed by {{kind}}." }
},
create(context) {
const sourceCode = context.sourceCode;
const double = context.options[0] !== "single";
const avoidEscape = context.options[1] && context.options[1].avoidEscape === true;
const quoteChar = double ? "\"" : "'";
const quoteName = double ? "double quotes" : "single quotes";
/** @type {boolean} */
let hasInvalidEOF;
return utils.defineTemplateBodyVisitor(context, { "VAttribute[value!=null]"(node) {
if (hasInvalidEOF) return;
if (utils.isVBindSameNameShorthand(node)) return;
const text = sourceCode.getText(node.value);
const firstChar = text[0];
if (firstChar !== quoteChar) {
const quoted = firstChar === "'" || firstChar === "\"";
if (avoidEscape && quoted) {
if (text.slice(1, -1).includes(quoteChar)) return;
}
context.report({
node: node.value,
loc: node.value.loc,
messageId: "expected",
data: { kind: quoteName },
fix(fixer) {
const contentText = quoted ? text.slice(1, -1) : text;
let fixToDouble = double;
if (avoidEscape && !quoted && contentText.includes(quoteChar)) fixToDouble = double ? contentText.includes("'") : !contentText.includes("\"");
const quotePattern = fixToDouble ? /"/g : /'/g;
const quoteEscaped = fixToDouble ? "&quot;" : "&apos;";
const fixQuoteChar = fixToDouble ? "\"" : "'";
const replacement = fixQuoteChar + contentText.replace(quotePattern, quoteEscaped) + fixQuoteChar;
return fixer.replaceText(node.value, replacement);
}
});
}
} }, { Program(node) {
hasInvalidEOF = utils.hasInvalidEOF(node);
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_html_quotes();
}
});
+142
View File
@@ -0,0 +1,142 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/html-self-closing.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
/**
* These strings wil be displayed in error messages.
*/
const ELEMENT_TYPE_MESSAGES = Object.freeze({
NORMAL: "HTML elements",
VOID: "HTML void elements",
COMPONENT: "Vue.js custom components",
SVG: "SVG elements",
MATH: "MathML elements",
UNKNOWN: "unknown elements"
});
/**
* Normalize the given options.
*/
function parseOptions(options) {
return {
NORMAL: options && options.html && options.html.normal || "always",
VOID: options && options.html && options.html.void || "never",
COMPONENT: options && options.html && options.html.component || "always",
SVG: options && options.svg || "always",
MATH: options && options.math || "always",
UNKNOWN: null
};
}
/**
* Get the elementType of the given element.
*/
function getElementType(node) {
if (import_utils.default.isCustomComponent(node)) return "COMPONENT";
if (import_utils.default.isHtmlElementNode(node)) {
if (import_utils.default.isHtmlVoidElementName(node.name)) return "VOID";
return "NORMAL";
}
if (import_utils.default.isSvgElementNode(node)) return "SVG";
if (import_utils.default.isMathElementNode(node)) return "MATH";
return "UNKNOWN";
}
/**
* Check whether the given element is empty or not.
* This ignores whitespaces, doesn't ignore comments.
*/
function isEmpty(node, sourceCode) {
const start = node.startTag.range[1];
const end = node.endTag == null ? node.range[1] : node.endTag.range[0];
return sourceCode.text.slice(start, end).trim() === "";
}
var html_self_closing_default = {
meta: {
type: "layout",
docs: {
description: "enforce self-closing style",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/html-self-closing.html"
},
fixable: "code",
schema: {
definitions: { optionValue: { enum: [
"always",
"never",
"any"
] } },
type: "array",
items: [{
type: "object",
properties: {
html: {
type: "object",
properties: {
normal: { $ref: "#/definitions/optionValue" },
void: { $ref: "#/definitions/optionValue" },
component: { $ref: "#/definitions/optionValue" }
},
additionalProperties: false
},
svg: { $ref: "#/definitions/optionValue" },
math: { $ref: "#/definitions/optionValue" }
},
additionalProperties: false
}],
maxItems: 1
},
messages: {
requireSelfClosing: "Require self-closing on {{elementType}} (<{{name}}>).",
disallowSelfClosing: "Disallow self-closing on {{elementType}} (<{{name}}/>)."
}
},
create(context) {
const sourceCode = context.sourceCode;
const options = parseOptions(context.options[0]);
let hasInvalidEOF = false;
return import_utils.default.defineTemplateBodyVisitor(context, { VElement(node) {
if (hasInvalidEOF || node.parent.type === "VDocumentFragment") return;
const elementType = getElementType(node);
const mode = options[elementType];
if (mode === "always" && !node.startTag.selfClosing && isEmpty(node, sourceCode)) context.report({
node: node.endTag || node,
messageId: "requireSelfClosing",
data: {
elementType: ELEMENT_TYPE_MESSAGES[elementType],
name: node.rawName
},
fix(fixer) {
const close = sourceCode.parserServices.getTemplateBodyTokenStore().getLastToken(node.startTag);
if (close.type !== "HTMLTagClose") return null;
return fixer.replaceTextRange([close.range[0], node.range[1]], "/>");
}
});
if (mode === "never" && node.startTag.selfClosing) context.report({
node,
loc: {
start: {
line: node.loc.end.line,
column: node.loc.end.column - 2
},
end: node.loc.end
},
messageId: "disallowSelfClosing",
data: {
elementType: ELEMENT_TYPE_MESSAGES[elementType],
name: node.rawName
},
fix(fixer) {
const close = sourceCode.parserServices.getTemplateBodyTokenStore().getLastToken(node.startTag);
if (close.type !== "HTMLSelfClosingTagClose") return null;
if (elementType === "VOID") return fixer.replaceText(close, ">");
const elementPart = sourceCode.text.slice(node.range[0], close.range[0]);
return fixer.replaceText(node, `${elementPart}></${node.rawName}>`);
}
});
} }, { Program(node) {
hasInvalidEOF = import_utils.default.hasInvalidEOF(node);
} });
}
};
//#endregion
exports.default = html_self_closing_default;
+65
View File
@@ -0,0 +1,65 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
//#region lib/rules/jsx-uses-vars.js
/**
The MIT License (MIT)
Copyright (c) 2014 Yannick Croissant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* @fileoverview Prevent variables used in JSX to be marked as unused
* @author Yannick Croissant
*/
var require_jsx_uses_vars = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
module.exports = {
meta: {
type: "problem",
docs: {
description: "prevent variables used in JSX to be marked as unused",
categories: ["base"],
url: "https://eslint.vuejs.org/rules/jsx-uses-vars.html"
},
schema: []
},
create(context) {
return { JSXOpeningElement(node) {
let name;
if (node.name.type === "JSXIdentifier") name = node.name.name;
else if (node.name.type === "JSXMemberExpression") {
let parent = node.name.object;
while (parent.type === "JSXMemberExpression") parent = parent.object;
name = parent.name;
} else return;
context.sourceCode.markVariableAsUsed(name, node);
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_jsx_uses_vars();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/key-spacing.js
/**
* @author Toru Nagashima
*/
var require_key_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("key-spacing", { skipDynamicArguments: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_key_spacing();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/keyword-spacing.js
/**
* @author Yosuke Ota
*/
var require_keyword_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("keyword-spacing", { skipDynamicArguments: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_keyword_spacing();
}
});
@@ -0,0 +1,99 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
let node_path = require("node:path");
node_path = require_runtime.__toESM(node_path);
//#region lib/rules/match-component-file-name.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
function canVerify(node) {
return node.type === "Literal" || node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1;
}
var match_component_file_name_default = {
meta: {
type: "suggestion",
docs: {
description: "require component name property to match its file name",
categories: void 0,
url: "https://eslint.vuejs.org/rules/match-component-file-name.html"
},
fixable: null,
hasSuggestions: true,
schema: [{
type: "object",
properties: {
extensions: {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
},
shouldMatchCase: { type: "boolean" }
},
additionalProperties: false
}],
messages: { shouldMatchFileName: "Component name `{{name}}` should match file name `{{filename}}`." }
},
create(context) {
const options = context.options[0];
const shouldMatchCase = options && options.shouldMatchCase || false;
const extensionsArray = options && options.extensions;
const allowedExtensions = Array.isArray(extensionsArray) ? extensionsArray : ["jsx"];
const extension = node_path.default.extname(context.filename);
const filename = node_path.default.basename(context.filename, extension);
const errors = [];
let componentCount = 0;
if (!allowedExtensions.includes(extension.replace(/^\./, ""))) return {};
function compareNames(name, filename) {
if (shouldMatchCase) return name === filename;
return require_casing.pascalCase(name) === filename || require_casing.kebabCase(name) === filename;
}
function verifyName(node) {
let name;
if (node.type === "TemplateLiteral") name = node.quasis[0].value.cooked;
else name = `${node.value}`;
if (!compareNames(name, filename)) errors.push({
node,
messageId: "shouldMatchFileName",
data: {
filename,
name
},
suggest: [{
desc: "Rename component to match file name.",
fix(fixer) {
const quote = node.type === "TemplateLiteral" ? "`" : node.raw[0];
return fixer.replaceText(node, `${quote}${filename}${quote}`);
}
}]
});
}
return import_utils.default.compositingVisitors(import_utils.default.executeOnCallVueComponent(context, (node) => {
if (node.arguments.length === 2) {
const argument = node.arguments[0];
if (canVerify(argument)) verifyName(argument);
}
}), import_utils.default.executeOnVue(context, (object) => {
const node = import_utils.default.findProperty(object, "name");
componentCount++;
if (!node) return;
if (!canVerify(node.value)) return;
verifyName(node.value);
}), import_utils.default.defineScriptSetupVisitor(context, { onDefineOptionsEnter(node) {
componentCount++;
if (node.arguments.length === 0) return;
const define = node.arguments[0];
if (define.type !== "ObjectExpression") return;
const nameNode = import_utils.default.findProperty(define, "name");
if (!nameNode) return;
if (!canVerify(nameNode.value)) return;
verifyName(nameNode.value);
} }), { "Program:exit"() {
if (componentCount > 1) return;
for (const error of errors) context.report(error);
} });
}
};
//#endregion
exports.default = match_component_file_name_default;
@@ -0,0 +1,44 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
//#region lib/rules/match-component-import-name.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
function getExpectedNames(identifier) {
return [require_casing.pascalCase(identifier.name), require_casing.kebabCase(identifier.name)];
}
var match_component_import_name_default = {
meta: {
type: "problem",
docs: {
description: "require the registered component name to match the imported component name",
categories: void 0,
url: "https://eslint.vuejs.org/rules/match-component-import-name.html"
},
fixable: null,
schema: [],
messages: { unexpected: "Component alias {{importedName}} should be one of: {{expectedName}}." }
},
create(context) {
return import_utils.default.executeOnVueComponent(context, (obj) => {
const components = import_utils.default.findProperty(obj, "components");
if (!components || !components.value || components.value.type !== "ObjectExpression") return;
for (const property of components.value.properties) {
if (property.type === "SpreadElement" || property.value.type !== "Identifier" || property.computed === true) continue;
const importedName = import_utils.default.getStaticPropertyName(property) || "";
const expectedNames = getExpectedNames(property.value);
if (!expectedNames.includes(importedName)) context.report({
node: property,
messageId: "unexpected",
data: {
importedName,
expectedName: expectedNames.join(", ")
}
});
}
});
}
};
//#endregion
exports.default = match_component_import_name_default;
@@ -0,0 +1,124 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/max-attributes-per-line.js
/**
* @fileoverview Define the number of attributes allows per line
* @author Filipa Lacerda
*/
var require_max_attributes_per_line = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @param {any} options
*/
function parseOptions(options) {
const defaults = {
singleline: 1,
multiline: 1
};
if (options) {
if (typeof options.singleline === "number") defaults.singleline = options.singleline;
else if (typeof options.singleline === "object" && typeof options.singleline.max === "number") defaults.singleline = options.singleline.max;
if (options.multiline) {
if (typeof options.multiline === "number") defaults.multiline = options.multiline;
else if (typeof options.multiline === "object" && typeof options.multiline.max === "number") defaults.multiline = options.multiline.max;
}
}
return defaults;
}
/**
* @param {(VDirective | VAttribute)[]} attributes
*/
function groupAttrsByLine(attributes) {
const propsPerLine = [[attributes[0]]];
for (let index = 1; index < attributes.length; index++) {
const previous = attributes[index - 1];
const current = attributes[index];
if (previous.loc.end.line === current.loc.start.line) propsPerLine.at(-1).push(current);
else propsPerLine.push([current]);
}
return propsPerLine;
}
module.exports = {
meta: {
type: "layout",
docs: {
description: "enforce the maximum number of attributes per line",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/max-attributes-per-line.html"
},
fixable: "whitespace",
schema: [{
type: "object",
properties: {
singleline: { oneOf: [{
type: "number",
minimum: 1
}, {
type: "object",
properties: { max: {
type: "number",
minimum: 1
} },
additionalProperties: false
}] },
multiline: { oneOf: [{
type: "number",
minimum: 1
}, {
type: "object",
properties: { max: {
type: "number",
minimum: 1
} },
additionalProperties: false
}] }
},
additionalProperties: false
}],
messages: { shouldBeOnNewLine: "'{{name}}' should be on a new line." }
},
create(context) {
const sourceCode = context.sourceCode;
const configuration = parseOptions(context.options[0]);
const multilineMaximum = configuration.multiline;
const singlelinemMaximum = configuration.singleline;
const template = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
return utils.defineTemplateBodyVisitor(context, { VStartTag(node) {
const numberOfAttributes = node.attributes.length;
if (!numberOfAttributes) return;
if (utils.isSingleLine(node) && numberOfAttributes > singlelinemMaximum) showErrors(node.attributes.slice(singlelinemMaximum));
if (!utils.isSingleLine(node)) {
for (const attrs of groupAttrsByLine(node.attributes)) if (attrs.length > multilineMaximum) showErrors(attrs.splice(multilineMaximum));
}
} });
/**
* @param {(VDirective | VAttribute)[]} attributes
*/
function showErrors(attributes) {
for (const [i, prop] of attributes.entries()) context.report({
node: prop,
loc: prop.loc,
messageId: "shouldBeOnNewLine",
data: { name: sourceCode.getText(prop.key) },
fix(fixer) {
if (i !== 0) return null;
/** @type {Range} */
const range = [template.getTokenBefore(prop, { filter: (token) => token.type !== "HTMLWhitespace" }).range[1], prop.range[0]];
return fixer.replaceTextRange(range, "\n");
}
});
}
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_max_attributes_per_line();
}
});
+318
View File
@@ -0,0 +1,318 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/max-len.js
/**
* @author Yosuke Ota
* @fileoverview Rule to check for max length on a line of Vue file.
*/
var require_max_len = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const OPTIONS_SCHEMA = {
type: "object",
properties: {
code: {
type: "integer",
minimum: 0
},
template: {
type: "integer",
minimum: 0
},
comments: {
type: "integer",
minimum: 0
},
tabWidth: {
type: "integer",
minimum: 0
},
ignorePattern: { type: "string" },
ignoreComments: { type: "boolean" },
ignoreTrailingComments: { type: "boolean" },
ignoreUrls: { type: "boolean" },
ignoreStrings: { type: "boolean" },
ignoreTemplateLiterals: { type: "boolean" },
ignoreRegExpLiterals: { type: "boolean" },
ignoreHTMLAttributeValues: { type: "boolean" },
ignoreHTMLTextContents: { type: "boolean" }
},
additionalProperties: false
};
const OPTIONS_OR_INTEGER_SCHEMA = { oneOf: [OPTIONS_SCHEMA, {
type: "integer",
minimum: 0
}] };
/**
* Computes the length of a line that may contain tabs. The width of each
* tab will be the number of spaces to the next tab stop.
* @param {string} line The line.
* @param {number} tabWidth The width of each tab stop in spaces.
* @returns {number} The computed line length.
* @private
*/
function computeLineLength(line, tabWidth) {
let extraCharacterCount = 0;
const re = /\t/gu;
let ret;
while (ret = re.exec(line)) {
const totalOffset = ret.index + extraCharacterCount;
const spaceCount = tabWidth - (tabWidth ? totalOffset % tabWidth : 0);
extraCharacterCount += spaceCount - 1;
}
return [...line].length + extraCharacterCount;
}
/**
* Tells if a given comment is trailing: it starts on the current line and
* extends to or past the end of the current line.
* @param {string} line The source line we want to check for a trailing comment on
* @param {number} lineNumber The one-indexed line number for line
* @param {Token | null} comment The comment to inspect
* @returns {comment is Token} If the comment is trailing on the given line
*/
function isTrailingComment(line, lineNumber, comment) {
return Boolean(comment && comment.loc.start.line === lineNumber && lineNumber <= comment.loc.end.line && (comment.loc.end.line > lineNumber || comment.loc.end.column === line.length));
}
/**
* Tells if a comment encompasses the entire line.
* @param {string} line The source line with a trailing comment
* @param {number} lineNumber The one-indexed line number this is on
* @param {Token | null} comment The comment to remove
* @returns {boolean} If the comment covers the entire line
*/
function isFullLineComment(line, lineNumber, comment) {
if (!comment) return false;
const start = comment.loc.start;
const end = comment.loc.end;
const isFirstTokenOnLine = !line.slice(0, comment.loc.start.column).trim();
return comment && (start.line < lineNumber || start.line === lineNumber && isFirstTokenOnLine) && (end.line > lineNumber || end.line === lineNumber && end.column === line.length);
}
/**
* Gets the line after the comment and any remaining trailing whitespace is
* stripped.
* @param {string} line The source line with a trailing comment
* @param {Token} comment The comment to remove
* @returns {string} Line without comment and trailing whitepace
*/
function stripTrailingComment(line, comment) {
return line.slice(0, comment.loc.start.column).replace(/\s+$/u, "");
}
/**
* Group AST nodes by line number, both start and end.
*
* @param {Token[]} nodes the AST nodes in question
* @returns { { [key: number]: Token[] } } the grouped nodes
* @private
*/
function groupByLineNumber(nodes) {
/** @type { { [key: number]: Token[] } } */
const grouped = {};
for (const node of nodes) for (let i = node.loc.start.line; i <= node.loc.end.line; ++i) {
if (!Array.isArray(grouped[i])) grouped[i] = [];
grouped[i].push(node);
}
return grouped;
}
module.exports = {
meta: {
type: "layout",
docs: {
description: "enforce a maximum line length in `.vue` files",
categories: void 0,
url: "https://eslint.vuejs.org/rules/max-len.html",
extensionSource: {
url: "https://eslint.org/docs/rules/max-len",
name: "ESLint core"
}
},
schema: [
OPTIONS_OR_INTEGER_SCHEMA,
OPTIONS_OR_INTEGER_SCHEMA,
OPTIONS_SCHEMA
],
messages: {
max: "This line has a length of {{lineLength}}. Maximum allowed is {{maxLength}}.",
maxComment: "This line has a comment length of {{lineLength}}. Maximum allowed is {{maxCommentLength}}."
}
},
create(context) {
const URL_REGEXP = /[^:/?#]:\/\/[^?#]/u;
const sourceCode = context.sourceCode;
/** @type {Token[]} */
const tokens = [];
/** @type {(HTMLComment | HTMLBogusComment | Comment)[]} */
const comments = [];
/** @type {VLiteral[]} */
const htmlAttributeValues = [];
const options = Object.assign({}, context.options.at(-1));
if (typeof context.options[0] === "number") options.code = context.options[0];
if (typeof context.options[1] === "number") options.tabWidth = context.options[1];
/** @type {number} */
const scriptMaxLength = typeof options.code === "number" ? options.code : 80;
/** @type {number} */
const tabWidth = typeof options.tabWidth === "number" ? options.tabWidth : 2;
/** @type {number} */
const templateMaxLength = typeof options.template === "number" ? options.template : scriptMaxLength;
const ignoreComments = !!options.ignoreComments;
const ignoreStrings = !!options.ignoreStrings;
const ignoreTemplateLiterals = !!options.ignoreTemplateLiterals;
const ignoreRegExpLiterals = !!options.ignoreRegExpLiterals;
const ignoreTrailingComments = !!options.ignoreTrailingComments || !!options.ignoreComments;
const ignoreUrls = !!options.ignoreUrls;
const ignoreHTMLAttributeValues = !!options.ignoreHTMLAttributeValues;
const ignoreHTMLTextContents = !!options.ignoreHTMLTextContents;
/** @type {number} */
const maxCommentLength = options.comments;
/** @type {RegExp} */
let ignorePattern = options.ignorePattern || null;
if (ignorePattern) ignorePattern = new RegExp(ignorePattern, "u");
/**
* Retrieves an array containing all strings (" or ') in the source code.
*
* @returns {Token[]} An array of string nodes.
*/
function getAllStrings() {
return tokens.filter((token) => token.type === "String" || token.type === "JSXText" && sourceCode.getNodeByRangeIndex(token.range[0] - 1).type === "JSXAttribute");
}
/**
* Retrieves an array containing all template literals in the source code.
*
* @returns {Token[]} An array of template literal nodes.
*/
function getAllTemplateLiterals() {
return tokens.filter((token) => token.type === "Template");
}
/**
* Retrieves an array containing all RegExp literals in the source code.
*
* @returns {Token[]} An array of RegExp literal nodes.
*/
function getAllRegExpLiterals() {
return tokens.filter((token) => token.type === "RegularExpression");
}
/**
* Retrieves an array containing all HTML texts in the source code.
*
* @returns {Token[]} An array of HTML text nodes.
*/
function getAllHTMLTextContents() {
return tokens.filter((token) => token.type === "HTMLText");
}
/**
* Check the program for max length
* @param {Program} node Node to examine
* @returns {void}
* @private
*/
function checkProgramForMaxLength(node) {
const programNode = node;
const templateBody = node.templateBody;
const scriptTokens = sourceCode.ast.tokens;
const scriptComments = sourceCode.getAllComments();
if (sourceCode.parserServices.getTemplateBodyTokenStore && templateBody) {
const templateTokens = sourceCode.parserServices.getTemplateBodyTokenStore().getTokens(templateBody, { includeComments: true });
if (templateBody.range[0] < programNode.range[0]) tokens.push(...templateTokens, ...scriptTokens);
else tokens.push(...scriptTokens, ...templateTokens);
} else tokens.push(...scriptTokens);
if (ignoreComments || maxCommentLength || ignoreTrailingComments) if (templateBody) if (templateBody.range[0] < programNode.range[0]) comments.push(...templateBody.comments, ...scriptComments);
else comments.push(...scriptComments, ...templateBody.comments);
else comments.push(...scriptComments);
/** @type {Range | undefined} */
let scriptLinesRange;
if (scriptTokens.length > 0) scriptLinesRange = scriptComments.length > 0 ? [Math.min(scriptTokens[0].loc.start.line, scriptComments[0].loc.start.line), Math.max(
/** @type {Token} */
scriptTokens.at(-1).loc.end.line,
/** @type {Comment} */
scriptComments.at(-1).loc.end.line
)] : [scriptTokens[0].loc.start.line, scriptTokens.at(-1).loc.end.line];
else if (scriptComments.length > 0) scriptLinesRange = [scriptComments[0].loc.start.line, scriptComments.at(-1).loc.end.line];
const templateLinesRange = templateBody && [templateBody.loc.start.line, templateBody.loc.end.line];
const lines = sourceCode.lines;
const stringsByLine = groupByLineNumber(getAllStrings());
const templateLiteralsByLine = groupByLineNumber(getAllTemplateLiterals());
const regExpLiteralsByLine = groupByLineNumber(getAllRegExpLiterals());
const htmlAttributeValuesByLine = groupByLineNumber(htmlAttributeValues);
const htmlTextContentsByLine = groupByLineNumber(getAllHTMLTextContents());
const commentsByLine = groupByLineNumber(comments);
for (const [i, line] of lines.entries()) {
const lineNumber = i + 1;
const inScript = scriptLinesRange && scriptLinesRange[0] <= lineNumber && lineNumber <= scriptLinesRange[1];
const inTemplate = templateLinesRange && templateLinesRange[0] <= lineNumber && lineNumber <= templateLinesRange[1];
if (!inScript && !inTemplate) continue;
const maxLength = Math.max(inScript ? scriptMaxLength : 0, inTemplate ? templateMaxLength : 0);
if (ignoreStrings && stringsByLine[lineNumber] || ignoreTemplateLiterals && templateLiteralsByLine[lineNumber] || ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber] || ignoreHTMLAttributeValues && htmlAttributeValuesByLine[lineNumber] || ignoreHTMLTextContents && htmlTextContentsByLine[lineNumber]) continue;
let lineIsComment = false;
let textToMeasure;
if (commentsByLine[lineNumber]) {
const commentList = [...commentsByLine[lineNumber]];
let comment = commentList.pop() || null;
if (isFullLineComment(line, lineNumber, comment)) {
lineIsComment = true;
textToMeasure = line;
} else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
textToMeasure = stripTrailingComment(line, comment);
comment = commentList.pop() || null;
while (isTrailingComment(textToMeasure, lineNumber, comment)) textToMeasure = stripTrailingComment(textToMeasure, comment);
} else textToMeasure = line;
} else textToMeasure = line;
if (ignorePattern && ignorePattern.test(textToMeasure) || ignoreUrls && URL_REGEXP.test(textToMeasure)) continue;
const lineLength = computeLineLength(textToMeasure, tabWidth);
const commentLengthApplies = lineIsComment && maxCommentLength;
if (lineIsComment && ignoreComments) continue;
if (commentLengthApplies) {
if (lineLength > maxCommentLength) context.report({
node,
loc: {
start: {
line: lineNumber,
column: maxCommentLength
},
end: {
line: lineNumber,
column: lineLength
}
},
messageId: "maxComment",
data: {
lineLength,
maxCommentLength
}
});
} else if (lineLength > maxLength) context.report({
node,
loc: {
start: {
line: lineNumber,
column: maxLength
},
end: {
line: lineNumber,
column: lineLength
}
},
messageId: "max",
data: {
lineLength,
maxLength
}
});
}
}
return utils.compositingVisitors(utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=false] > VLiteral"(node) {
htmlAttributeValues.push(node);
} }), { "Program:exit"(node) {
checkProgramForMaxLength(node);
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_max_len();
}
});
@@ -0,0 +1,97 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/max-lines-per-block.js
/**
* @author lsdsjy
* @fileoverview Rule for checking the maximum number of lines in Vue SFC blocks.
*/
var require_max_lines_per_block = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { SourceCode } = require("eslint");
const utils = require_index.default;
/**
* @param {string} text
*/
function isEmptyLine(text) {
return !text.trim();
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "enforce maximum number of lines in Vue SFC blocks",
categories: void 0,
url: "https://eslint.vuejs.org/rules/max-lines-per-block.html"
},
fixable: null,
schema: [{
type: "object",
properties: {
style: {
type: "integer",
minimum: 1
},
template: {
type: "integer",
minimum: 1
},
script: {
type: "integer",
minimum: 1
},
skipBlankLines: {
type: "boolean",
minimum: 0
}
},
additionalProperties: false
}],
messages: { tooManyLines: "Block has too many lines ({{lineCount}}). Maximum allowed is {{limit}}." }
},
create(context) {
const option = context.options[0] || {};
/**
* @type {Record<string, number>}
*/
const limits = {
template: option.template,
script: option.script,
style: option.style
};
const sourceCode = context.sourceCode;
const documentFragment = sourceCode.parserServices.getDocumentFragment && sourceCode.parserServices.getDocumentFragment();
function getTopLevelHTMLElements() {
if (documentFragment) return documentFragment.children.filter(utils.isVElement);
return [];
}
return { Program(node) {
if (utils.hasInvalidEOF(node)) return;
for (const block of getTopLevelHTMLElements()) if (limits[block.name]) {
let lineCount = block.loc.end.line - block.loc.start.line - 1;
if (option.skipBlankLines) {
const lines = SourceCode.splitLines(sourceCode.getText(block));
lineCount -= lines.filter(isEmptyLine).length;
}
if (lineCount > limits[block.name]) context.report({
node: block,
messageId: "tooManyLines",
data: {
limit: limits[block.name],
lineCount
}
});
}
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_max_lines_per_block();
}
});
+71
View File
@@ -0,0 +1,71 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/max-props.js
/**
* @author kevsommer Kevin Sommer
* See LICENSE file in root directory for full license.
*/
var require_max_props = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "enforce maximum number of props in Vue component",
categories: void 0,
url: "https://eslint.vuejs.org/rules/max-props.html"
},
fixable: null,
schema: [{
type: "object",
properties: { maxProps: {
type: "integer",
minimum: 1
} },
additionalProperties: false,
minProperties: 1
}],
messages: { tooManyProps: "Component has too many props ({{propCount}}). Maximum allowed is {{limit}}." }
},
create(context) {
/** @type {Record<string, number>} */
const option = context.options[0] || {};
/**
* @param {import('../utils').ComponentProp[]} props
* @param {CallExpression | Property} node
*/
function checkMaxNumberOfProps(props, node) {
const propCount = new Set(props.map((prop) => prop.propName)).size;
if (propCount > option.maxProps && props[0].node) context.report({
node,
messageId: "tooManyProps",
data: {
propCount,
limit: option.maxProps
}
});
}
return utils.compositingVisitors(utils.executeOnVue(context, (node) => {
const propsNode = node.properties.find(
/** @returns {p is Property} */
(p) => p.type === "Property" && utils.getStaticPropertyName(p) === "props"
);
if (!propsNode) return;
checkMaxNumberOfProps(utils.getComponentPropsFromOptions(node), propsNode);
}), utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node, props) {
checkMaxNumberOfProps(props, node);
} }));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_max_props();
}
});
@@ -0,0 +1,65 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
//#region lib/rules/max-template-depth.js
/**
* @author kevsommer Kevin Sommer
* See LICENSE file in root directory for full license.
*/
var require_max_template_depth = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
module.exports = {
meta: {
type: "problem",
docs: {
description: "enforce maximum depth of template",
categories: void 0,
url: "https://eslint.vuejs.org/rules/max-template-depth.html"
},
fixable: null,
schema: [{
type: "object",
properties: { maxDepth: {
type: "integer",
minimum: 1
} },
additionalProperties: false,
minProperties: 1
}],
messages: { templateTooDeep: "Element is nested too deeply (depth of {{depth}}, maximum allowed is {{limit}})." }
},
create(context) {
const option = context.options[0] || {};
/**
* @param {VElement} element
* @param {number} curDepth
*/
function checkMaxDepth(element, curDepth) {
if (curDepth > option.maxDepth) context.report({
node: element,
messageId: "templateTooDeep",
data: {
depth: curDepth,
limit: option.maxDepth
}
});
if (!element.children) return;
for (const child of element.children) if (child.type === "VElement") checkMaxDepth(child, curDepth + 1);
}
return { Program(program) {
const element = program.templateBody;
if (element == null) return;
if (element.type !== "VElement") return;
checkMaxDepth(element, 0);
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_max_template_depth();
}
});
@@ -0,0 +1,94 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
let node_path = require("node:path");
node_path = require_runtime.__toESM(node_path);
//#region lib/rules/multi-word-component-names.ts
/**
* @author Marton Csordas
* See LICENSE file in root directory for full license.
*/
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
var multi_word_component_names_default = {
meta: {
type: "suggestion",
docs: {
description: "require component names to be always multi-word",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/multi-word-component-names.html"
},
schema: [{
type: "object",
properties: { ignores: {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
} },
additionalProperties: false
}],
messages: { unexpected: "Component name \"{{value}}\" should always be multi-word." }
},
create(context) {
const ignores = new Set(["App", "app"]);
for (const ignore of context.options[0] && context.options[0].ignores || []) {
ignores.add(ignore);
if (require_casing.isPascalCase(ignore)) ignores.add(require_casing.kebabCase(ignore));
}
let hasVue = import_utils.default.isScriptSetup(context);
let hasName = false;
/**
* Returns true if the given component name is valid, otherwise false.
* */
function isValidComponentName(name) {
if (ignores.has(name) || import_utils.default.VUE3_BUILTIN_COMPONENT_NAMES.has(name)) return true;
return require_casing.kebabCase(name).split("-").length > 1;
}
function validateName(nameNode) {
if (nameNode.type !== "Literal") return;
const componentName = `${nameNode.value}`;
if (!isValidComponentName(componentName)) context.report({
node: nameNode,
messageId: "unexpected",
data: { value: componentName }
});
}
return import_utils.default.compositingVisitors(import_utils.default.executeOnCallVueComponent(context, (node) => {
hasVue = true;
if (node.arguments.length !== 2) return;
hasName = true;
validateName(node.arguments[0]);
}), import_utils.default.executeOnVue(context, (obj) => {
hasVue = true;
const node = import_utils.default.findProperty(obj, "name");
if (!node) return;
hasName = true;
validateName(node.value);
}), import_utils.default.defineScriptSetupVisitor(context, { onDefineOptionsEnter(node) {
if (node.arguments.length === 0) return;
const define = node.arguments[0];
if (define.type !== "ObjectExpression") return;
const nameNode = import_utils.default.findProperty(define, "name");
if (!nameNode) return;
hasName = true;
validateName(nameNode.value);
} }), { "Program:exit"(node) {
if (hasName) return;
if (!hasVue && node.body.length > 0) return;
const fileName = context.filename;
const componentName = node_path.default.basename(fileName, node_path.default.extname(fileName));
if (import_utils.default.isVueFile(fileName) && !isValidComponentName(componentName)) context.report({
messageId: "unexpected",
data: { value: componentName },
loc: {
line: 1,
column: 0
}
});
} });
}
};
//#endregion
exports.default = multi_word_component_names_default;
@@ -0,0 +1,140 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
const require_inline_non_void_elements = require('../utils/inline-non-void-elements.js');
//#region lib/rules/multiline-html-element-content-newline.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
function isMultilineElement(element) {
return element.loc.start.line < element.endTag.loc.start.line;
}
function parseOptions(options) {
return Object.assign({
ignores: [
"pre",
"textarea",
...require_inline_non_void_elements.default
],
ignoreWhenEmpty: true,
allowEmptyLines: false
}, options);
}
function getPhrase(lineBreaks) {
switch (lineBreaks) {
case 0: return "no";
default: return `${lineBreaks}`;
}
}
/**
* Check whether the given element is empty or not.
* This ignores whitespaces, doesn't ignore comments.
*/
function isEmpty(node, sourceCode) {
const start = node.startTag.range[1];
const end = node.endTag.range[0];
return sourceCode.text.slice(start, end).trim() === "";
}
var multiline_html_element_content_newline_default = {
meta: {
type: "layout",
docs: {
description: "require a line break before and after the contents of a multiline element",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/multiline-html-element-content-newline.html"
},
fixable: "whitespace",
schema: [{
type: "object",
properties: {
ignoreWhenEmpty: { type: "boolean" },
ignores: {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
},
allowEmptyLines: { type: "boolean" }
},
additionalProperties: false
}],
messages: {
unexpectedAfterClosingBracket: "Expected 1 line break after opening tag (`<{{name}}>`), but {{actual}} line breaks found.",
unexpectedBeforeOpeningBracket: "Expected 1 line break before closing tag (`</{{name}}>`), but {{actual}} line breaks found."
}
},
create(context) {
const options = parseOptions(context.options[0]);
const ignores = options.ignores;
const ignoreWhenEmpty = options.ignoreWhenEmpty;
const allowEmptyLines = options.allowEmptyLines;
const sourceCode = context.sourceCode;
const template = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
let inIgnoreElement = null;
function isIgnoredElement(node) {
return ignores.includes(node.name) || ignores.includes(require_casing.pascalCase(node.rawName)) || ignores.includes(require_casing.kebabCase(node.rawName));
}
function isInvalidLineBreaks(lineBreaks) {
return allowEmptyLines ? lineBreaks === 0 : lineBreaks !== 1;
}
return import_utils.default.defineTemplateBodyVisitor(context, {
VElement(node) {
if (inIgnoreElement) return;
if (isIgnoredElement(node)) {
inIgnoreElement = node;
return;
}
if (node.startTag.selfClosing || !node.endTag) return;
const element = node;
if (!isMultilineElement(element)) return;
const getTokenOption = {
includeComments: true,
filter: (token) => token.type !== "HTMLWhitespace"
};
if (ignoreWhenEmpty && element.children.length === 0 && template.getFirstTokensBetween(element.startTag, element.endTag, getTokenOption).length === 0) return;
const contentFirst = template.getTokenAfter(element.startTag, getTokenOption);
const contentLast = template.getTokenBefore(element.endTag, getTokenOption);
const beforeLineBreaks = contentFirst.loc.start.line - element.startTag.loc.end.line;
const afterLineBreaks = element.endTag.loc.start.line - contentLast.loc.end.line;
if (isInvalidLineBreaks(beforeLineBreaks)) context.report({
node: template.getLastToken(element.startTag),
loc: {
start: element.startTag.loc.end,
end: contentFirst.loc.start
},
messageId: "unexpectedAfterClosingBracket",
data: {
name: element.rawName,
actual: getPhrase(beforeLineBreaks)
},
fix(fixer) {
const range = [element.startTag.range[1], contentFirst.range[0]];
return fixer.replaceTextRange(range, "\n");
}
});
if (isEmpty(element, sourceCode)) return;
if (isInvalidLineBreaks(afterLineBreaks)) context.report({
node: template.getFirstToken(element.endTag),
loc: {
start: contentLast.loc.end,
end: element.endTag.loc.start
},
messageId: "unexpectedBeforeOpeningBracket",
data: {
name: element.name,
actual: getPhrase(afterLineBreaks)
},
fix(fixer) {
const range = [contentLast.range[1], element.endTag.range[0]];
return fixer.replaceTextRange(range, "\n");
}
});
},
"VElement:exit"(node) {
if (inIgnoreElement === node) inIgnoreElement = null;
}
});
}
};
//#endregion
exports.default = multiline_html_element_content_newline_default;
@@ -0,0 +1,25 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/multiline-ternary.js
/**
* @author dev1437
* See LICENSE file in root directory for full license.
*/
var require_multiline_ternary = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapStylisticOrCoreRule } = require_index.default;
module.exports = wrapStylisticOrCoreRule("multiline-ternary", {
skipDynamicArguments: true,
applyDocument: true
});
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_multiline_ternary();
}
});
@@ -0,0 +1,80 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/mustache-interpolation-spacing.js
/**
* @fileoverview enforce unified spacing in mustache interpolations.
* @author Armano
*/
var require_mustache_interpolation_spacing = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "layout",
docs: {
description: "enforce unified spacing in mustache interpolations",
categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"],
url: "https://eslint.vuejs.org/rules/mustache-interpolation-spacing.html"
},
fixable: "whitespace",
schema: [{ enum: ["always", "never"] }],
messages: {
expectedSpaceAfter: "Expected 1 space after '{{', but not found.",
expectedSpaceBefore: "Expected 1 space before '}}', but not found.",
unexpectedSpaceAfter: "Expected no space after '{{', but found.",
unexpectedSpaceBefore: "Expected no space before '}}', but found."
}
},
create(context) {
const options = context.options[0] || "always";
const sourceCode = context.sourceCode;
const template = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
return utils.defineTemplateBodyVisitor(context, { "VExpressionContainer[expression!=null]"(node) {
const openBrace = template.getFirstToken(node);
const closeBrace = template.getLastToken(node);
if (!openBrace || !closeBrace || openBrace.type !== "VExpressionStart" || closeBrace.type !== "VExpressionEnd") return;
const firstToken = template.getTokenAfter(openBrace, { includeComments: true });
const lastToken = template.getTokenBefore(closeBrace, { includeComments: true });
if (options === "always") {
if (openBrace.range[1] === firstToken.range[0]) context.report({
node: openBrace,
messageId: "expectedSpaceAfter",
fix: (fixer) => fixer.insertTextAfter(openBrace, " ")
});
if (closeBrace.range[0] === lastToken.range[1]) context.report({
node: closeBrace,
messageId: "expectedSpaceBefore",
fix: (fixer) => fixer.insertTextBefore(closeBrace, " ")
});
} else {
if (openBrace.range[1] !== firstToken.range[0]) context.report({
loc: {
start: openBrace.loc.start,
end: firstToken.loc.start
},
messageId: "unexpectedSpaceAfter",
fix: (fixer) => fixer.removeRange([openBrace.range[1], firstToken.range[0]])
});
if (closeBrace.range[0] !== lastToken.range[1]) context.report({
loc: {
start: lastToken.loc.end,
end: closeBrace.loc.end
},
messageId: "unexpectedSpaceBefore",
fix: (fixer) => fixer.removeRange([lastToken.range[1], closeBrace.range[0]])
});
}
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_mustache_interpolation_spacing();
}
});
@@ -0,0 +1,116 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/new-line-between-multi-line-property.js
/**
* @fileoverview Enforce new lines between multi-line properties in Vue components.
* @author IWANABETHATGUY
*/
var require_new_line_between_multi_line_property = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @param {Token} node
*/
function isComma(node) {
return node.type === "Punctuator" && node.value === ",";
}
/**
* Check whether the between given nodes has empty line.
* @param {SourceCode} sourceCode
* @param {ASTNode} pre
* @param {ASTNode} cur
*/
function* iterateBetweenTokens(sourceCode, pre, cur) {
yield sourceCode.getLastToken(pre);
yield* sourceCode.getTokensBetween(pre, cur, { includeComments: true });
yield sourceCode.getFirstToken(cur);
}
/**
* Check whether the between given nodes has empty line.
* @param {SourceCode} sourceCode
* @param {ASTNode} pre
* @param {ASTNode} cur
*/
function hasEmptyLine(sourceCode, pre, cur) {
/** @type {Token|null} */
let preToken = null;
for (const token of iterateBetweenTokens(sourceCode, pre, cur)) {
if (preToken && token.loc.start.line - preToken.loc.end.line >= 2) return true;
preToken = token;
}
return false;
}
module.exports = {
meta: {
type: "layout",
docs: {
description: "enforce new lines between multi-line properties in Vue components",
categories: void 0,
url: "https://eslint.vuejs.org/rules/new-line-between-multi-line-property.html"
},
fixable: "whitespace",
schema: [{
type: "object",
properties: { minLineOfMultilineProperty: {
type: "number",
minimum: 2
} },
additionalProperties: false
}],
messages: { missingEmptyLine: "Enforce new lines between multi-line properties in Vue components." }
},
create(context) {
let minLineOfMultilineProperty = 2;
if (context.options && context.options[0] && context.options[0].minLineOfMultilineProperty) minLineOfMultilineProperty = context.options[0].minLineOfMultilineProperty;
/** @type {CallExpression[]} */
const callStack = [];
const sourceCode = context.sourceCode;
return Object.assign(utils.defineVueVisitor(context, {
CallExpression(node) {
callStack.push(node);
},
"CallExpression:exit"() {
callStack.pop();
},
ObjectExpression(node) {
if (callStack.length > 0) return;
const properties = node.properties;
for (let i = 1; i < properties.length; i++) {
const cur = properties[i];
const pre = properties[i - 1];
if (pre.loc.end.line - pre.loc.start.line + 1 < minLineOfMultilineProperty) continue;
if (hasEmptyLine(sourceCode, pre, cur)) continue;
context.report({
node: pre,
loc: {
start: pre.loc.end,
end: cur.loc.start
},
messageId: "missingEmptyLine",
fix(fixer) {
/** @type {Token|null} */
let preToken = null;
for (const token of iterateBetweenTokens(sourceCode, pre, cur)) {
if (preToken && preToken.loc.end.line < token.loc.start.line) return fixer.insertTextAfter(preToken, "\n");
preToken = token;
}
const commaToken = sourceCode.getTokenAfter(pre, isComma);
return fixer.insertTextAfter(commaToken || pre, "\n\n");
}
});
}
}
}));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_new_line_between_multi_line_property();
}
});
+84
View File
@@ -0,0 +1,84 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/next-tick-style.js
/**
* @fileoverview enforce Promise or callback style in `nextTick`
* @author Flo Edelmann
* @copyright 2020 Flo Edelmann. All rights reserved.
* See LICENSE file in root directory for full license.
*/
var require_next_tick_style = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const { findVariable } = require("@eslint-community/eslint-utils");
/**
* @param {Identifier} identifier
* @param {RuleContext} context
* @returns {CallExpression|undefined}
*/
function getVueNextTickCallExpression(identifier, context) {
if (identifier.name === "$nextTick" && identifier.parent.type === "MemberExpression" && utils.isThis(identifier.parent.object, context) && identifier.parent.parent.type === "CallExpression" && identifier.parent.parent.callee === identifier.parent) return identifier.parent.parent;
if (identifier.name === "nextTick" && identifier.parent.type === "MemberExpression" && identifier.parent.object.type === "Identifier" && identifier.parent.object.name === "Vue" && identifier.parent.parent.type === "CallExpression" && identifier.parent.parent.callee === identifier.parent) return identifier.parent.parent;
if (identifier.parent.type === "CallExpression" && identifier.parent.callee === identifier) {
const variable = findVariable(utils.getScope(context, identifier), identifier);
if (variable != null && variable.defs.length === 1) {
const def = variable.defs[0];
if (def.type === "ImportBinding" && def.node.type === "ImportSpecifier" && def.node.imported.type === "Identifier" && def.node.imported.name === "nextTick" && def.node.parent.type === "ImportDeclaration" && def.node.parent.source.value === "vue") return identifier.parent;
}
}
}
/**
* @param {CallExpression} callExpression
* @returns {boolean}
*/
function isAwaitedPromise(callExpression) {
return callExpression.parent.type === "AwaitExpression" || callExpression.parent.type === "MemberExpression" && callExpression.parent.property.type === "Identifier" && callExpression.parent.property.name === "then";
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce Promise or callback style in `nextTick`",
categories: void 0,
url: "https://eslint.vuejs.org/rules/next-tick-style.html"
},
fixable: "code",
schema: [{ enum: ["promise", "callback"] }],
messages: {
usePromise: "Use the Promise returned by `nextTick` instead of passing a callback function.",
useCallback: "Pass a callback function to `nextTick` instead of using the returned Promise."
}
},
create(context) {
const preferredStyle = context.options[0] || "promise";
return utils.defineVueVisitor(context, { Identifier(node) {
const callExpression = getVueNextTickCallExpression(node, context);
if (!callExpression) return;
if (preferredStyle === "callback") {
if (callExpression.arguments.length !== 1 || isAwaitedPromise(callExpression)) context.report({
node,
messageId: "useCallback"
});
return;
}
if (callExpression.arguments.length > 0 || !isAwaitedPromise(callExpression)) context.report({
node,
messageId: "usePromise",
fix(fixer) {
return fixer.insertTextAfter(node, "().then");
}
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_next_tick_style();
}
});
@@ -0,0 +1,48 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-arrow-functions-in-watch.js
/**
* @author Sosuke Suzuki
*/
var require_no_arrow_functions_in_watch = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using arrow functions to define watcher",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-arrow-functions-in-watch.html"
},
fixable: null,
schema: [],
messages: { noArrowFunctionsInWatch: "You should not use an arrow function to define a watcher." }
},
create(context) {
return utils.executeOnVue(context, (obj) => {
const watchNode = utils.findProperty(obj, "watch");
if (watchNode == null) return;
const watchValue = watchNode.value;
if (watchValue.type !== "ObjectExpression") return;
for (const property of watchValue.properties) {
if (property.type !== "Property") continue;
for (const handler of utils.iterateWatchHandlerValues(property)) if (handler.type === "ArrowFunctionExpression") context.report({
node: handler,
messageId: "noArrowFunctionsInWatch"
});
}
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_arrow_functions_in_watch();
}
});
@@ -0,0 +1,241 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-async-in-computed-properties.js
/**
* @fileoverview Check if there are no asynchronous actions inside computed properties.
* @author Armano
*/
var require_no_async_in_computed_properties = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { ReferenceTracker } = require("@eslint-community/eslint-utils");
const utils = require_index.default;
/**
* @typedef {import('../utils').VueObjectData} VueObjectData
* @typedef {import('../utils').VueVisitor} VueVisitor
* @typedef {import('../utils').ComponentComputedProperty} ComponentComputedProperty
*/
const PROMISE_FUNCTIONS = new Set([
"then",
"catch",
"finally"
]);
const PROMISE_METHODS = new Set([
"all",
"allSettled",
"any",
"race",
"reject",
"resolve",
"try",
"withResolvers"
]);
const TIMED_FUNCTIONS = new Set([
"setTimeout",
"setInterval",
"setImmediate",
"requestAnimationFrame"
]);
/**
* @param {CallExpression} node
*/
function isTimedFunction(node) {
const callee = utils.skipChainExpression(node.callee);
return (callee.type === "Identifier" && TIMED_FUNCTIONS.has(callee.name) || callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "window" && TIMED_FUNCTIONS.has(utils.getStaticPropertyName(callee) || "")) && node.arguments.length > 0;
}
/**
* @param {*} node
* @returns {*}
*/
function skipWrapper(node) {
while (node && node.expression) node = node.expression;
return node;
}
/**
* Get the root object name from a member expression chain
* @param {MemberExpression} memberExpr
* @returns {string|null}
*/
function getRootObjectName(memberExpr) {
let current = skipWrapper(memberExpr.object);
while (current) switch (current.type) {
case "MemberExpression":
current = skipWrapper(current.object);
break;
case "CallExpression": {
const calleeExpr = skipWrapper(current.callee);
if (calleeExpr.type === "MemberExpression") current = skipWrapper(calleeExpr.object);
else if (calleeExpr.type === "Identifier") return calleeExpr.name;
else return null;
break;
}
case "Identifier": return current.name;
default: return null;
}
return null;
}
/**
* @param {string} name
* @param {*} callee
* @returns {boolean}
*/
function isPromiseMethod(name, callee) {
return PROMISE_FUNCTIONS.has(name) || callee.object.type === "Identifier" && callee.object.name === "Promise" && PROMISE_METHODS.has(name);
}
/**
* @param {CallExpression} node
* @param {Set<string>} ignoredObjectNames
*/
function isPromise(node, ignoredObjectNames) {
const callee = utils.skipChainExpression(node.callee);
if (callee.type === "MemberExpression") {
const name = utils.getStaticPropertyName(callee);
if (!name || !isPromiseMethod(name, callee)) return false;
const rootObjectName = getRootObjectName(callee);
if (rootObjectName && ignoredObjectNames.has(rootObjectName)) return false;
return true;
}
return false;
}
/**
* @param {CallExpression} node
* @param {RuleContext} context
*/
function isNextTick(node, context) {
const callee = utils.skipChainExpression(node.callee);
if (callee.type === "MemberExpression") {
const name = utils.getStaticPropertyName(callee);
return utils.isThis(callee.object, context) && name === "$nextTick" || callee.object.type === "Identifier" && callee.object.name === "Vue" && name === "nextTick";
}
return false;
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow asynchronous actions in computed properties",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-async-in-computed-properties.html"
},
fixable: null,
schema: [{
type: "object",
properties: { ignoredObjectNames: {
type: "array",
items: { type: "string" },
uniqueItems: true,
additionalItems: false
} },
additionalProperties: false
}],
messages: {
unexpectedInFunction: "Unexpected {{expressionName}} in computed function.",
unexpectedInProperty: "Unexpected {{expressionName}} in \"{{propertyName}}\" computed property."
}
},
create(context) {
const options = context.options[0] || {};
const ignoredObjectNames = new Set(options.ignoredObjectNames || []);
/** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
const computedPropertiesMap = /* @__PURE__ */ new Map();
/** @type {(FunctionExpression | ArrowFunctionExpression)[]} */
const computedFunctionNodes = [];
/**
* @typedef {object} ScopeStack
* @property {ScopeStack | null} upper
* @property {BlockStatement | Expression} body
*/
/** @type {ScopeStack | null} */
let scopeStack = null;
const expressionTypes = {
promise: "asynchronous action",
nextTick: "asynchronous action",
await: "await operator",
async: "async function declaration",
new: "Promise object",
timed: "timed function"
};
/**
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
* @param {VueObjectData|undefined} [info]
*/
function onFunctionEnter(node, info) {
if (node.async) verify(node, node.body, "async", info ? computedPropertiesMap.get(info.node) : null);
scopeStack = {
upper: scopeStack,
body: node.body
};
}
function onFunctionExit() {
scopeStack = scopeStack && scopeStack.upper;
}
/**
* @param {ESNode} node
* @param {BlockStatement | Expression} targetBody
* @param {keyof expressionTypes} type
* @param {ComponentComputedProperty[]|undefined|null} computedProperties
*/
function verify(node, targetBody, type, computedProperties) {
for (const cp of computedProperties || []) if (cp.value && node.loc.start.line >= cp.value.loc.start.line && node.loc.end.line <= cp.value.loc.end.line && targetBody === cp.value) {
context.report({
node,
messageId: "unexpectedInProperty",
data: {
expressionName: expressionTypes[type],
propertyName: cp.key || "unknown"
}
});
return;
}
for (const cf of computedFunctionNodes) if (node.loc.start.line >= cf.body.loc.start.line && node.loc.end.line <= cf.body.loc.end.line && targetBody === cf.body) {
context.report({
node,
messageId: "unexpectedInFunction",
data: { expressionName: expressionTypes[type] }
});
return;
}
}
const nodeVisitor = {
":function": onFunctionEnter,
":function:exit": onFunctionExit,
NewExpression(node, info) {
if (!scopeStack) return;
if (node.callee.type === "Identifier" && node.callee.name === "Promise") verify(node, scopeStack.body, "new", info ? computedPropertiesMap.get(info.node) : null);
},
CallExpression(node, info) {
if (!scopeStack) return;
if (isPromise(node, ignoredObjectNames)) verify(node, scopeStack.body, "promise", info ? computedPropertiesMap.get(info.node) : null);
else if (isTimedFunction(node)) verify(node, scopeStack.body, "timed", info ? computedPropertiesMap.get(info.node) : null);
else if (isNextTick(node, context)) verify(node, scopeStack.body, "nextTick", info ? computedPropertiesMap.get(info.node) : null);
},
AwaitExpression(node, info) {
if (!scopeStack) return;
verify(node, scopeStack.body, "await", info ? computedPropertiesMap.get(info.node) : null);
}
};
return utils.compositingVisitors({ Program(program) {
const tracker = new ReferenceTracker(utils.getScope(context, program));
for (const { node } of utils.iterateReferencesTraceMap(tracker, { computed: { [ReferenceTracker.CALL]: true } })) {
if (node.type !== "CallExpression") continue;
const getter = utils.getGetterBodyFromComputedFunction(node);
if (getter) computedFunctionNodes.push(getter);
}
} }, utils.isScriptSetup(context) ? utils.defineScriptSetupVisitor(context, nodeVisitor) : utils.defineVueVisitor(context, {
onVueObjectEnter(node) {
computedPropertiesMap.set(node, utils.getComputedProperties(node));
},
...nodeVisitor
}));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_async_in_computed_properties();
}
});
@@ -0,0 +1,200 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
const require_regexp = require('../utils/regexp.js');
//#region lib/rules/no-bare-strings-in-template.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const DEFAULT_ALLOWLIST = [
"(",
")",
",",
".",
"&",
"+",
"-",
"=",
"*",
"/",
"#",
"%",
"!",
"?",
":",
"[",
"]",
"{",
"}",
"<",
">",
"·",
"•",
"",
"",
"—",
"",
"|"
];
const DEFAULT_ATTRIBUTES = {
"/.+/": [
"title",
"aria-label",
"aria-placeholder",
"aria-roledescription",
"aria-valuetext"
],
input: ["placeholder"],
img: ["alt"]
};
const DEFAULT_DIRECTIVES = ["v-text"];
/**
* Parse attributes option
*/
function parseTargetAttrs(options) {
const result = {
names: {},
regexps: [],
cache: {}
};
for (const tagName of Object.keys(options)) {
const attrs = new Set(options[tagName]);
if (require_regexp.isRegExp(tagName)) result.regexps.push({
name: require_regexp.toRegExp(tagName),
attrs
});
else result.names[tagName] = attrs;
}
return result;
}
/**
* Get a string from given expression container node
*/
function getStringValue(value) {
const expression = value.expression;
if (!expression) return null;
if (expression.type !== "Literal") return null;
if (typeof expression.value === "string") return expression.value;
return null;
}
var no_bare_strings_in_template_default = {
meta: {
type: "suggestion",
docs: {
description: "disallow the use of bare strings in `<template>`",
categories: void 0,
url: "https://eslint.vuejs.org/rules/no-bare-strings-in-template.html"
},
schema: [{
type: "object",
properties: {
allowlist: {
type: "array",
items: { type: "string" },
uniqueItems: true
},
attributes: {
type: "object",
patternProperties: { "^(?:\\S+|/.*/[a-z]*)$": {
type: "array",
items: { type: "string" },
uniqueItems: true
} },
additionalProperties: false
},
directives: {
type: "array",
items: {
type: "string",
pattern: "^v-"
},
uniqueItems: true
}
},
additionalProperties: false
}],
messages: {
unexpected: "Unexpected non-translated string used.",
unexpectedInAttr: "Unexpected non-translated string used in `{{attr}}`."
}
},
create(context) {
const opts = context.options[0] || {};
const rawAllowlist = opts.allowlist || DEFAULT_ALLOWLIST;
const attributes = parseTargetAttrs(opts.attributes || DEFAULT_ATTRIBUTES);
const directives = opts.directives || DEFAULT_DIRECTIVES;
const stringAllowlist = [];
const regexAllowlist = [];
for (const item of rawAllowlist) if (require_regexp.isRegExp(item)) regexAllowlist.push(require_regexp.toRegExp(item));
else stringAllowlist.push(item);
const allowlistRe = stringAllowlist.length > 0 ? new RegExp(stringAllowlist.map((w) => require_regexp.escape(w)).sort((a, b) => b.length - a.length).join("|"), "gu") : null;
let elementStack = null;
/**
* Gets the bare string from given string
*/
function getBareString(str) {
let result = str.trim();
if (allowlistRe) result = result.replace(allowlistRe, "");
for (const regex of regexAllowlist) {
const flags = regex.flags.includes("g") ? regex.flags : `${regex.flags}g`;
const globalRegex = new RegExp(regex.source, flags);
result = result.replace(globalRegex, "");
}
return result.trim();
}
/**
* Get the attribute to be verified from the element name.
*/
function getTargetAttrs(tagName) {
if (attributes.cache[tagName]) return attributes.cache[tagName];
const result = [];
if (attributes.names[tagName]) result.push(...attributes.names[tagName]);
for (const { name, attrs } of attributes.regexps) {
name.lastIndex = 0;
if (name.test(tagName)) result.push(...attrs);
}
if (require_casing.isKebabCase(tagName)) result.push(...getTargetAttrs(require_casing.pascalCase(tagName)));
return attributes.cache[tagName] = new Set(result);
}
return import_utils.default.defineTemplateBodyVisitor(context, {
VText(node) {
if (getBareString(node.value)) context.report({
node,
messageId: "unexpected"
});
},
VElement(node) {
elementStack = {
upper: elementStack,
name: node.rawName,
attrs: getTargetAttrs(node.rawName)
};
},
"VElement:exit"() {
elementStack = elementStack && elementStack.upper;
},
VAttribute(node) {
if (!node.value || !elementStack) return;
if (node.directive === false) {
if (!elementStack.attrs.has(node.key.rawName)) return;
if (getBareString(node.value.value)) context.report({
node: node.value,
messageId: "unexpectedInAttr",
data: { attr: node.key.rawName }
});
} else {
const directive = `v-${node.key.name.name}`;
if (!directives.includes(directive)) return;
const str = getStringValue(node.value);
if (str && getBareString(str)) context.report({
node: node.value,
messageId: "unexpectedInAttr",
data: { attr: directive }
});
}
}
});
}
};
//#endregion
exports.default = no_bare_strings_in_template_default;
@@ -0,0 +1,115 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-boolean-default.js
/**
* @fileoverview Prevents boolean defaults from being set
* @author Hiroki Osame
*/
var require_no_boolean_default = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef {import('../utils').ComponentProp} ComponentProp
* @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
*/
/**
* @param {Expression|undefined} node
*/
function isBooleanIdentifier(node) {
return Boolean(node && node.type === "Identifier" && node.name === "Boolean");
}
/**
* Detects whether given prop node is a Boolean
* @param {ComponentObjectProp} prop
* @return {Boolean}
*/
function isBooleanProp(prop) {
const value = utils.skipTSAsExpression(prop.value);
return isBooleanIdentifier(value) || value.type === "ObjectExpression" && isBooleanIdentifier(utils.findProperty(value, "type")?.value);
}
/**
* @param {ObjectExpression} propDefValue
*/
function getDefaultNode(propDefValue) {
return utils.findProperty(propDefValue, "default");
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow boolean defaults",
categories: void 0,
url: "https://eslint.vuejs.org/rules/no-boolean-default.html"
},
fixable: null,
schema: [{ enum: ["default-false", "no-default"] }],
messages: {
noBooleanDefault: "Boolean prop should not set a default (Vue defaults it to false).",
defaultFalse: "Boolean prop should only be defaulted to false."
}
},
create(context) {
const booleanType = context.options[0] || "no-default";
/**
* @param {ComponentProp} prop
* @param {(propName: string) => Expression[]} otherDefaultProvider
*/
function processProp(prop, otherDefaultProvider) {
if (prop.type === "object") {
if (!isBooleanProp(prop)) return;
if (prop.value.type === "ObjectExpression") {
const defaultNode = getDefaultNode(prop.value);
if (defaultNode) verifyDefaultExpression(defaultNode.value);
}
if (prop.propName != null) for (const defaultNode of otherDefaultProvider(prop.propName)) verifyDefaultExpression(defaultNode);
} else if (prop.type === "type") {
if (prop.types.length !== 1 || prop.types[0] !== "Boolean") return;
for (const defaultNode of otherDefaultProvider(prop.propName)) verifyDefaultExpression(defaultNode);
}
}
/**
* @param {ComponentProp[]} props
* @param {(propName: string) => Expression[]} otherDefaultProvider
*/
function processProps(props, otherDefaultProvider) {
for (const prop of props) processProp(prop, otherDefaultProvider);
}
/**
* @param {Expression} defaultNode
*/
function verifyDefaultExpression(defaultNode) {
switch (booleanType) {
case "no-default":
context.report({
node: defaultNode,
messageId: "noBooleanDefault"
});
break;
case "default-false":
if (defaultNode.type !== "Literal" || defaultNode.value !== false) context.report({
node: defaultNode,
messageId: "defaultFalse"
});
break;
}
}
return utils.compositingVisitors(utils.executeOnVueComponent(context, (obj) => {
processProps(utils.getComponentPropsFromOptions(obj), () => []);
}), utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node, props) {
const defaultsByWithDefaults = utils.getWithDefaultsPropExpressions(node);
const defaultsByAssignmentPatterns = utils.getDefaultPropExpressionsForPropsDestructure(node);
processProps(props, (propName) => [defaultsByWithDefaults[propName], defaultsByAssignmentPatterns[propName]?.expression].filter(utils.isDef));
} }));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_boolean_default();
}
});
+116
View File
@@ -0,0 +1,116 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-child-content.js
/**
* @author Flo Edelmann
* See LICENSE file in root directory for full license.
*/
var require_no_child_content = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { defineTemplateBodyVisitor } = require_index.default;
/**
* @typedef {object} RuleOption
* @property {string[]} additionalDirectives
*/
/**
* @param {VNode | Token} node
* @returns {boolean}
*/
function isWhiteSpaceTextNode(node) {
return node.type === "VText" && node.value.trim() === "";
}
/**
* @param {Position} pos1
* @param {Position} pos2
* @returns {'less' | 'equal' | 'greater'}
*/
function comparePositions(pos1, pos2) {
if (pos1.line < pos2.line || pos1.line === pos2.line && pos1.column < pos2.column) return "less";
if (pos1.line > pos2.line || pos1.line === pos2.line && pos1.column > pos2.column) return "greater";
return "equal";
}
/**
* @param {(VNode | Token)[]} nodes
* @returns {SourceLocation | undefined}
*/
function getLocationRange(nodes) {
/** @type {Position | undefined} */
let start;
/** @type {Position | undefined} */
let end;
for (const node of nodes) {
if (!start || comparePositions(node.loc.start, start) === "less") start = node.loc.start;
if (!end || comparePositions(node.loc.end, end) === "greater") end = node.loc.end;
}
if (start === void 0 || end === void 0) return;
return {
start,
end
};
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow element's child contents which would be overwritten by a directive like `v-html` or `v-text`",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-child-content.html"
},
fixable: null,
hasSuggestions: true,
schema: [{
type: "object",
additionalProperties: false,
properties: { additionalDirectives: {
type: "array",
uniqueItems: true,
minItems: 1,
items: { type: "string" }
} },
required: ["additionalDirectives"]
}],
messages: {
disallowedChildContent: "Child content is disallowed because it will be overwritten by the v-{{ directiveName }} directive.",
removeChildContent: "Remove child content."
}
},
create(context) {
const directives = new Set(["html", "text"]);
/** @type {RuleOption | undefined} */
const option = context.options[0];
if (option !== void 0) for (const directive of option.additionalDirectives) directives.add(directive);
return defineTemplateBodyVisitor(context, { "VAttribute[directive=true]"(directiveNode) {
const directiveName = directiveNode.key.name.name;
const elementNode = directiveNode.parent.parent;
if (elementNode.endTag === null || !directives.has(directiveName)) return;
const elementComments = context.sourceCode.parserServices.getTemplateBodyTokenStore().getTokensBetween(elementNode.startTag, elementNode.endTag, {
includeComments: true,
filter: (token) => token.type === "HTMLComment"
});
const childNodes = [...elementNode.children, ...elementComments];
if (childNodes.some((childNode) => !isWhiteSpaceTextNode(childNode))) context.report({
node: elementNode,
loc: getLocationRange(childNodes),
messageId: "disallowedChildContent",
data: { directiveName },
suggest: [{
messageId: "removeChildContent",
*fix(fixer) {
for (const childNode of childNodes) yield fixer.remove(childNode);
}
}]
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_child_content();
}
});
@@ -0,0 +1,81 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-computed-properties-in-data.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_computed_properties_in_data = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef {import('../utils').VueObjectData} VueObjectData
*/
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow accessing computed properties in `data`",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-computed-properties-in-data.html"
},
fixable: null,
schema: [],
messages: { cannotBeUsed: "The computed property cannot be used in `data()` because it is before initialization." }
},
create(context) {
/** @type {Map<ObjectExpression, {data: FunctionExpression | ArrowFunctionExpression, computedNames:Set<string>}>} */
const contextMap = /* @__PURE__ */ new Map();
/**
* @typedef {object} ScopeStack
* @property {ScopeStack | null} upper
* @property {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
*/
/** @type {ScopeStack | null} */
let scopeStack = null;
return utils.compositingVisitors({
":function"(node) {
scopeStack = {
upper: scopeStack,
node
};
},
":function:exit"() {
scopeStack = scopeStack && scopeStack.upper;
}
}, utils.defineVueVisitor(context, {
onVueObjectEnter(node) {
const dataProperty = utils.findProperty(node, "data");
if (!dataProperty || dataProperty.value.type !== "FunctionExpression" && dataProperty.value.type !== "ArrowFunctionExpression") return;
const computedNames = /* @__PURE__ */ new Set();
for (const computed of utils.iterateProperties(node, new Set(["computed"]))) computedNames.add(computed.name);
contextMap.set(node, {
data: dataProperty.value,
computedNames
});
},
MemberExpression(node, vueData) {
if (!scopeStack || !utils.isThis(node.object, context)) return;
const ctx = contextMap.get(vueData.node);
if (!ctx || ctx.data !== scopeStack.node) return;
const name = utils.getStaticPropertyName(node);
if (!name || !ctx.computedNames.has(name)) return;
context.report({
node,
messageId: "cannotBeUsed"
});
}
}));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_computed_properties_in_data();
}
});
+45
View File
@@ -0,0 +1,45 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-console.js
/**
* @author ItMaga <https://github.com/ItMaga>
* See LICENSE file in root directory for full license.
*/
var require_no_console = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = utils.wrapCoreRule("no-console", {
skipBaseHandlers: true,
create(context) {
const allowed = (context.options[0] || {}).allow || [];
/**
* Copied from the core rule `no-console`.
* Checks whether the property name of the given MemberExpression node
* is allowed by options or not.
* @param {MemberExpression} node The MemberExpression node to check.
* @returns {boolean} `true` if the property name of the node is allowed.
*/
function isAllowed(node) {
const propertyName = utils.getStaticPropertyName(node);
return propertyName && allowed.includes(propertyName);
}
return { MemberExpression(node) {
if (node.object.type === "Identifier" && node.object.name === "console" && !isAllowed(node)) context.report({
node: node.object,
loc: node.object.loc,
messageId: "unexpected"
});
} };
}
});
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_console();
}
});
@@ -0,0 +1,30 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-constant-condition.js
/**
* @author Flo Edelmann
*/
var require_no_constant_condition = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapCoreRule } = require_index.default;
const conditionalDirectiveNames = new Set([
"v-show",
"v-if",
"v-else-if"
]);
module.exports = wrapCoreRule("no-constant-condition", { create(_context, { baseHandlers }) {
return { VDirectiveKey(node) {
if (conditionalDirectiveNames.has(`v-${node.name.name}`) && node.parent.value && node.parent.value.expression && baseHandlers.IfStatement) baseHandlers.IfStatement({ test: node.parent.value.expression });
} };
} });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_constant_condition();
}
});
@@ -0,0 +1,52 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-custom-modifiers-on-v-model.js
/**
* @author Przemyslaw Falowski (@przemkow)
* @fileoverview This rule checks whether v-model used on the component do not have custom modifiers
*/
var require_no_custom_modifiers_on_v_model = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const VALID_MODIFIERS = new Set([
"lazy",
"number",
"trim"
]);
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow custom modifiers on v-model used on the component",
categories: ["vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-custom-modifiers-on-v-model.html"
},
fixable: null,
schema: [],
messages: { notSupportedModifier: "'v-model' directives don't support the modifier '{{name}}'." }
},
create(context) {
return utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name.name='model']"(node) {
const element = node.parent.parent;
if (utils.isCustomComponent(element)) {
for (const modifier of node.key.modifiers) if (!VALID_MODIFIERS.has(modifier.name)) context.report({
node,
loc: node.loc,
messageId: "notSupportedModifier",
data: { name: modifier.name }
});
}
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_custom_modifiers_on_v_model();
}
});
@@ -0,0 +1,75 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-data-object-declaration.js
/**
* @fileoverview disallow using deprecated object declaration on data
* @author yoyo930021
*/
var require_no_deprecated_data_object_declaration = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/** @param {Token} token */
function isOpenParen(token) {
return token.type === "Punctuator" && token.value === "(";
}
/** @param {Token} token */
function isCloseParen(token) {
return token.type === "Punctuator" && token.value === ")";
}
/**
* @param {Expression} node
* @param {SourceCode} sourceCode
*/
function getFirstAndLastTokens(node, sourceCode) {
let first = sourceCode.getFirstToken(node);
let last = sourceCode.getLastToken(node);
while (true) {
const prev = sourceCode.getTokenBefore(first);
const next = sourceCode.getTokenAfter(last);
if (isOpenParen(prev) && isCloseParen(next)) {
first = prev;
last = next;
} else return {
first,
last
};
}
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated object declaration on data (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-data-object-declaration.html"
},
fixable: "code",
schema: [],
messages: { objectDeclarationIsDeprecated: "Object declaration on 'data' property is deprecated. Using function declaration instead." }
},
create(context) {
const sourceCode = context.sourceCode;
return utils.executeOnVue(context, (obj) => {
const invalidData = utils.findProperty(obj, "data", (p) => p.value.type !== "FunctionExpression" && p.value.type !== "ArrowFunctionExpression" && p.value.type !== "Identifier");
if (invalidData) context.report({
node: invalidData,
messageId: "objectDeclarationIsDeprecated",
fix(fixer) {
const tokens = getFirstAndLastTokens(invalidData.value, sourceCode);
return [fixer.insertTextBefore(tokens.first, "function() {\nreturn "), fixer.insertTextAfter(tokens.last, ";\n}")];
}
});
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_data_object_declaration();
}
});
@@ -0,0 +1,84 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-delete-set.js
/**
* @author Wayne Zhang
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_delete_set = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const { ReferenceTracker } = require("@eslint-community/eslint-utils");
/**
* @typedef {import('@eslint-community/eslint-utils').TYPES.TraceMap} TraceMap
*/
/** @type {TraceMap} */
const deletedImportApisMap = {
set: { [ReferenceTracker.CALL]: true },
del: { [ReferenceTracker.CALL]: true }
};
const deprecatedApis = new Set(["set", "delete"]);
const deprecatedDollarApis = new Set(["$set", "$delete"]);
/**
* @param {Expression|Super} node
*/
function isVue(node) {
return node.type === "Identifier" && node.name === "Vue";
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `$delete` and `$set` (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-delete-set.html"
},
fixable: null,
schema: [],
messages: { deprecated: "The `$delete`, `$set` is deprecated." }
},
create(context) {
/**
* @param {Identifier} identifier
* @param {RuleContext} context
* @returns {CallExpression|undefined}
*/
function getVueDeprecatedCallExpression(identifier, context) {
if (deprecatedDollarApis.has(identifier.name) && identifier.parent.type === "MemberExpression" && utils.isThis(identifier.parent.object, context) && identifier.parent.parent.type === "CallExpression" && identifier.parent.parent.callee === identifier.parent) return identifier.parent.parent;
if (deprecatedApis.has(identifier.name) && identifier.parent.type === "MemberExpression" && isVue(identifier.parent.object) && identifier.parent.parent.type === "CallExpression" && identifier.parent.parent.callee === identifier.parent) return identifier.parent.parent;
}
const nodeVisitor = { Identifier(node) {
if (!getVueDeprecatedCallExpression(node, context)) return;
context.report({
node,
messageId: "deprecated"
});
} };
return utils.compositingVisitors(utils.defineVueVisitor(context, nodeVisitor), utils.defineScriptSetupVisitor(context, nodeVisitor), { Program(node) {
const tracker = new ReferenceTracker(utils.getScope(context, node));
const esmTraceMap = { vue: {
[ReferenceTracker.ESM]: true,
...deletedImportApisMap
} };
const cjsTraceMap = { vue: { ...deletedImportApisMap } };
for (const { node } of [...tracker.iterateEsmReferences(esmTraceMap), ...tracker.iterateCjsReferences(cjsTraceMap)]) {
const refNode = node;
context.report({
node: refNode.callee,
messageId: "deprecated"
});
}
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_delete_set();
}
});
@@ -0,0 +1,70 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-destroyed-lifecycle.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_destroyed_lifecycle = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @param {RuleFixer} fixer
* @param {Property} property
* @param {string} newName
*/
function fix(fixer, property, newName) {
if (property.computed) {
if (property.key.type === "Literal" || property.key.type === "TemplateLiteral") return fixer.replaceTextRange([property.key.range[0] + 1, property.key.range[1] - 1], newName);
return null;
}
if (property.shorthand) return fixer.insertTextBefore(property.key, `${newName}:`);
return fixer.replaceText(property.key, newName);
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-destroyed-lifecycle.html"
},
fixable: "code",
schema: [],
messages: {
deprecatedDestroyed: "The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.",
deprecatedBeforeDestroy: "The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead."
}
},
create(context) {
return utils.executeOnVue(context, (obj) => {
const destroyed = utils.findProperty(obj, "destroyed");
if (destroyed) context.report({
node: destroyed.key,
messageId: "deprecatedDestroyed",
fix(fixer) {
return fix(fixer, destroyed, "unmounted");
}
});
const beforeDestroy = utils.findProperty(obj, "beforeDestroy");
if (beforeDestroy) context.report({
node: beforeDestroy.key,
messageId: "deprecatedBeforeDestroy",
fix(fixer) {
return fix(fixer, beforeDestroy, "beforeUnmount");
}
});
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_destroyed_lifecycle();
}
});
@@ -0,0 +1,52 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-dollar-listeners-api.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_dollar_listeners_api = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `$listeners` (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-dollar-listeners-api.html"
},
fixable: null,
schema: [],
messages: { deprecated: "The `$listeners` is deprecated." }
},
create(context) {
return utils.defineTemplateBodyVisitor(context, { VExpressionContainer(node) {
for (const reference of node.references) {
if (reference.variable != null) continue;
if (reference.id.name === "$listeners") context.report({
node: reference.id,
messageId: "deprecated"
});
}
} }, utils.defineVueVisitor(context, { MemberExpression(node) {
if (node.property.type !== "Identifier" || node.property.name !== "$listeners") return;
if (!utils.isThis(node.object, context)) return;
context.report({
node: node.property,
messageId: "deprecated"
});
} }));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_dollar_listeners_api();
}
});
@@ -0,0 +1,58 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-dollar-scopedslots-api.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_dollar_scopedslots_api = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `$scopedSlots` (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-dollar-scopedslots-api.html"
},
fixable: "code",
schema: [],
messages: { deprecated: "The `$scopedSlots` is deprecated." }
},
create(context) {
return utils.defineTemplateBodyVisitor(context, { VExpressionContainer(node) {
for (const reference of node.references) {
if (reference.variable != null) continue;
if (reference.id.name === "$scopedSlots") context.report({
node: reference.id,
messageId: "deprecated",
fix(fixer) {
return fixer.replaceText(reference.id, "$slots");
}
});
}
} }, utils.defineVueVisitor(context, { MemberExpression(node) {
if (node.property.type !== "Identifier" || node.property.name !== "$scopedSlots") return;
if (!utils.isThis(node.object, context)) return;
context.report({
node: node.property,
messageId: "deprecated",
fix(fixer) {
return fixer.replaceText(node.property, "$slots");
}
});
} }));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_dollar_scopedslots_api();
}
});
@@ -0,0 +1,50 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-events-api.js
/**
* @fileoverview disallow using deprecated events api
* @author yoyo930021
*/
var require_no_deprecated_events_api = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated events api (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-events-api.html"
},
fixable: null,
schema: [],
messages: { noDeprecatedEventsApi: "The Events api `$on`, `$off` `$once` is deprecated. Using external library instead, for example mitt." }
},
create(context) {
return utils.defineVueVisitor(context, { "CallExpression > MemberExpression, CallExpression > ChainExpression > MemberExpression"(node) {
const call = node.parent.type === "ChainExpression" ? node.parent.parent : node.parent;
if (call.optional) return;
if (utils.skipChainExpression(call.callee) !== node || ![
"$on",
"$off",
"$once"
].includes(utils.getStaticPropertyName(node) || "")) return;
if (!utils.isThis(node.object, context)) return;
context.report({
node: node.property,
messageId: "noDeprecatedEventsApi"
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_events_api();
}
});
@@ -0,0 +1,43 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-filter.js
/**
* @author Przemyslaw Falowski (@przemkow)
* @fileoverview disallow using deprecated filters syntax
*/
var require_no_deprecated_filter = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated filters syntax (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-filter.html"
},
fixable: null,
schema: [],
messages: { noDeprecatedFilter: "Filters are deprecated." }
},
create(context) {
return utils.defineTemplateBodyVisitor(context, { VFilterSequenceExpression(node) {
context.report({
node,
loc: node.loc,
messageId: "noDeprecatedFilter"
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_filter();
}
});
@@ -0,0 +1,45 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-functional-template.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_functional_template = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated the `functional` template (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-functional-template.html"
},
fixable: null,
schema: [],
messages: { unexpected: "The `functional` template are deprecated." }
},
create(context) {
return { Program(program) {
const element = program.templateBody;
if (element == null) return;
const functional = utils.getAttribute(element, "functional");
if (functional) context.report({
node: functional,
messageId: "unexpected"
});
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_functional_template();
}
});
@@ -0,0 +1,59 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-html-element-is.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_html_element_is = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated the `is` attribute on HTML elements (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-html-element-is.html"
},
fixable: null,
schema: [],
messages: { unexpected: "The `is` attribute on HTML element are deprecated." }
},
create(context) {
/** @param {VElement} node */
function isValidElement(node) {
return !utils.isHtmlWellKnownElementName(node.rawName) && !utils.isSvgWellKnownElementName(node.rawName) && !utils.isMathWellKnownElementName(node.rawName);
}
return utils.defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name.name='bind'][key.argument.name='is']"(node) {
if (isValidElement(node.parent.parent)) return;
context.report({
node,
loc: node.loc,
messageId: "unexpected"
});
},
"VAttribute[directive=false][key.name='is']"(node) {
if (isValidElement(node.parent.parent)) return;
if (node.value && node.value.value.startsWith("vue:")) return;
context.report({
node,
loc: node.loc,
messageId: "unexpected"
});
}
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_html_element_is();
}
});
@@ -0,0 +1,43 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-inline-template.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_inline_template = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `inline-template` attribute (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-inline-template.html"
},
fixable: null,
schema: [],
messages: { unexpected: "`inline-template` are deprecated." }
},
create(context) {
return utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=false] > VIdentifier[rawName='inline-template']"(node) {
context.report({
node,
loc: node.loc,
messageId: "unexpected"
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_inline_template();
}
});
@@ -0,0 +1,101 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-model-definition.js
/**
* @author Flo Edelmann
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_model_definition = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const allowedPropNames = new Set(["modelValue", "model-value"]);
const allowedEventNames = new Set(["update:modelValue", "update:model-value"]);
/**
* @param {ObjectExpression} node
* @param {string} key
* @returns {Literal | TemplateLiteral | undefined}
*/
function findPropertyValue(node, key) {
const property = node.properties.find((property) => property.type === "Property" && property.key.type === "Identifier" && property.key.name === key);
if (!property || property.type !== "Property" || !utils.isStringLiteral(property.value)) return;
return property.value;
}
/**
* @param {RuleFixer} fixer
* @param {Literal | TemplateLiteral} node
* @param {string} text
*/
function replaceLiteral(fixer, node, text) {
return fixer.replaceTextRange([node.range[0] + 1, node.range[1] - 1], text);
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow deprecated `model` definition (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-model-definition.html"
},
fixable: null,
hasSuggestions: true,
schema: [{
type: "object",
additionalProperties: false,
properties: { allowVue3Compat: { type: "boolean" } }
}],
messages: {
deprecatedModel: "`model` definition is deprecated.",
vue3Compat: "`model` definition is deprecated. You may use the Vue 3-compatible `modelValue`/`update:modelValue` though.",
changeToModelValue: "Change to `modelValue`/`update:modelValue`.",
changeToKebabModelValue: "Change to `model-value`/`update:model-value`."
}
},
create(context) {
const allowVue3Compat = Boolean(context.options[0]?.allowVue3Compat);
return utils.executeOnVue(context, (obj) => {
const modelProperty = utils.findProperty(obj, "model");
if (!modelProperty || modelProperty.value.type !== "ObjectExpression") return;
if (!allowVue3Compat) {
context.report({
node: modelProperty,
messageId: "deprecatedModel"
});
return;
}
const propName = findPropertyValue(modelProperty.value, "prop");
const eventName = findPropertyValue(modelProperty.value, "event");
if (!propName || !eventName || !allowedPropNames.has(utils.getStringLiteralValue(propName, true) ?? "") || !allowedEventNames.has(utils.getStringLiteralValue(eventName, true) ?? "")) context.report({
node: modelProperty,
messageId: "vue3Compat",
suggest: propName && eventName ? [{
messageId: "changeToModelValue",
*fix(fixer) {
const newPropName = "modelValue";
const newEventName = "update:modelValue";
yield replaceLiteral(fixer, propName, newPropName);
yield replaceLiteral(fixer, eventName, newEventName);
}
}, {
messageId: "changeToKebabModelValue",
*fix(fixer) {
const newPropName = "model-value";
const newEventName = "update:model-value";
yield replaceLiteral(fixer, propName, newPropName);
yield replaceLiteral(fixer, eventName, newEventName);
}
}] : []
});
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_model_definition();
}
});
@@ -0,0 +1,105 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-props-default-this.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_props_default_this = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @param {Expression|SpreadElement|null} node
*/
function isFunctionIdentifier(node) {
return node && node.type === "Identifier" && node.name === "Function";
}
/**
* @param {Expression} node
* @returns {boolean}
*/
function hasFunctionType(node) {
if (isFunctionIdentifier(node)) return true;
if (node.type === "ArrayExpression") return node.elements.some(isFunctionIdentifier);
return false;
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow deprecated `this` access in props default function (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-props-default-this.html"
},
fixable: null,
schema: [],
messages: { deprecated: "Props default value factory functions no longer have access to `this`." }
},
create(context) {
/**
* @typedef {object} ScopeStack
* @property {ScopeStack | null} upper
* @property {FunctionExpression | FunctionDeclaration} node
* @property {boolean} propDefault
*/
/** @type {Set<FunctionExpression>} */
const propsDefault = /* @__PURE__ */ new Set();
/** @type {ScopeStack | null} */
let scopeStack = null;
/**
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
*/
function onFunctionEnter(node) {
if (node.type === "ArrowFunctionExpression") return;
if (scopeStack) scopeStack = {
upper: scopeStack,
node,
propDefault: false
};
else if (node.type === "FunctionExpression" && propsDefault.has(node)) scopeStack = {
upper: scopeStack,
node,
propDefault: true
};
}
/**
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
*/
function onFunctionExit(node) {
if (scopeStack && scopeStack.node === node) scopeStack = scopeStack.upper;
}
return utils.defineVueVisitor(context, {
onVueObjectEnter(node) {
for (const prop of utils.getComponentPropsFromOptions(node)) {
if (prop.type !== "object") continue;
if (prop.value.type !== "ObjectExpression") continue;
const def = utils.findProperty(prop.value, "default");
if (!def) continue;
const type = utils.findProperty(prop.value, "type");
if (type && hasFunctionType(type.value)) continue;
if (def.value.type !== "FunctionExpression") continue;
propsDefault.add(def.value);
}
},
":function": onFunctionEnter,
":function:exit": onFunctionExit,
ThisExpression(node) {
if (scopeStack && scopeStack.propDefault) context.report({
node,
messageId: "deprecated"
});
}
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_props_default_this();
}
});
@@ -0,0 +1,57 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_casing = require('../utils/casing.js');
//#region lib/rules/no-deprecated-router-link-tag-prop.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
function getComponentNames(context) {
let components = ["RouterLink"];
if (context.options[0] && context.options[0].components) components = context.options[0].components;
return new Set(components.flatMap((component) => [require_casing.kebabCase(component), require_casing.pascalCase(component)]));
}
var no_deprecated_router_link_tag_prop_default = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-router-link-tag-prop.html"
},
fixable: null,
schema: [{
type: "object",
properties: { components: {
type: "array",
items: { type: "string" },
uniqueItems: true,
minItems: 1
} },
additionalProperties: false
}],
messages: { deprecated: "'tag' property on '{{element}}' component is deprecated. Use scoped slots instead." }
},
create(context) {
const components = getComponentNames(context);
return import_utils.default.defineTemplateBodyVisitor(context, { VElement(node) {
if (!components.has(node.rawName)) return;
let tagKey = null;
const tagAttr = import_utils.default.getAttribute(node, "tag");
if (tagAttr) tagKey = tagAttr.key;
else {
const directive = import_utils.default.getDirective(node, "bind", "tag");
if (directive) {
const arg = directive.key.argument;
if (arg && arg.type === "VIdentifier") tagKey = arg;
}
}
if (tagKey) context.report({
node: tagKey,
messageId: "deprecated",
data: { element: node.rawName }
});
} });
}
};
//#endregion
exports.default = no_deprecated_router_link_tag_prop_default;
@@ -0,0 +1,40 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_scope_attribute$1 = require('./syntaxes/scope-attribute.js');
//#region lib/rules/no-deprecated-scope-attribute.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_scope_attribute = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const scopeAttribute = require_scope_attribute$1.default;
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow deprecated `scope` attribute (in Vue.js 2.5.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-scope-attribute.html"
},
fixable: "code",
schema: [],
messages: { forbiddenScopeAttribute: "`scope` attributes are deprecated." }
},
create(context) {
const templateBodyVisitor = scopeAttribute.createTemplateBodyVisitor(context);
return utils.defineTemplateBodyVisitor(context, templateBodyVisitor);
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_scope_attribute();
}
});
@@ -0,0 +1,41 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_slot_attribute = require('./syntaxes/slot-attribute.js');
//#region lib/rules/no-deprecated-slot-attribute.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
var no_deprecated_slot_attribute_default = {
meta: {
type: "suggestion",
docs: {
description: "disallow deprecated `slot` attribute (in Vue.js 2.6.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-slot-attribute.html"
},
fixable: "code",
schema: [{
type: "object",
properties: {
ignore: {
type: "array",
items: { type: "string" },
uniqueItems: true
},
ignoreParents: {
type: "array",
items: { type: "string" },
uniqueItems: true
}
},
additionalProperties: false
}],
messages: { forbiddenSlotAttribute: "`slot` attributes are deprecated." }
},
create(context) {
const templateBodyVisitor = require_slot_attribute.default.createTemplateBodyVisitor(context);
return import_utils.default.defineTemplateBodyVisitor(context, templateBodyVisitor);
}
};
//#endregion
exports.default = no_deprecated_slot_attribute_default;
@@ -0,0 +1,40 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_slot_scope_attribute$1 = require('./syntaxes/slot-scope-attribute.js');
//#region lib/rules/no-deprecated-slot-scope-attribute.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_slot_scope_attribute = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const slotScopeAttribute = require_slot_scope_attribute$1.default;
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-slot-scope-attribute.html"
},
fixable: "code",
schema: [],
messages: { forbiddenSlotScopeAttribute: "`slot-scope` are deprecated." }
},
create(context) {
const templateBodyVisitor = slotScopeAttribute.createTemplateBodyVisitor(context, { fixToUpgrade: true });
return utils.defineTemplateBodyVisitor(context, templateBodyVisitor);
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_slot_scope_attribute();
}
});
@@ -0,0 +1,49 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-v-bind-sync.js
/**
* @author Przemyslaw Falowski (@przemkow)
* @fileoverview Disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
*/
var require_no_deprecated_v_bind_sync = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-v-bind-sync.html"
},
fixable: "code",
schema: [],
messages: { syncModifierIsDeprecated: "'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead." }
},
create(context) {
return utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name.name='bind']"(node) {
if (node.key.modifiers.map((mod) => mod.name).includes("sync")) context.report({
node,
loc: node.loc,
messageId: "syncModifierIsDeprecated",
fix(fixer) {
if (node.key.argument == null) return null;
if (node.key.modifiers.length > 1) return null;
const bindArgument = context.sourceCode.getText(node.key.argument);
return fixer.replaceText(node.key, `v-model:${bindArgument}`);
}
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_v_bind_sync();
}
});
@@ -0,0 +1,40 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_v_is$1 = require('./syntaxes/v-is.js');
//#region lib/rules/no-deprecated-v-is.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_v_is = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const vIs = require_v_is$1.default;
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow deprecated `v-is` directive (in Vue.js 3.1.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-v-is.html"
},
fixable: null,
schema: [],
messages: { forbiddenVIs: "`v-is` directive is deprecated." }
},
create(context) {
const templateBodyVisitor = vIs.createTemplateBodyVisitor(context);
return utils.defineTemplateBodyVisitor(context, templateBodyVisitor);
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_v_is();
}
});
@@ -0,0 +1,43 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-v-on-native-modifier.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_v_on_native_modifier = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `.native` modifiers (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-v-on-native-modifier.html"
},
fixable: null,
schema: [],
messages: { deprecated: "'.native' modifier on 'v-on' directive is deprecated." }
},
create(context) {
return utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name.name='on'] > VDirectiveKey > VIdentifier[name='native']"(node) {
if (!node.parent.modifiers.includes(node)) return;
context.report({
node,
messageId: "deprecated"
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_v_on_native_modifier();
}
});
@@ -0,0 +1,38 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_keycode_to_key = require('../utils/keycode-to-key.js');
//#region lib/rules/no-deprecated-v-on-number-modifiers.ts
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
var no_deprecated_v_on_number_modifiers_default = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-v-on-number-modifiers.html"
},
fixable: "code",
schema: [],
messages: { numberModifierIsDeprecated: "'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead." }
},
create(context) {
return import_utils.default.defineTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name.name='on'] > VDirectiveKey"(node) {
const modifier = node.modifiers.find((mod) => Number.isInteger(Number.parseInt(mod.name, 10)));
if (!modifier) return;
const keyCodes = Number.parseInt(modifier.name, 10);
if (keyCodes > 9 || keyCodes < 0) context.report({
node: modifier,
messageId: "numberModifierIsDeprecated",
fix(fixer) {
const key = require_keycode_to_key.default[keyCodes];
if (!key) return null;
return fixer.replaceText(modifier, `${key}`);
}
});
} });
}
};
//#endregion
exports.default = no_deprecated_v_on_number_modifiers_default;
@@ -0,0 +1,44 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-deprecated-vue-config-keycodes.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_deprecated_vue_config_keycodes = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-deprecated-vue-config-keycodes.html"
},
fixable: null,
schema: [],
messages: { unexpected: "`Vue.config.keyCodes` are deprecated." }
},
create(context) {
return { "MemberExpression[property.type='Identifier'][property.name='keyCodes']"(node) {
const config = utils.skipChainExpression(node.object);
if (config.type !== "MemberExpression" || config.property.type !== "Identifier" || config.property.name !== "config" || config.object.type !== "Identifier" || config.object.name !== "Vue") return;
context.report({
node,
messageId: "unexpected"
});
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_deprecated_vue_config_keycodes();
}
});
+139
View File
@@ -0,0 +1,139 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-dupe-keys.js
/**
* @fileoverview Prevents duplication of field names.
* @author Armano
*/
var require_no_dupe_keys = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { findVariable } = require("@eslint-community/eslint-utils");
const utils = require_index.default;
/**
* @typedef {import('../utils').GroupName} GroupName
* @typedef {import('eslint').Scope.Variable} Variable
* @typedef {import('../utils').ComponentProp} ComponentProp
*/
/** @type {GroupName[]} */
const GROUP_NAMES = [
"props",
"computed",
"data",
"methods",
"setup"
];
/**
* Gets the props pattern node from given `defineProps()` node
* @param {CallExpression} node
* @returns {Pattern|null}
*/
function getPropsPattern(node) {
let target = node;
if (target.parent && target.parent.type === "CallExpression" && target.parent.arguments[0] === target && target.parent.callee.type === "Identifier" && target.parent.callee.name === "withDefaults") target = target.parent;
if (!target.parent || target.parent.type !== "VariableDeclarator" || target.parent.init !== target) return null;
return target.parent.id;
}
/**
* Checks whether the initialization of the given variable declarator node contains one of the references.
* @param {VariableDeclarator} node
* @param {ESNode[]} references
*/
function isInsideInitializer(node, references) {
const init = node.init;
if (!init) return false;
return references.some((id) => init.range[0] <= id.range[0] && id.range[1] <= init.range[1]);
}
/**
* Collects all renamed props from a pattern
* @param {Pattern | null} pattern - The destructuring pattern
* @returns {Set<string>} - Set of prop names that have been renamed
*/
function collectRenamedProps(pattern) {
const renamedProps = /* @__PURE__ */ new Set();
if (!pattern || pattern.type !== "ObjectPattern") return renamedProps;
for (const prop of pattern.properties) {
if (prop.type !== "Property") continue;
if (prop.key.type === "Identifier" && prop.value.type === "Identifier" && prop.key.name !== prop.value.name) renamedProps.add(prop.key.name);
}
return renamedProps;
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow duplication of field names",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-dupe-keys.html"
},
fixable: null,
schema: [{
type: "object",
properties: { groups: { type: "array" } },
additionalProperties: false
}],
messages: { duplicateKey: "Duplicate key '{{name}}'. May cause name collision in script or template tag." }
},
create(context) {
const options = context.options[0] || {};
const groups = new Set([...GROUP_NAMES, ...options.groups || []]);
return utils.compositingVisitors(utils.executeOnVue(context, (obj) => {
const properties = utils.iterateProperties(obj, groups);
/** @type {Set<string>} */
const usedNames = /* @__PURE__ */ new Set();
for (const o of properties) {
if (usedNames.has(o.name)) context.report({
node: o.node,
messageId: "duplicateKey",
data: { name: o.name }
});
usedNames.add(o.name);
}
}), utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node, props) {
const propsNode = getPropsPattern(node);
const propReferences = [...propsNode ? extractReferences(propsNode) : [], node];
const renamedProps = collectRenamedProps(propsNode);
for (const prop of props) {
if (!prop.propName) continue;
if (renamedProps.has(prop.propName)) continue;
const variable = findVariable(utils.getScope(context, node), prop.propName);
if (!variable || variable.defs.length === 0) continue;
if (variable.defs.some((def) => {
if (def.type !== "Variable") return false;
return isInsideInitializer(def.node, propReferences);
})) continue;
context.report({
node: variable.defs[0].node,
messageId: "duplicateKey",
data: { name: prop.propName }
});
}
} }));
/**
* Extracts references from the given node.
* @param {Pattern} node
* @returns {Identifier[]} References
*/
function extractReferences(node) {
if (node.type === "Identifier") {
const variable = findVariable(utils.getScope(context, node), node);
if (!variable) return [];
return variable.references.map((ref) => ref.identifier);
}
if (node.type === "ObjectPattern") return node.properties.flatMap((prop) => extractReferences(prop.type === "Property" ? prop.value : prop));
if (node.type === "AssignmentPattern") return extractReferences(node.left);
if (node.type === "RestElement") return extractReferences(node.argument);
return [];
}
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_dupe_keys();
}
});
+134
View File
@@ -0,0 +1,134 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-dupe-v-else-if.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_dupe_v_else_if = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef {NonNullable<VExpressionContainer['expression']>} VExpression
*/
/**
* @typedef {object} OrOperands
* @property {VExpression} OrOperands.node
* @property {AndOperands[]} OrOperands.operands
*
* @typedef {object} AndOperands
* @property {VExpression} AndOperands.node
* @property {VExpression[]} AndOperands.operands
*/
/**
* Splits the given node by the given logical operator.
* @param {string} operator Logical operator `||` or `&&`.
* @param {VExpression} node The node to split.
* @returns {VExpression[]} Array of conditions that makes the node when joined by the operator.
*/
function splitByLogicalOperator(operator, node) {
if (node.type === "LogicalExpression" && node.operator === operator) return [...splitByLogicalOperator(operator, node.left), ...splitByLogicalOperator(operator, node.right)];
return [node];
}
/**
* @param {VExpression} node
*/
function splitByOr(node) {
return splitByLogicalOperator("||", node);
}
/**
* @param {VExpression} node
*/
function splitByAnd(node) {
return splitByLogicalOperator("&&", node);
}
/**
* @param {VExpression} node
* @returns {OrOperands}
*/
function buildOrOperands(node) {
return {
node,
operands: splitByOr(node).map((orOperand) => {
return {
node: orOperand,
operands: splitByAnd(orOperand)
};
})
};
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow duplicate conditions in `v-if` / `v-else-if` chains",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-dupe-v-else-if.html"
},
fixable: null,
schema: [],
messages: { unexpected: "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the `v-if` / `v-else-if` chain." }
},
create(context) {
const sourceCode = context.sourceCode;
const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore && sourceCode.parserServices.getTemplateBodyTokenStore();
/**
* Determines whether the two given nodes are considered to be equal. In particular, given that the nodes
* represent expressions in a boolean context, `||` and `&&` can be considered as commutative operators.
* @param {VExpression} a First node.
* @param {VExpression} b Second node.
* @returns {boolean} `true` if the nodes are considered to be equal.
*/
function equal(a, b) {
if (a.type !== b.type) return false;
if (a.type === "LogicalExpression" && b.type === "LogicalExpression" && (a.operator === "||" || a.operator === "&&") && a.operator === b.operator) return equal(a.left, b.left) && equal(a.right, b.right) || equal(a.left, b.right) && equal(a.right, b.left);
return utils.equalTokens(a, b, tokenStore);
}
/**
* Determines whether the first given AndOperands is a subset of the second given AndOperands.
*
* e.g. A: (a && b), B: (a && b && c): B is a subset of A.
*
* @param {AndOperands} operandsA The AndOperands to compare from.
* @param {AndOperands} operandsB The AndOperands to compare against.
* @returns {boolean} `true` if the `andOperandsA` is a subset of the `andOperandsB`.
*/
function isSubset(operandsA, operandsB) {
return operandsA.operands.every((operandA) => operandsB.operands.some((operandB) => equal(operandA, operandB)));
}
return utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name.name='else-if']"(node) {
if (!node.value || !node.value.expression) return;
const test = node.value.expression;
const listToCheck = (test.type === "LogicalExpression" && test.operator === "&&" ? [...splitByAnd(test), test] : [test]).map(buildOrOperands);
/** @type {VElement | null} */
let current = node.parent.parent;
while (current && (current = utils.prevSibling(current))) {
const vIf = utils.getDirective(current, "if");
const currentTestDir = vIf || utils.getDirective(current, "else-if");
if (!currentTestDir) return;
if (currentTestDir.value && currentTestDir.value.expression) {
const currentOrOperands = buildOrOperands(currentTestDir.value.expression);
for (const condition of listToCheck) if ((condition.operands = condition.operands.filter((orOperand) => !currentOrOperands.operands.some((currentOrOperand) => isSubset(currentOrOperand, orOperand)))).length === 0) {
context.report({
node: condition.node,
messageId: "unexpected"
});
return;
}
}
if (vIf) return;
}
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_dupe_v_else_if();
}
});
@@ -0,0 +1,85 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-duplicate-attr-inheritance.js
/**
* @fileoverview Disable inheritAttrs when using v-bind="$attrs"
* @author Hiroki Osame
*/
var require_no_duplicate_attr_inheritance = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/** @param {VElement[]} elements */
function isConditionalGroup(elements) {
const firstElement = elements[0];
const lastElement = elements.at(-1);
if (!lastElement || elements.length < 2) return false;
const inBetweenElements = elements.slice(1, -1);
return utils.hasDirective(firstElement, "if") && (utils.hasDirective(lastElement, "else-if") || utils.hasDirective(lastElement, "else")) && inBetweenElements.every((element) => utils.hasDirective(element, "else-if"));
}
/** @param {VElement[]} elements */
function isMultiRootNodes(elements) {
if (elements.length > 1 && !isConditionalGroup(elements)) return true;
return false;
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce `inheritAttrs` to be set to `false` when using `v-bind=\"$attrs\"`",
categories: void 0,
url: "https://eslint.vuejs.org/rules/no-duplicate-attr-inheritance.html"
},
fixable: null,
schema: [{
type: "object",
properties: { checkMultiRootNodes: { type: "boolean" } },
additionalProperties: false
}],
messages: { noDuplicateAttrInheritance: "Set \"inheritAttrs\" to false." }
},
create(context) {
const checkMultiRootNodes = (context.options[0] || {}).checkMultiRootNodes === true;
/** @type {Literal['value']} */
let inheritsAttrs = true;
/** @type {VReference[]} */
const attrsRefs = [];
/** @param {ObjectExpression} node */
function processOptions(node) {
const inheritAttrsProp = utils.findProperty(node, "inheritAttrs");
if (inheritAttrsProp && inheritAttrsProp.value.type === "Literal") inheritsAttrs = inheritAttrsProp.value.value;
}
return utils.compositingVisitors(utils.executeOnVue(context, processOptions), utils.defineScriptSetupVisitor(context, { onDefineOptionsEnter(node) {
if (node.arguments.length === 0) return;
const define = node.arguments[0];
if (define.type !== "ObjectExpression") return;
processOptions(define);
} }), utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name.name='bind'][key.argument=null] > VExpressionContainer"(node) {
if (!inheritsAttrs) return;
const reference = node.references.find((reference) => {
if (reference.variable != null) return false;
return reference.id.name === "$attrs";
});
if (reference) attrsRefs.push(reference);
} }), { "Program:exit"(program) {
const element = program.templateBody;
if (element == null) return;
const rootElements = element.children.filter(utils.isVElement);
if (!checkMultiRootNodes && isMultiRootNodes(rootElements)) return;
if (attrsRefs.length > 0) for (const attrsRef of attrsRefs) context.report({
node: attrsRef.id,
messageId: "noDuplicateAttrInheritance"
});
} });
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_duplicate_attr_inheritance();
}
});
@@ -0,0 +1,87 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-duplicate-attributes.js
/**
* @author Toru Nagashima
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
var require_no_duplicate_attributes = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* Get the name of the given attribute node.
* @param {VAttribute | VDirective} attribute The attribute node to get.
* @returns {string | null} The name of the attribute.
*/
function getName(attribute) {
if (!attribute.directive) return attribute.key.name;
if (attribute.key.name.name === "bind") return attribute.key.argument && attribute.key.argument.type === "VIdentifier" && attribute.key.argument.name || null;
return null;
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow duplication of attributes",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-duplicate-attributes.html"
},
fixable: null,
schema: [{
type: "object",
properties: {
allowCoexistClass: { type: "boolean" },
allowCoexistStyle: { type: "boolean" }
},
additionalProperties: false
}],
messages: { duplicateAttribute: "Duplicate attribute '{{name}}'." }
},
create(context) {
const options = context.options[0] || {};
const allowCoexistStyle = options.allowCoexistStyle !== false;
const allowCoexistClass = options.allowCoexistClass !== false;
/** @type {Set<string>} */
const directiveNames = /* @__PURE__ */ new Set();
/** @type {Set<string>} */
const attributeNames = /* @__PURE__ */ new Set();
/**
* @param {string} name
* @param {boolean} isDirective
*/
function isDuplicate(name, isDirective) {
if (allowCoexistStyle && name === "style" || allowCoexistClass && name === "class") return isDirective ? directiveNames.has(name) : attributeNames.has(name);
return directiveNames.has(name) || attributeNames.has(name);
}
return utils.defineTemplateBodyVisitor(context, {
VStartTag() {
directiveNames.clear();
attributeNames.clear();
},
VAttribute(node) {
const name = getName(node);
if (name == null) return;
if (isDuplicate(name, node.directive)) context.report({
node,
loc: node.loc,
messageId: "duplicateAttribute",
data: { name }
});
if (node.directive) directiveNames.add(name);
else attributeNames.add(name);
}
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_duplicate_attributes();
}
});
@@ -0,0 +1,236 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-duplicate-class-names.js
/**
* @fileoverview disallow duplication of class names in class attributes
* @author Yizack Rangel
* See LICENSE file in root directory for full license.
*/
var require_no_duplicate_class_names = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @param {VDirective} node
* @param {Expression} [expression]
* @param {boolean} [unconditional=true] whether the expression is unconditional
* @param {Expression} [parentExpr] parent expression for context
* @return {IterableIterator<{ node: Literal | TemplateElement, unconditional: boolean, parentExpr?: Expression }>}
*/
function* extractClassNodes(node, expression, unconditional = true, parentExpr) {
const nodeExpression = expression ?? node.value?.expression;
if (!nodeExpression) return;
switch (nodeExpression.type) {
case "Literal":
yield {
node: nodeExpression,
unconditional,
parentExpr
};
break;
case "ObjectExpression":
for (const prop of nodeExpression.properties) if (prop.type === "Property" && prop.key?.type === "Literal" && typeof prop.key.value === "string") yield {
node: prop.key,
unconditional: false,
parentExpr: nodeExpression
};
break;
case "ArrayExpression":
for (const element of nodeExpression.elements) {
if (!element || element.type === "SpreadElement") continue;
yield* extractClassNodes(node, element, unconditional, nodeExpression);
}
break;
case "ConditionalExpression":
yield* extractClassNodes(node, nodeExpression.consequent, false, nodeExpression);
yield* extractClassNodes(node, nodeExpression.alternate, false, nodeExpression);
break;
case "TemplateLiteral":
for (const quasi of nodeExpression.quasis) yield {
node: quasi,
unconditional,
parentExpr: nodeExpression
};
for (const expr of nodeExpression.expressions) yield* extractClassNodes(node, expr, unconditional, nodeExpression);
break;
case "BinaryExpression":
if (nodeExpression.operator === "+") {
yield* extractClassNodes(node, nodeExpression.left, unconditional, nodeExpression);
yield* extractClassNodes(node, nodeExpression.right, unconditional, nodeExpression);
}
break;
case "LogicalExpression":
yield* extractClassNodes(node, nodeExpression.left, unconditional, nodeExpression);
yield* extractClassNodes(node, nodeExpression.right, false, nodeExpression);
break;
}
}
/**
* @param {string} classList
* @returns {string[]}
*/
function getClassNames(classList) {
return classList.split(/\s+/).filter(Boolean);
}
/**
* @param {string} raw - raw class names string including quotes
* @returns {string}
*/
function removeDuplicateClassNames(raw) {
const quote = raw[0];
const inner = raw.slice(1, -1);
const tokens = inner.split(/(\s+)/);
/** @type {string[]} */
const kept = [];
const used = /* @__PURE__ */ new Set();
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (!token) continue;
if (/^\s+$/.test(token)) if (kept.length > 0) kept[kept.length - 1] += token;
else kept.push(token);
else if (used.has(token)) {
const nextToken = tokens[i + 1];
if (kept.length > 0 && i + 1 < tokens.length && /^\s+$/.test(nextToken)) {
for (let j = kept.length - 1; j >= 0; j--) if (!/^\s+$/.test(kept[j])) {
kept[j] = kept[j].split(/(\s+)/)[0] + nextToken;
break;
}
i++;
}
} else {
kept.push(token);
used.add(token);
}
}
const endsWithSpace = /\s$/.test(inner);
const lastItem = kept.at(-1);
if (lastItem && !endsWithSpace) {
if (!/^\s+$/.test(lastItem)) {
const parts = lastItem.split(/(\s+)/);
kept[kept.length - 1] = parts[0];
}
}
return quote + kept.join("") + quote;
}
/** @param {VLiteral | Literal | TemplateElement | null} node */
function getRawValue(node) {
if (!node?.value) return null;
return typeof node.value === "object" && "raw" in node.value ? node.value.raw : node.value;
}
module.exports = {
meta: {
type: "suggestion",
docs: {
url: "https://eslint.vuejs.org/rules/no-duplicate-class-names.html",
description: "disallow duplication of class names in class attributes",
categories: void 0
},
fixable: "code",
schema: [],
messages: { duplicateClassNames: "Duplicate class name{{plural}} {{names}}." }
},
create: (context) => {
/**
* @param {VLiteral | Literal | TemplateElement | null} node
*/
function reportDuplicateClasses(node) {
if (!node?.value) return;
const classList = getRawValue(node);
if (typeof classList !== "string") return;
const classNames = getClassNames(classList);
if (classNames.length <= 1) return;
const seen = /* @__PURE__ */ new Set();
const duplicates = /* @__PURE__ */ new Set();
for (const className of classNames) if (seen.has(className)) duplicates.add(className);
else seen.add(className);
if (duplicates.size === 0) return;
context.report({
node,
messageId: "duplicateClassNames",
data: {
names: [...duplicates].map((name) => `'${name}'`).join(", "),
plural: duplicates.size > 1 ? "s" : ""
},
fix: (fixer) => {
const raw = context.sourceCode.text.slice(node.range[0], node.range[1]);
return fixer.replaceText(node, removeDuplicateClassNames(raw));
}
});
return duplicates;
}
return utils.defineTemplateBodyVisitor(context, {
"VAttribute[directive=false][key.name='class'][value.type='VLiteral']"(node) {
reportDuplicateClasses(node.value);
},
"VAttribute[directive=true][key.argument.name='class'][value.type='VExpressionContainer']"(node) {
const parent = node.parent;
const staticAttr = (parent.attributes || []).find((attr) => attr.key && attr.key.name === "class" && attr.value && attr.value.type === "VLiteral");
/** @type {Set<string> | null} */
let staticClasses = null;
if (staticAttr && staticAttr.value && staticAttr.value.type === "VLiteral") staticClasses = new Set(getClassNames(String(staticAttr.value.value)));
/** @type {Set<string>} */
const reported = /* @__PURE__ */ new Set();
/** @type {Set<string>} */
const duplicatesInExpression = /* @__PURE__ */ new Set();
/** @type {Map<string, ASTNode>} */
const seen = /* @__PURE__ */ new Map();
/** @type {Map<string, {node: ASTNode, unconditional: boolean, parentExpr?: Expression}>} */
const collected = /* @__PURE__ */ new Map();
const classNodes = extractClassNodes(node);
for (const { node: reportNode, unconditional, parentExpr } of classNodes) {
const reportedClasses = reportDuplicateClasses(reportNode);
if (reportedClasses) for (const reportedClass of reportedClasses) reported.add(reportedClass);
const classList = getRawValue(reportNode);
if (typeof classList !== "string") continue;
const classNames = getClassNames(classList);
for (const className of classNames) {
if (reported.has(className)) continue;
const existing = collected.get(className);
if (existing) {
const isSameParent = parentExpr && existing.parentExpr === parentExpr && (parentExpr.type === "BinaryExpression" || parentExpr.type === "TemplateLiteral");
if (existing.unconditional || unconditional || isSameParent) duplicatesInExpression.add(className);
} else collected.set(className, {
node: reportNode.parent,
unconditional,
parentExpr
});
if (unconditional) if (seen.has(className)) duplicatesInExpression.add(className);
else seen.set(className, reportNode.parent);
}
if (staticClasses) {
const intersection = classNames.filter((n) => staticClasses.has(n));
if (intersection.length > 0 && parent) context.report({
node: parent,
messageId: "duplicateClassNames",
data: {
names: intersection.map((name) => `'${name}'`).join(", "),
plural: intersection.length > 1 ? "s" : ""
}
});
}
}
for (const className of duplicatesInExpression) {
const reportNode = seen.get(className) || collected.get(className)?.node;
if (reportNode) context.report({
node: reportNode,
messageId: "duplicateClassNames",
data: {
names: `'${className}'`,
plural: ""
}
});
}
}
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_duplicate_class_names();
}
});
@@ -0,0 +1,72 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-empty-component-block.js
/**
* @author tyankatsu <https://github.com/tyankatsu0105>
* See LICENSE file in root directory for full license.
*/
var require_no_empty_component_block = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { isVElement } = require_index.default;
/**
* check whether has attribute `src`
* @param {VElement} componentBlock
*/
function hasAttributeSrc(componentBlock) {
const hasAttribute = componentBlock.startTag.attributes.length > 0;
const hasSrc = componentBlock.startTag.attributes.some((attribute) => !attribute.directive && attribute.key.name === "src" && attribute.value && attribute.value.value !== "");
return hasAttribute && hasSrc;
}
/**
* check whether value under the component block is only whitespaces or break lines
* @param {VElement} componentBlock
*/
function isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) {
return componentBlock.children.length === 1 && componentBlock.children[0].type === "VText" && !componentBlock.children[0].value.trim();
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow the `<template>` `<script>` `<style>` block to be empty",
categories: void 0,
url: "https://eslint.vuejs.org/rules/no-empty-component-block.html"
},
fixable: "code",
schema: [],
messages: { unexpected: "`<{{ blockName }}>` is empty. Empty block is not allowed." }
},
create(context) {
const sourceCode = context.sourceCode;
if (!sourceCode.parserServices.getDocumentFragment) return {};
const documentFragment = sourceCode.parserServices.getDocumentFragment();
if (!documentFragment) return {};
const componentBlocks = documentFragment.children.filter(isVElement);
return { Program() {
for (const componentBlock of componentBlocks) {
if (componentBlock.name !== "template" && componentBlock.name !== "script" && componentBlock.name !== "style") continue;
if (hasAttributeSrc(componentBlock)) continue;
if (isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) || componentBlock.children.length === 0) context.report({
node: componentBlock,
loc: componentBlock.loc,
messageId: "unexpected",
data: { blockName: componentBlock.name },
fix(fixer) {
return fixer.remove(componentBlock);
}
});
}
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_empty_component_block();
}
});
+21
View File
@@ -0,0 +1,21 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-empty-pattern.js
/**
* @author Yosuke Ota
*/
var require_no_empty_pattern = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { wrapCoreRule } = require_index.default;
module.exports = wrapCoreRule("no-empty-pattern");
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_empty_pattern();
}
});
@@ -0,0 +1,63 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-export-in-script-setup.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_export_in_script_setup = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
/**
* @typedef {import('@typescript-eslint/types').TSESTree.ExportAllDeclaration} TSESTreeExportAllDeclaration
* @typedef {import('@typescript-eslint/types').TSESTree.ExportDefaultDeclaration} TSESTreeExportDefaultDeclaration
* @typedef {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration} TSESTreeExportNamedDeclaration
*/
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow `export` in `<script setup>`",
categories: ["vue3-essential", "vue2-essential"],
url: "https://eslint.vuejs.org/rules/no-export-in-script-setup.html"
},
fixable: null,
schema: [],
messages: { forbidden: "`<script setup>` cannot contain ES module exports." }
},
create(context) {
/**
* @param {ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration} node
* @param {SourceLocation} loc
*/
function verify(node, loc) {
const tsNode = node;
if (tsNode.exportKind === "type") return;
if (tsNode.type === "ExportNamedDeclaration" && tsNode.specifiers.length > 0 && tsNode.specifiers.every((spec) => spec.exportKind === "type")) return;
context.report({
node,
loc,
messageId: "forbidden"
});
}
return utils.defineScriptSetupVisitor(context, {
ExportAllDeclaration: (node) => verify(node, node.loc),
ExportDefaultDeclaration: (node) => verify(node, node.loc),
ExportNamedDeclaration: (node) => {
if (node.declaration) verify(node, context.sourceCode.getFirstToken(node).loc);
else verify(node, node.loc);
}
});
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_export_in_script_setup();
}
});
@@ -0,0 +1,158 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-expose-after-await.js
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
var require_no_expose_after_await = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const { findVariable } = require("@eslint-community/eslint-utils");
const utils = require_index.default;
/**
* Get the callee member node from the given CallExpression
* @param {CallExpression} node CallExpression
*/
function getCalleeMemberNode(node) {
const callee = utils.skipChainExpression(node.callee);
if (callee.type === "MemberExpression") {
const name = utils.getStaticPropertyName(callee);
if (name) return {
name,
member: callee
};
}
return null;
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow asynchronously registered `expose`",
categories: ["vue3-essential"],
url: "https://eslint.vuejs.org/rules/no-expose-after-await.html"
},
fixable: null,
schema: [],
messages: { forbidden: "`{{name}}` is forbidden after an `await` expression." }
},
create(context) {
/**
* @typedef {object} SetupScopeData
* @property {boolean} afterAwait
* @property {[number,number]} range
* @property {(node: Identifier, callNode: CallExpression) => boolean} isExposeReferenceId
* @property {(node: Identifier) => boolean} isContextReferenceId
*/
/**
* @typedef {object} ScopeStack
* @property {ScopeStack | null} upper
* @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program} scopeNode
*/
/** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program, SetupScopeData>} */
const setupScopes = /* @__PURE__ */ new Map();
/** @type {ScopeStack | null} */
let scopeStack = null;
return utils.compositingVisitors({ Program(node) {
scopeStack = {
upper: scopeStack,
scopeNode: node
};
} }, {
":function"(node) {
scopeStack = {
upper: scopeStack,
scopeNode: node
};
},
":function:exit"() {
scopeStack = scopeStack && scopeStack.upper;
},
AwaitExpression(node) {
if (!scopeStack) return;
const setupScope = setupScopes.get(scopeStack.scopeNode);
if (!setupScope || !utils.inRange(setupScope.range, node)) return;
setupScope.afterAwait = true;
},
CallExpression(node) {
if (!scopeStack) return;
const setupScope = setupScopes.get(scopeStack.scopeNode);
if (!setupScope || !setupScope.afterAwait || !utils.inRange(setupScope.range, node)) return;
const { isContextReferenceId, isExposeReferenceId } = setupScope;
if (node.callee.type === "Identifier" && isExposeReferenceId(node.callee, node)) context.report({
node,
messageId: "forbidden",
data: { name: node.callee.name }
});
else {
const expose = getCalleeMemberNode(node);
if (expose && expose.name === "expose" && expose.member.object.type === "Identifier" && isContextReferenceId(expose.member.object)) context.report({
node,
messageId: "forbidden",
data: { name: expose.name }
});
}
}
}, (() => {
const scriptSetup = utils.getScriptSetupElement(context);
if (!scriptSetup) return {};
return { Program(node) {
setupScopes.set(node, {
afterAwait: false,
range: scriptSetup.range,
isExposeReferenceId: (id, callNode) => callNode.parent.type === "ExpressionStatement" && callNode.parent.parent === node && id.name === "defineExpose",
isContextReferenceId: () => false
});
} };
})(), utils.defineVueVisitor(context, {
onSetupFunctionEnter(node) {
const contextParam = node.params[1];
if (!contextParam) return;
if (contextParam.type === "RestElement") return;
if (contextParam.type === "ArrayPattern") return;
/** @type {Set<Identifier>} */
const contextReferenceIds = /* @__PURE__ */ new Set();
/** @type {Set<Identifier>} */
const exposeReferenceIds = /* @__PURE__ */ new Set();
if (contextParam.type === "ObjectPattern") {
const exposeProperty = utils.findAssignmentProperty(contextParam, "expose");
if (!exposeProperty) return;
const exposeParam = exposeProperty.value;
const variable = exposeParam.type === "Identifier" ? findVariable(utils.getScope(context, exposeParam), exposeParam) : null;
if (!variable) return;
for (const reference of variable.references) {
if (!reference.isRead()) continue;
exposeReferenceIds.add(reference.identifier);
}
} else if (contextParam.type === "Identifier") {
const variable = findVariable(utils.getScope(context, contextParam), contextParam);
if (!variable) return;
for (const reference of variable.references) {
if (!reference.isRead()) continue;
contextReferenceIds.add(reference.identifier);
}
}
setupScopes.set(node, {
afterAwait: false,
range: node.range,
isExposeReferenceId: (id) => exposeReferenceIds.has(id),
isContextReferenceId: (id) => contextReferenceIds.has(id)
});
},
onSetupFunctionExit(node) {
setupScopes.delete(node);
}
}));
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_expose_after_await();
}
});
+133
View File
@@ -0,0 +1,133 @@
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
const require_index$1 = require('../utils/style-variables/index.js');
let _eslint_community_eslint_utils = require("@eslint-community/eslint-utils");
//#region lib/rules/no-extra-parens.ts
/**
* @author Yosuke Ota
*/
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
var no_extra_parens_default = import_utils.default.wrapStylisticOrCoreRule("no-extra-parens", {
skipDynamicArguments: true,
applyDocument: true,
create: createForVueSyntax
});
/**
* Check whether the given token is a left parenthesis.
*/
function isLeftParen(token) {
return token.type === "Punctuator" && token.value === "(";
}
/**
* Check whether the given token is a right parenthesis.
*/
function isRightParen(token) {
return token.type === "Punctuator" && token.value === ")";
}
/**
* Check whether the given token is a left brace.
*/
function isLeftBrace(token) {
return token.type === "Punctuator" && token.value === "{";
}
/**
* Check whether the given token is a right brace.
*/
function isRightBrace(token) {
return token.type === "Punctuator" && token.value === "}";
}
/**
* Check whether the given token is a left bracket.
*/
function isLeftBracket(token) {
return token.type === "Punctuator" && token.value === "[";
}
/**
* Check whether the given token is a right bracket.
*/
function isRightBracket(token) {
return token.type === "Punctuator" && token.value === "]";
}
/**
* Determines if a given expression node is an IIFE
*/
function isIIFE(node) {
return node.type === "CallExpression" && node.callee.type === "FunctionExpression";
}
function createForVueSyntax(context) {
const sourceCode = context.sourceCode;
if (!sourceCode.parserServices.getTemplateBodyTokenStore) return {};
const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore();
/**
* Checks if the given node turns into a filter when unwraped.
*/
function isUnwrapChangeToFilter(expression) {
let parenStack = null;
for (const token of tokenStore.getTokens(expression)) {
if (parenStack) {
if (parenStack.isUpToken(token)) {
parenStack = parenStack.upper;
continue;
}
} else if (token.value === "|") return true;
if (isLeftParen(token)) parenStack = {
isUpToken: isRightParen,
upper: parenStack
};
else if (isLeftBracket(token)) parenStack = {
isUpToken: isRightBracket,
upper: parenStack
};
else if (isLeftBrace(token)) parenStack = {
isUpToken: isRightBrace,
upper: parenStack
};
}
return false;
}
/**
* Checks if the given node is CSS v-bind() without quote.
*/
function isStyleVariableWithoutQuote(node, expression) {
const styleVars = require_index$1.getStyleVariablesContext(context);
if (!styleVars || !styleVars.vBinds.includes(node)) return false;
const vBindToken = tokenStore.getFirstToken(node);
return tokenStore.getTokensBetween(vBindToken, expression).every(isLeftParen);
}
function verify(node) {
if (!node.expression) return;
const expression = node.expression.type === "VFilterSequenceExpression" ? node.expression.expression : node.expression;
if (!(0, _eslint_community_eslint_utils.isParenthesized)(expression, tokenStore)) return;
if (!(0, _eslint_community_eslint_utils.isParenthesized)(2, expression, tokenStore)) {
if (isIIFE(expression) && !(0, _eslint_community_eslint_utils.isParenthesized)(expression.callee, tokenStore)) return;
if (isUnwrapChangeToFilter(expression)) return;
if (isStyleVariableWithoutQuote(node, expression)) return;
}
report(expression);
}
/**
* Report the node
*/
function report(node) {
const sourceCode = context.sourceCode;
const leftParenToken = tokenStore.getTokenBefore(node);
const rightParenToken = tokenStore.getTokenAfter(node);
context.report({
node,
loc: leftParenToken.loc,
messageId: "unexpected",
fix(fixer) {
const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
return fixer.replaceTextRange([leftParenToken.range[0], rightParenToken.range[1]], parenthesizedSource);
}
});
}
return {
"VAttribute[directive=true][key.name.name='bind'] > VExpressionContainer": verify,
"VElement > VExpressionContainer": verify
};
}
//#endregion
exports.default = no_extra_parens_default;
@@ -0,0 +1,22 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-implicit-coercion.js
/**
* @author lozinsky <https://github.com/lozinsky>
* See LICENSE file in root directory for full license.
*/
var require_no_implicit_coercion = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
module.exports = utils.wrapCoreRule("no-implicit-coercion", { applyDocument: true });
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_implicit_coercion();
}
});
@@ -0,0 +1,83 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-import-compiler-macros.js
/**
* @author Wayne Zhang
* See LICENSE file in root directory for full license.
*/
var require_no_import_compiler_macros = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const COMPILER_MACROS = new Set([
"defineProps",
"defineEmits",
"defineExpose",
"withDefaults",
"defineModel",
"defineOptions",
"defineSlots"
]);
const VUE_MODULES = new Set([
"@vue/runtime-core",
"@vue/runtime-dom",
"vue"
]);
/**
* @param {Token} node
*/
function isComma(node) {
return node.type === "Punctuator" && node.value === ",";
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow importing Vue compiler macros",
categories: void 0,
url: "https://eslint.vuejs.org/rules/no-import-compiler-macros.html"
},
fixable: "code",
schema: [],
messages: {
noImportCompilerMacros: "'{{name}}' is a compiler macro and doesn't need to be imported.",
onlyValidInScriptSetup: "'{{name}}' is a compiler macro and can only be used inside <script setup>."
}
},
create(context) {
const sourceCode = context.sourceCode;
return { ImportDeclaration(node) {
if (node.specifiers.length === 0 || !VUE_MODULES.has(node.source.value)) return;
for (const specifier of node.specifiers) {
if (specifier.type !== "ImportSpecifier" || !COMPILER_MACROS.has(specifier.imported.name)) continue;
context.report({
node: specifier,
messageId: utils.isScriptSetup(context) ? "noImportCompilerMacros" : "onlyValidInScriptSetup",
data: { name: specifier.imported.name },
fix: (fixer) => {
const isOnlySpecifier = node.specifiers.length === 1;
const isLastSpecifier = specifier === node.specifiers.at(-1);
if (isOnlySpecifier) return fixer.remove(node);
else if (isLastSpecifier) {
const precedingComma = sourceCode.getTokenBefore(specifier, isComma);
return fixer.removeRange([precedingComma ? precedingComma.range[0] : specifier.range[0], specifier.range[1]]);
} else {
const subsequentComma = sourceCode.getTokenAfter(specifier, isComma);
return fixer.removeRange([specifier.range[0], subsequentComma ? subsequentComma.range[1] : specifier.range[1]]);
}
}
});
}
} };
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_import_compiler_macros();
}
});
@@ -0,0 +1,168 @@
'use strict';
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_index = require('../utils/index.js');
//#region lib/rules/no-irregular-whitespace.js
/**
* @author Yosuke Ota
* @fileoverview Rule to disalow whitespace that is not a tab or space, whitespace inside strings and comments are allowed
*/
var require_no_irregular_whitespace = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
const utils = require_index.default;
const ALL_IRREGULARS = /[\f\v\u0085\uFEFF\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000\u2028\u2029]/u;
const IRREGULAR_WHITESPACE = /[\f\v\u0085\uFEFF\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000]+/gmu;
const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/gmu;
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow irregular whitespace in `.vue` files",
categories: void 0,
url: "https://eslint.vuejs.org/rules/no-irregular-whitespace.html",
extensionSource: {
url: "https://eslint.org/docs/rules/no-irregular-whitespace",
name: "ESLint core"
}
},
schema: [{
type: "object",
properties: {
skipComments: { type: "boolean" },
skipStrings: { type: "boolean" },
skipTemplates: { type: "boolean" },
skipRegExps: { type: "boolean" },
skipHTMLAttributeValues: { type: "boolean" },
skipHTMLTextContents: { type: "boolean" }
},
additionalProperties: false
}],
messages: { disallow: "Irregular whitespace not allowed." }
},
create(context) {
/** @type {{start: number, end: number}[]} */
let errorIndices = [];
const options = context.options[0] || {};
const skipComments = !!options.skipComments;
const skipStrings = options.skipStrings !== false;
const skipRegExps = !!options.skipRegExps;
const skipTemplates = !!options.skipTemplates;
const skipHTMLAttributeValues = !!options.skipHTMLAttributeValues;
const skipHTMLTextContents = !!options.skipHTMLTextContents;
const sourceCode = context.sourceCode;
/**
* Removes errors that occur inside a string node
* @param {ASTNode | Token} node to check for matching errors.
* @returns {void}
* @private
*/
function removeWhitespaceError(node) {
const [startIndex, endIndex] = node.range;
errorIndices = errorIndices.filter((error) => error.start < startIndex || endIndex <= error.start);
}
/**
* Checks literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {Literal} node to check for matching errors.
* @returns {void}
* @private
*/
function removeInvalidNodeErrorsInLiteral(node) {
const shouldCheckStrings = skipStrings && typeof node.value === "string";
const shouldCheckRegExps = skipRegExps && Boolean(node.regex);
if ((shouldCheckStrings || shouldCheckRegExps) && ALL_IRREGULARS.test(sourceCode.getText(node))) removeWhitespaceError(node);
}
/**
* Checks template string literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {TemplateElement} node to check for matching errors.
* @returns {void}
* @private
*/
function removeInvalidNodeErrorsInTemplateLiteral(node) {
if (ALL_IRREGULARS.test(node.value.raw)) removeWhitespaceError(node);
}
/**
* Checks HTML attribute value nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {VLiteral} node to check for matching errors.
* @returns {void}
* @private
*/
function removeInvalidNodeErrorsInHTMLAttributeValue(node) {
if (ALL_IRREGULARS.test(sourceCode.getText(node))) removeWhitespaceError(node);
}
/**
* Checks HTML text content nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {VText} node to check for matching errors.
* @returns {void}
* @private
*/
function removeInvalidNodeErrorsInHTMLTextContent(node) {
if (ALL_IRREGULARS.test(sourceCode.getText(node))) removeWhitespaceError(node);
}
/**
* Checks comment nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {Comment | HTMLComment | HTMLBogusComment} node to check for matching errors.
* @returns {void}
* @private
*/
function removeInvalidNodeErrorsInComment(node) {
if (ALL_IRREGULARS.test(node.value)) removeWhitespaceError(node);
}
/**
* Checks the program source for irregular whitespaces and irregular line terminators
* @returns {void}
* @private
*/
function checkForIrregularWhitespace() {
const source = sourceCode.getText();
let match;
while ((match = IRREGULAR_WHITESPACE.exec(source)) !== null) errorIndices.push({
start: match.index,
end: match.index + match[0].length
});
while ((match = IRREGULAR_LINE_TERMINATORS.exec(source)) !== null) errorIndices.push({
start: match.index,
end: match.index + match[0].length
});
}
checkForIrregularWhitespace();
if (errorIndices.length === 0) return {};
const bodyVisitor = utils.defineTemplateBodyVisitor(context, {
...skipHTMLAttributeValues ? { "VAttribute[directive=false] > VLiteral": removeInvalidNodeErrorsInHTMLAttributeValue } : {},
...skipHTMLTextContents ? { VText: removeInvalidNodeErrorsInHTMLTextContent } : {},
Literal: removeInvalidNodeErrorsInLiteral,
...skipTemplates ? { TemplateElement: removeInvalidNodeErrorsInTemplateLiteral } : {}
});
return {
...bodyVisitor,
Literal: removeInvalidNodeErrorsInLiteral,
...skipTemplates ? { TemplateElement: removeInvalidNodeErrorsInTemplateLiteral } : {},
"Program:exit"(node) {
if (bodyVisitor["Program:exit"]) bodyVisitor["Program:exit"](node);
const templateBody = node.templateBody;
if (skipComments) {
for (const node of sourceCode.getAllComments()) removeInvalidNodeErrorsInComment(node);
if (templateBody) for (const node of templateBody.comments) removeInvalidNodeErrorsInComment(node);
}
const [scriptStart, scriptEnd] = node.range;
const [templateStart, templateEnd] = templateBody ? templateBody.range : [0, 0];
errorIndices = errorIndices.filter((error) => scriptStart <= error.start && error.start < scriptEnd || templateStart <= error.start && error.start < templateEnd);
for (const { start, end } of errorIndices) context.report({
loc: {
start: sourceCode.getLocFromIndex(start),
end: sourceCode.getLocFromIndex(end)
},
messageId: "disallow"
});
}
};
}
};
}));
//#endregion
Object.defineProperty(exports, 'default', {
enumerable: true,
get: function () {
return require_no_irregular_whitespace();
}
});

Some files were not shown because too many files have changed in this diff Show More