routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+21
@@ -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();
|
||||
}
|
||||
});
|
||||
+21
@@ -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();
|
||||
}
|
||||
});
|
||||
+21
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+100
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+240
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+70
@@ -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;
|
||||
Generated
Vendored
+120
@@ -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;
|
||||
Generated
Vendored
+67
@@ -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;
|
||||
+158
@@ -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;
|
||||
+93
@@ -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();
|
||||
}
|
||||
});
|
||||
+287
@@ -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();
|
||||
}
|
||||
});
|
||||
+60
@@ -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();
|
||||
}
|
||||
});
|
||||
+73
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+126
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+86
@@ -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();
|
||||
}
|
||||
});
|
||||
+28
@@ -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();
|
||||
}
|
||||
});
|
||||
+115
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+121
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+97
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+127
@@ -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;
|
||||
Generated
Vendored
+102
@@ -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;
|
||||
+174
@@ -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
@@ -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
@@ -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
@@ -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 ? """ : "'";
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+99
@@ -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;
|
||||
Generated
Vendored
+44
@@ -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;
|
||||
+124
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+97
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+65
@@ -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();
|
||||
}
|
||||
});
|
||||
+94
@@ -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;
|
||||
Generated
Vendored
+140
@@ -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;
|
||||
+25
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+80
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+116
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+48
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+241
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+200
@@ -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;
|
||||
+115
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+81
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+30
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+52
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+75
@@ -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();
|
||||
}
|
||||
});
|
||||
+84
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+70
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+52
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+58
@@ -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();
|
||||
}
|
||||
});
|
||||
+50
@@ -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();
|
||||
}
|
||||
});
|
||||
+43
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+45
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+59
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+43
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+101
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+105
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+57
@@ -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;
|
||||
Generated
Vendored
+40
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+41
@@ -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;
|
||||
Generated
Vendored
+40
@@ -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();
|
||||
}
|
||||
});
|
||||
+49
@@ -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();
|
||||
}
|
||||
});
|
||||
+40
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+43
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+38
@@ -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;
|
||||
Generated
Vendored
+44
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
Generated
Vendored
+85
@@ -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();
|
||||
}
|
||||
});
|
||||
+87
@@ -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();
|
||||
}
|
||||
});
|
||||
+236
@@ -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();
|
||||
}
|
||||
});
|
||||
+72
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
+63
@@ -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();
|
||||
}
|
||||
});
|
||||
+158
@@ -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
@@ -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;
|
||||
+22
@@ -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();
|
||||
}
|
||||
});
|
||||
+83
@@ -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();
|
||||
}
|
||||
});
|
||||
+168
@@ -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
Reference in New Issue
Block a user