eslint-plugin-vue
Advanced tools
Comparing version 8.7.1 to 9.0.0
@@ -19,5 +19,4 @@ /* | ||
'vue/comment-directive': 'error', | ||
'vue/jsx-uses-vars': 'error', | ||
'vue/script-setup-uses-vars': 'error' | ||
'vue/jsx-uses-vars': 'error' | ||
} | ||
} |
@@ -12,2 +12,3 @@ /* | ||
'vue/no-async-in-computed-properties': 'error', | ||
'vue/no-child-content': 'error', | ||
'vue/no-computed-properties-in-data': 'error', | ||
@@ -18,5 +19,8 @@ 'vue/no-custom-modifiers-on-v-model': 'error', | ||
'vue/no-duplicate-attributes': 'error', | ||
'vue/no-export-in-script-setup': 'error', | ||
'vue/no-multiple-template-root': 'error', | ||
'vue/no-mutating-props': 'error', | ||
'vue/no-parsing-error': 'error', | ||
'vue/no-ref-as-operand': 'error', | ||
'vue/no-reserved-component-names': 'error', | ||
'vue/no-reserved-keys': 'error', | ||
@@ -29,2 +33,3 @@ 'vue/no-reserved-props': [ | ||
], | ||
'vue/no-setup-props-destructure': 'error', | ||
'vue/no-shared-component-data': 'error', | ||
@@ -36,2 +41,3 @@ 'vue/no-side-effects-in-computed-properties': 'error', | ||
'vue/no-unused-vars': 'error', | ||
'vue/no-use-computed-property-like-method': 'error', | ||
'vue/no-use-v-if-with-v-for': 'error', | ||
@@ -41,2 +47,3 @@ 'vue/no-useless-template-attributes': 'error', | ||
'vue/no-v-model-argument': 'error', | ||
'vue/no-v-text-v-html-on-component': 'error', | ||
'vue/require-component-is': 'error', | ||
@@ -48,3 +55,8 @@ 'vue/require-prop-type-constructor': 'error', | ||
'vue/return-in-computed-property': 'error', | ||
'vue/return-in-emits-validator': 'error', | ||
'vue/use-v-on-exact': 'error', | ||
'vue/valid-attribute-name': 'error', | ||
'vue/valid-define-emits': 'error', | ||
'vue/valid-define-props': 'error', | ||
'vue/valid-model-definition': 'error', | ||
'vue/valid-next-tick': 'error', | ||
@@ -51,0 +63,0 @@ 'vue/valid-template-root': 'error', |
@@ -12,2 +12,3 @@ /* | ||
'vue/no-async-in-computed-properties': 'error', | ||
'vue/no-child-content': 'error', | ||
'vue/no-computed-properties-in-data': 'error', | ||
@@ -37,2 +38,3 @@ 'vue/no-deprecated-data-object-declaration': 'error', | ||
'vue/no-export-in-script-setup': 'error', | ||
'vue/no-expose-after-await': 'error', | ||
'vue/no-lifecycle-after-await': 'error', | ||
@@ -42,2 +44,3 @@ 'vue/no-mutating-props': 'error', | ||
'vue/no-ref-as-operand': 'error', | ||
'vue/no-reserved-component-names': 'error', | ||
'vue/no-reserved-keys': 'error', | ||
@@ -52,6 +55,9 @@ 'vue/no-reserved-props': 'error', | ||
'vue/no-unused-vars': 'error', | ||
'vue/no-use-computed-property-like-method': 'error', | ||
'vue/no-use-v-if-with-v-for': 'error', | ||
'vue/no-useless-template-attributes': 'error', | ||
'vue/no-v-for-template-key-on-child': 'error', | ||
'vue/no-v-text-v-html-on-component': 'error', | ||
'vue/no-watch-after-await': 'error', | ||
'vue/prefer-import-from-vue': 'error', | ||
'vue/require-component-is': 'error', | ||
@@ -67,2 +73,3 @@ 'vue/require-prop-type-constructor': 'error', | ||
'vue/use-v-on-exact': 'error', | ||
'vue/valid-attribute-name': 'error', | ||
'vue/valid-define-emits': 'error', | ||
@@ -69,0 +76,0 @@ 'vue/valid-define-props': 'error', |
@@ -34,3 +34,2 @@ /* | ||
eqeqeq: require('./rules/eqeqeq'), | ||
'experimental-script-setup-vars': require('./rules/experimental-script-setup-vars'), | ||
'first-attribute-linebreak': require('./rules/first-attribute-linebreak'), | ||
@@ -58,3 +57,2 @@ 'func-call-spacing': require('./rules/func-call-spacing'), | ||
'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'), | ||
'name-property-casing': require('./rules/name-property-casing'), | ||
'new-line-between-multi-line-property': require('./rules/new-line-between-multi-line-property'), | ||
@@ -68,3 +66,2 @@ 'next-tick-style': require('./rules/next-tick-style'), | ||
'no-computed-properties-in-data': require('./rules/no-computed-properties-in-data'), | ||
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), | ||
'no-constant-condition': require('./rules/no-constant-condition'), | ||
@@ -139,3 +136,2 @@ 'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'), | ||
'no-undef-properties': require('./rules/no-undef-properties'), | ||
'no-unregistered-components': require('./rules/no-unregistered-components'), | ||
'no-unsupported-features': require('./rules/no-unsupported-features'), | ||
@@ -207,4 +203,6 @@ 'no-unused-components': require('./rules/no-unused-components'), | ||
'v-slot-style': require('./rules/v-slot-style'), | ||
'valid-attribute-name': require('./rules/valid-attribute-name'), | ||
'valid-define-emits': require('./rules/valid-define-emits'), | ||
'valid-define-props': require('./rules/valid-define-props'), | ||
'valid-model-definition': require('./rules/valid-model-definition'), | ||
'valid-next-tick': require('./rules/valid-next-tick'), | ||
@@ -244,2 +242,4 @@ 'valid-template-root': require('./rules/valid-template-root'), | ||
environments: { | ||
// TODO Remove in the next major version | ||
/** @deprecated */ | ||
'setup-compiler-macros': { | ||
@@ -246,0 +246,0 @@ globals: { |
@@ -87,6 +87,6 @@ /** | ||
const disableDirectiveKeys = [] | ||
if (state.block.disableAllKeys.size) { | ||
if (state.block.disableAllKeys.size > 0) { | ||
disableDirectiveKeys.push(...state.block.disableAllKeys) | ||
} | ||
if (state.line.disableAllKeys.size) { | ||
if (state.line.disableAllKeys.size > 0) { | ||
disableDirectiveKeys.push(...state.line.disableAllKeys) | ||
@@ -105,3 +105,3 @@ } | ||
if (disableDirectiveKeys.length) { | ||
if (disableDirectiveKeys.length > 0) { | ||
// Store used eslint-disable comment key | ||
@@ -116,3 +116,3 @@ usedDisableDirectiveKeys.push(...disableDirectiveKeys) | ||
if (unusedDisableDirectiveReports.size) { | ||
if (unusedDisableDirectiveReports.size > 0) { | ||
for (const key of usedDisableDirectiveKeys) { | ||
@@ -119,0 +119,0 @@ // Remove used eslint-disable comments |
@@ -15,2 +15,22 @@ /** | ||
/** | ||
* @param {VDirective | VAttribute} node | ||
* @returns {string | null} | ||
*/ | ||
function getAttributeName(node) { | ||
if (!node.directive) { | ||
return node.key.rawName | ||
} | ||
if ( | ||
node.key.name.name === 'bind' && | ||
node.key.argument && | ||
node.key.argument.type === 'VIdentifier' | ||
) { | ||
return node.key.argument.rawName | ||
} | ||
return null | ||
} | ||
module.exports = { | ||
@@ -56,8 +76,6 @@ meta: { | ||
const useHyphenated = option !== 'never' | ||
let ignoredAttributes = ['data-', 'aria-', 'slot-scope'].concat( | ||
svgAttributes | ||
) | ||
const ignoredAttributes = ['data-', 'aria-', 'slot-scope', ...svgAttributes] | ||
if (optionsPayload && optionsPayload.ignore) { | ||
ignoredAttributes = ignoredAttributes.concat(optionsPayload.ignore) | ||
ignoredAttributes.push(...optionsPayload.ignore) | ||
} | ||
@@ -94,5 +112,3 @@ | ||
function isIgnoredAttribute(value) { | ||
const isIgnored = ignoredAttributes.some((attr) => { | ||
return value.includes(attr) | ||
}) | ||
const isIgnored = ignoredAttributes.some((attr) => value.includes(attr)) | ||
@@ -118,10 +134,4 @@ if (isIgnored) { | ||
const name = !node.directive | ||
? node.key.rawName | ||
: node.key.name.name === 'bind' | ||
? node.key.argument && | ||
node.key.argument.type === 'VIdentifier' && | ||
node.key.argument.rawName | ||
: /* otherwise */ false | ||
if (!name || isIgnoredAttribute(name)) return | ||
const name = getAttributeName(node) | ||
if (name === null || isIgnoredAttribute(name)) return | ||
@@ -128,0 +138,0 @@ reportIssue(node, name) |
@@ -114,26 +114,27 @@ /** | ||
const name = attribute.key.name.name | ||
if (name === 'for') { | ||
return ATTRS.LIST_RENDERING | ||
} else if ( | ||
name === 'if' || | ||
name === 'else-if' || | ||
name === 'else' || | ||
name === 'show' || | ||
name === 'cloak' | ||
) { | ||
return ATTRS.CONDITIONALS | ||
} else if (name === 'pre' || name === 'once') { | ||
return ATTRS.RENDER_MODIFIERS | ||
} else if (name === 'model') { | ||
return ATTRS.TWO_WAY_BINDING | ||
} else if (name === 'on') { | ||
return ATTRS.EVENTS | ||
} else if (name === 'html' || name === 'text') { | ||
return ATTRS.CONTENT | ||
} else if (name === 'slot') { | ||
return ATTRS.SLOT | ||
} else if (name === 'is') { | ||
return ATTRS.DEFINITION | ||
} else { | ||
return ATTRS.OTHER_DIRECTIVES | ||
switch (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 | ||
} | ||
@@ -148,12 +149,15 @@ } | ||
} | ||
if (propName === 'is') { | ||
return ATTRS.DEFINITION | ||
} else if (propName === 'id') { | ||
return ATTRS.GLOBAL | ||
} else if (propName === 'ref' || propName === 'key') { | ||
return ATTRS.UNIQUE | ||
} else if (propName === 'slot' || propName === 'slot-scope') { | ||
return ATTRS.SLOT | ||
} else { | ||
return ATTRS.OTHER_ATTR | ||
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: | ||
return ATTRS.OTHER_ATTR | ||
} | ||
@@ -218,3 +222,3 @@ } | ||
const attributePosition = {} | ||
attributeOrder.forEach((item, i) => { | ||
for (const [i, item] of attributeOrder.entries()) { | ||
if (Array.isArray(item)) { | ||
@@ -225,3 +229,3 @@ for (const attr of item) { | ||
} else attributePosition[item] = i | ||
}) | ||
} | ||
@@ -326,4 +330,3 @@ /** | ||
const results = [] | ||
for (let index = 0; index < attributes.length; index++) { | ||
const attr = attributes[index] | ||
for (const [index, attr] of attributes.entries()) { | ||
const position = getPositionFromAttrIndex(index) | ||
@@ -330,0 +333,0 @@ if (position == null) { |
@@ -55,9 +55,17 @@ /** | ||
* @param {string} blockName The block name. | ||
* @param { UserBlockOptions } option An option to parse. | ||
* @param {UserBlockOptions} option An option to parse. | ||
* @returns {BlockOptions} Normalized option. | ||
*/ | ||
function normalizeOption(blockName, option) { | ||
const lang = new Set( | ||
Array.isArray(option.lang) ? option.lang : option.lang ? [option.lang] : [] | ||
) | ||
/** @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 = new Set() | ||
} | ||
let hasDefault = false | ||
@@ -169,4 +177,3 @@ for (const def of DEFAULT_LANGUAGES[blockName] || []) { | ||
) | ||
if (!Object.keys(options).length) { | ||
// empty | ||
if (Object.keys(options).length === 0) { | ||
return {} | ||
@@ -203,7 +210,5 @@ } | ||
} else if (option.lang.size === 0) { | ||
if ((DEFAULT_LANGUAGES[tag] || []).includes(lang.value.value)) { | ||
messageId = 'unexpectedDefault' | ||
} else { | ||
messageId = 'unexpected' | ||
} | ||
messageId = (DEFAULT_LANGUAGES[tag] || []).includes(lang.value.value) | ||
? 'unexpectedDefault' | ||
: 'unexpected' | ||
} else { | ||
@@ -210,0 +215,0 @@ messageId = 'useOrNot' |
@@ -260,5 +260,4 @@ /** | ||
const option = | ||
options.multiline === options.singleline | ||
? options.singleline | ||
: /[\n\r\u2028\u2029]/u.test(text.trim()) | ||
options.multiline !== options.singleline && | ||
/[\n\r\u2028\u2029]/u.test(text.trim()) | ||
? options.multiline | ||
@@ -352,9 +351,2 @@ : options.singleline | ||
/** | ||
* @returns {VElement[]} | ||
*/ | ||
function getTopLevelHTMLElements() { | ||
return documentFragment.children.filter(utils.isVElement) | ||
} | ||
return utils.defineTemplateBodyVisitor( | ||
@@ -370,4 +362,6 @@ context, | ||
for (const element of getTopLevelHTMLElements()) { | ||
verify(element) | ||
for (const element of documentFragment.children) { | ||
if (utils.isVElement(element)) { | ||
verify(element) | ||
} | ||
} | ||
@@ -374,0 +368,0 @@ } |
@@ -54,3 +54,3 @@ /** | ||
const rulesRe = /([^,\s]+)[,\s]*/g | ||
const rulesRe = /([^\s,]+)[\s,]*/g | ||
let startIndex = match[0].length | ||
@@ -130,31 +130,31 @@ rulesRe.lastIndex = startIndex | ||
const parsed = parse(COMMENT_DIRECTIVE_B, comment.value) | ||
if (parsed != null) { | ||
if (parsed.type === 'eslint-disable') { | ||
if (parsed.rules.length) { | ||
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) | ||
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 { | ||
if (parsed.rules.length) { | ||
for (const rule of parsed.rules) { | ||
enable(context, comment.loc.start, 'block', rule.ruleId) | ||
} | ||
} else { | ||
enable(context, comment.loc.start, 'block', null) | ||
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) | ||
} | ||
@@ -178,3 +178,3 @@ } | ||
const column = -1 | ||
if (parsed.rules.length) { | ||
if (parsed.rules.length > 0) { | ||
const rules = reportUnusedDisableDirectives | ||
@@ -181,0 +181,0 @@ ? reportUnusedRules(context, comment, parsed.type, parsed.rules) |
@@ -46,13 +46,22 @@ /** | ||
for (const prefer of preferOptions) { | ||
if (prefer === 'script-setup') { | ||
opts.allowsSFC.scriptSetup = true | ||
} else if (prefer === 'composition') { | ||
opts.allowsSFC.composition = true | ||
opts.allowsOther.composition = true | ||
} else if (prefer === 'composition-vue2') { | ||
opts.allowsSFC.compositionVue2 = true | ||
opts.allowsOther.compositionVue2 = true | ||
} else if (prefer === 'options') { | ||
opts.allowsSFC.options = true | ||
opts.allowsOther.options = true | ||
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 | ||
} | ||
} | ||
@@ -184,5 +193,5 @@ } | ||
function buildOptionPhrase(name) { | ||
return LIFECYCLE_HOOK_OPTIONS.has(name) | ||
? `\`${name}\` lifecycle hook` | ||
: name === 'setup' || name === 'render' | ||
if (LIFECYCLE_HOOK_OPTIONS.has(name)) return `\`${name}\` lifecycle hook` | ||
return name === 'setup' || name === 'render' | ||
? `\`${name}\` function` | ||
@@ -189,0 +198,0 @@ : `\`${name}\` option` |
@@ -15,2 +15,15 @@ /** | ||
/** | ||
* @param {Expression | SpreadElement} node | ||
* @returns {node is (Literal | TemplateLiteral)} | ||
*/ | ||
function canConvert(node) { | ||
return ( | ||
node.type === 'Literal' || | ||
(node.type === 'TemplateLiteral' && | ||
node.expressions.length === 0 && | ||
node.quasis.length === 1) | ||
) | ||
} | ||
module.exports = { | ||
@@ -34,4 +47,5 @@ meta: { | ||
const options = context.options[0] | ||
const caseType = | ||
allowedCaseOptions.indexOf(options) !== -1 ? options : 'PascalCase' | ||
const caseType = allowedCaseOptions.includes(options) | ||
? options | ||
: 'PascalCase' | ||
@@ -76,15 +90,2 @@ // ---------------------------------------------------------------------- | ||
/** | ||
* @param {Expression | SpreadElement} node | ||
* @returns {node is (Literal | TemplateLiteral)} | ||
*/ | ||
function canConvert(node) { | ||
return ( | ||
node.type === 'Literal' || | ||
(node.type === 'TemplateLiteral' && | ||
node.expressions.length === 0 && | ||
node.quasis.length === 1) | ||
) | ||
} | ||
return Object.assign( | ||
@@ -91,0 +92,0 @@ {}, |
@@ -61,4 +61,5 @@ /** | ||
const options = context.options[1] || {} | ||
const caseType = | ||
allowedCaseOptions.indexOf(caseOption) !== -1 ? caseOption : defaultCase | ||
const caseType = allowedCaseOptions.includes(caseOption) | ||
? caseOption | ||
: defaultCase | ||
/** @type {RegExp[]} */ | ||
@@ -65,0 +66,0 @@ const ignores = (options.ignores || []).map(toRegExp) |
@@ -67,5 +67,5 @@ /** | ||
node.value.properties.forEach((property) => { | ||
for (const property of node.value.properties) { | ||
if (property.type !== 'Property') { | ||
return | ||
continue | ||
} | ||
@@ -75,3 +75,3 @@ | ||
if (!name || checkCase(name)) { | ||
return | ||
continue | ||
} | ||
@@ -114,5 +114,5 @@ | ||
}) | ||
}) | ||
} | ||
}) | ||
} | ||
} |
@@ -28,2 +28,22 @@ /** | ||
/** | ||
* @param {VElement} element | ||
* @return {string} | ||
*/ | ||
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(' ') | ||
} | ||
module.exports = { | ||
@@ -78,3 +98,3 @@ meta: { | ||
(context.options[0] && context.options[0].order) || DEFAULT_ORDER | ||
orderOptions.forEach((selectorOrSelectors, index) => { | ||
for (const [index, selectorOrSelectors] of orderOptions.entries()) { | ||
if (Array.isArray(selectorOrSelectors)) { | ||
@@ -95,22 +115,2 @@ for (const selector of selectorOrSelectors) { | ||
} | ||
}) | ||
/** | ||
* @param {VElement} element | ||
* @return {string} | ||
*/ | ||
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(' ') | ||
} | ||
@@ -142,3 +142,3 @@ | ||
const elementWithOrders = elements.flatMap((element) => { | ||
const elementsWithOrder = elements.flatMap((element) => { | ||
const order = getOrderElement(element) | ||
@@ -148,4 +148,5 @@ return order ? [{ order, element }] : [] | ||
const sourceCode = context.getSourceCode() | ||
elementWithOrders.forEach(({ order: expected, element }, index) => { | ||
const firstUnordered = elementWithOrders | ||
for (const [index, elementWithOrders] of elementsWithOrder.entries()) { | ||
const { order: expected, element } = elementWithOrders | ||
const firstUnordered = elementsWithOrder | ||
.slice(0, index) | ||
@@ -196,3 +197,3 @@ .filter(({ order }) => expected.index < order.index) | ||
} | ||
}) | ||
} | ||
} | ||
@@ -199,0 +200,0 @@ } |
@@ -25,3 +25,3 @@ /** | ||
const ALLOWED_CASE_OPTIONS = ['kebab-case', 'camelCase'] | ||
const DEFAULT_CASE = 'kebab-case' | ||
const DEFAULT_CASE = 'camelCase' | ||
@@ -28,0 +28,0 @@ /** |
@@ -235,3 +235,2 @@ /** | ||
}) | ||
const textSpace = getTextBetweenTokens(beforeTargetToken, targetComment) | ||
// make insert text: comments + node + space before target | ||
@@ -242,3 +241,3 @@ const textNode = sourceCode.getText( | ||
) | ||
const insertText = textNode + textSpace | ||
const insertText = getInsertText(textNode, target) | ||
@@ -252,7 +251,18 @@ return [ | ||
/** | ||
* @param {ASTNode} tokenBefore | ||
* @param {ASTNode} tokenAfter | ||
* Get result text to insert | ||
* @param {string} textNode | ||
* @param {ASTNode} target | ||
*/ | ||
function getTextBetweenTokens(tokenBefore, tokenAfter) { | ||
return sourceCode.text.slice(tokenBefore.range[1], tokenAfter.range[0]) | ||
function getInsertText(textNode, target) { | ||
const afterTargetComment = sourceCode.getTokenAfter(target, { | ||
includeComments: true | ||
}) | ||
const afterText = sourceCode.text.slice( | ||
target.range[1], | ||
afterTargetComment.range[0] | ||
) | ||
// handle case when a();b() -> b()a(); | ||
const invalidResult = !textNode.endsWith(';') && !afterText.includes('\n') | ||
return textNode + afterText + (invalidResult ? ';' : '') | ||
} | ||
@@ -262,4 +272,4 @@ | ||
* Get position of the beginning of the token's line(or prevToken end if no line) | ||
* @param {ASTNode} token | ||
* @param {ASTNode} prevToken | ||
* @param {ASTNode|Token} token | ||
* @param {ASTNode|Token} prevToken | ||
*/ | ||
@@ -266,0 +276,0 @@ function getLineStartIndex(token, prevToken) { |
@@ -17,2 +17,11 @@ /** | ||
/** | ||
* | ||
* @param {string} type | ||
* @returns {type is 'button' | 'submit' | 'reset'} | ||
*/ | ||
function isButtonType(type) { | ||
return type === 'button' || type === 'submit' || type === 'reset' | ||
} | ||
const optionDefaults = { | ||
@@ -79,10 +88,2 @@ button: true, | ||
/** | ||
* | ||
* @param {string} type | ||
* @returns {type is 'button' | 'submit' | 'reset'} | ||
*/ | ||
function isButtonType(type) { | ||
return type === 'button' || type === 'submit' || type === 'reset' | ||
} | ||
/** | ||
* @param {ASTNode} node | ||
@@ -89,0 +90,0 @@ * @param {string} messageId |
@@ -71,3 +71,3 @@ /** | ||
return utils.defineTemplateBodyVisitor(context, { | ||
return utils.defineDocumentVisitor(context, { | ||
/** @param {VStartTag | VEndTag} node */ | ||
@@ -74,0 +74,0 @@ 'VStartTag, VEndTag'(node) { |
@@ -95,3 +95,3 @@ /** | ||
return utils.defineTemplateBodyVisitor(context, { | ||
return utils.defineDocumentVisitor(context, { | ||
/** @param {VStartTag | VEndTag} node */ | ||
@@ -98,0 +98,0 @@ 'VStartTag, VEndTag'(node) { |
@@ -49,6 +49,4 @@ /** | ||
const char = s[0] | ||
if (char === ' ' || char === '\t') { | ||
if (s.split('').every((c) => c === char)) { | ||
return `${s.length} ${toUnit(char)}${s.length === 1 ? '' : 's'}` | ||
} | ||
if ((char === ' ' || char === '\t') && [...s].every((c) => c === char)) { | ||
return `${s.length} ${toUnit(char)}${s.length === 1 ? '' : 's'}` | ||
} | ||
@@ -222,4 +220,4 @@ | ||
// validate indent charctor | ||
for (let i = 0; i < actualOffsetIndentText.length; ++i) { | ||
if (actualOffsetIndentText[i] !== options.indentChar) { | ||
for (const [i, char] of [...actualOffsetIndentText].entries()) { | ||
if (char !== options.indentChar) { | ||
context.report({ | ||
@@ -233,3 +231,3 @@ loc: { | ||
expected: toUnit(options.indentChar), | ||
actual: toUnit(actualOffsetIndentText[i]) | ||
actual: toUnit(char) | ||
}, | ||
@@ -236,0 +234,0 @@ fix: defineFix(line, actualIndentText, expectedIndentText) |
@@ -79,8 +79,8 @@ /** | ||
const fixToDouble = | ||
avoidEscape && !quoted && contentText.includes(quoteChar) | ||
? double | ||
? contentText.includes("'") | ||
: !contentText.includes('"') | ||
: double | ||
let fixToDouble = double | ||
if (avoidEscape && !quoted && contentText.includes(quoteChar)) { | ||
fixToDouble = double | ||
? contentText.includes("'") | ||
: !contentText.includes('"') | ||
} | ||
@@ -87,0 +87,0 @@ const quotePattern = fixToDouble ? /"/g : /'/g |
@@ -19,4 +19,19 @@ /** | ||
/** | ||
* @param {Expression | SpreadElement} node | ||
* @returns {node is (Literal | TemplateLiteral)} | ||
*/ | ||
function canVerify(node) { | ||
return ( | ||
node.type === 'Literal' || | ||
(node.type === 'TemplateLiteral' && | ||
node.expressions.length === 0 && | ||
node.quasis.length === 1) | ||
) | ||
} | ||
module.exports = { | ||
meta: { | ||
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions | ||
hasSuggestions: true, | ||
type: 'suggestion', | ||
@@ -105,3 +120,13 @@ docs: { | ||
'Component name `{{name}}` should match file name `{{filename}}`.', | ||
data: { filename, name } | ||
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}`) | ||
} | ||
} | ||
] | ||
}) | ||
@@ -111,15 +136,2 @@ } | ||
/** | ||
* @param {Expression | SpreadElement} node | ||
* @returns {node is (Literal | TemplateLiteral)} | ||
*/ | ||
function canVerify(node) { | ||
return ( | ||
node.type === 'Literal' || | ||
(node.type === 'TemplateLiteral' && | ||
node.expressions.length === 0 && | ||
node.quasis.length === 1) | ||
) | ||
} | ||
return Object.assign( | ||
@@ -149,3 +161,3 @@ {}, | ||
errors.forEach((error) => context.report(error)) | ||
for (const error of errors) context.report(error) | ||
} | ||
@@ -152,0 +164,0 @@ } |
@@ -10,2 +10,10 @@ /** | ||
/** | ||
* @param {Identifier} identifier | ||
* @return {Array<String>} | ||
*/ | ||
function getExpectedNames(identifier) { | ||
return [casing.pascalCase(identifier.name), casing.kebabCase(identifier.name)] | ||
} | ||
module.exports = { | ||
@@ -32,13 +40,2 @@ meta: { | ||
create(context) { | ||
/** | ||
* @param {Identifier} identifier | ||
* @return {Array<String>} | ||
*/ | ||
function getExpectedNames(identifier) { | ||
return [ | ||
casing.pascalCase(identifier.name), | ||
casing.kebabCase(identifier.name) | ||
] | ||
} | ||
return utils.executeOnVueComponent(context, (obj) => { | ||
@@ -54,29 +51,26 @@ const components = utils.findProperty(obj, 'components') | ||
components.value.properties.forEach( | ||
/** @param {Property | SpreadElement} property */ | ||
(property) => { | ||
if ( | ||
property.type === 'SpreadElement' || | ||
property.value.type !== 'Identifier' || | ||
property.computed === true | ||
) { | ||
return | ||
} | ||
for (const property of components.value.properties) { | ||
if ( | ||
property.type === 'SpreadElement' || | ||
property.value.type !== 'Identifier' || | ||
property.computed === true | ||
) { | ||
continue | ||
} | ||
const importedName = utils.getStaticPropertyName(property) || '' | ||
const expectedNames = getExpectedNames(property.value) | ||
if (!expectedNames.includes(importedName)) { | ||
context.report({ | ||
node: property, | ||
messageId: 'unexpected', | ||
data: { | ||
importedName, | ||
expectedName: expectedNames.join(', ') | ||
} | ||
}) | ||
} | ||
const importedName = utils.getStaticPropertyName(property) || '' | ||
const expectedNames = getExpectedNames(property.value) | ||
if (!expectedNames.includes(importedName)) { | ||
context.report({ | ||
node: property, | ||
messageId: 'unexpected', | ||
data: { | ||
importedName, | ||
expectedName: expectedNames.join(', ') | ||
} | ||
}) | ||
} | ||
) | ||
} | ||
}) | ||
} | ||
} |
@@ -12,2 +12,56 @@ /** | ||
/** | ||
* @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[propsPerLine.length - 1].push(current) | ||
} else { | ||
propsPerLine.push([current]) | ||
} | ||
} | ||
return propsPerLine | ||
} | ||
module.exports = { | ||
@@ -83,12 +137,15 @@ meta: { | ||
if (utils.isSingleLine(node)) { | ||
if (numberOfAttributes > singlelinemMaximum) { | ||
showErrors(node.attributes.slice(singlelinemMaximum)) | ||
} | ||
if ( | ||
utils.isSingleLine(node) && | ||
numberOfAttributes > singlelinemMaximum | ||
) { | ||
showErrors(node.attributes.slice(singlelinemMaximum)) | ||
} | ||
if (!utils.isSingleLine(node)) { | ||
groupAttrsByLine(node.attributes) | ||
.filter((attrs) => attrs.length > multilineMaximum) | ||
.forEach((attrs) => showErrors(attrs.splice(multilineMaximum))) | ||
for (const attrs of groupAttrsByLine(node.attributes)) { | ||
if (attrs.length > multilineMaximum) { | ||
showErrors(attrs.splice(multilineMaximum)) | ||
} | ||
} | ||
} | ||
@@ -101,34 +158,3 @@ } | ||
// ---------------------------------------------------------------------- | ||
/** | ||
* @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') { | ||
if (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') { | ||
if (typeof options.multiline.max === 'number') { | ||
defaults.multiline = options.multiline.max | ||
} | ||
} | ||
} | ||
} | ||
return defaults | ||
} | ||
/** | ||
@@ -138,3 +164,3 @@ * @param {(VDirective | VAttribute)[]} attributes | ||
function showErrors(attributes) { | ||
attributes.forEach((prop, i) => { | ||
for (const [i, prop] of attributes.entries()) { | ||
context.report({ | ||
@@ -162,23 +188,5 @@ node: prop, | ||
}) | ||
}) | ||
} | ||
} | ||
/** | ||
* @param {(VDirective | VAttribute)[]} attributes | ||
*/ | ||
function groupAttrsByLine(attributes) { | ||
const propsPerLine = [[attributes[0]]] | ||
attributes.reduce((previous, current) => { | ||
if (previous.loc.end.line === current.loc.start.line) { | ||
propsPerLine[propsPerLine.length - 1].push(current) | ||
} else { | ||
propsPerLine.push([current]) | ||
} | ||
return current | ||
}) | ||
return propsPerLine | ||
} | ||
} | ||
} |
@@ -102,3 +102,3 @@ /** | ||
return Array.from(line).length + extraCharacterCount | ||
return [...line].length + extraCharacterCount | ||
} | ||
@@ -161,32 +161,22 @@ | ||
/** | ||
* Ensure that an array exists at [key] on `object`, and add `value` to it. | ||
* Group AST nodes by line number, both start and end. | ||
* | ||
* @param { { [key: number]: Token[] } } object the object to mutate | ||
* @param {number} key the object's key | ||
* @param {Token} value the value to add | ||
* @returns {void} | ||
* @param {Token[]} nodes the AST nodes in question | ||
* @returns { { [key: number]: Token[] } } the grouped nodes | ||
* @private | ||
*/ | ||
function ensureArrayAndPush(object, key, value) { | ||
if (!Array.isArray(object[key])) { | ||
object[key] = [] | ||
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) | ||
} | ||
} | ||
object[key].push(value) | ||
return grouped | ||
} | ||
/** | ||
* A reducer to group an AST node by line number, both start and end. | ||
* | ||
* @param { { [key: number]: Token[] } } acc the accumulator | ||
* @param {Token} node the AST node in question | ||
* @returns { { [key: number]: Token[] } } the modified accumulator | ||
* @private | ||
*/ | ||
function groupByLineNumber(acc, node) { | ||
for (let i = node.loc.start.line; i <= node.loc.end.line; ++i) { | ||
ensureArrayAndPush(acc, i, node) | ||
} | ||
return acc | ||
} | ||
// ------------------------------------------------------------------------------ | ||
@@ -371,23 +361,22 @@ // Rule Definition | ||
/** @type {Range} */ | ||
/** @type {Range | undefined} */ | ||
let scriptLinesRange | ||
if (scriptTokens.length) { | ||
if (scriptComments.length) { | ||
scriptLinesRange = [ | ||
Math.min( | ||
scriptTokens[0].loc.start.line, | ||
scriptComments[0].loc.start.line | ||
), | ||
Math.max( | ||
scriptTokens[scriptTokens.length - 1].loc.end.line, | ||
scriptComments[scriptComments.length - 1].loc.end.line | ||
) | ||
] | ||
} else { | ||
scriptLinesRange = [ | ||
scriptTokens[0].loc.start.line, | ||
scriptTokens[scriptTokens.length - 1].loc.end.line | ||
] | ||
} | ||
} else if (scriptComments.length) { | ||
if (scriptTokens.length > 0) { | ||
scriptLinesRange = | ||
scriptComments.length > 0 | ||
? [ | ||
Math.min( | ||
scriptTokens[0].loc.start.line, | ||
scriptComments[0].loc.start.line | ||
), | ||
Math.max( | ||
scriptTokens[scriptTokens.length - 1].loc.end.line, | ||
scriptComments[scriptComments.length - 1].loc.end.line | ||
) | ||
] | ||
: [ | ||
scriptTokens[0].loc.start.line, | ||
scriptTokens[scriptTokens.length - 1].loc.end.line | ||
] | ||
} else if (scriptComments.length > 0) { | ||
scriptLinesRange = [ | ||
@@ -407,27 +396,18 @@ scriptComments[0].loc.start.line, | ||
const strings = getAllStrings() | ||
const stringsByLine = strings.reduce(groupByLineNumber, {}) | ||
const stringsByLine = groupByLineNumber(strings) | ||
const templateLiterals = getAllTemplateLiterals() | ||
const templateLiteralsByLine = templateLiterals.reduce( | ||
groupByLineNumber, | ||
{} | ||
) | ||
const templateLiteralsByLine = groupByLineNumber(templateLiterals) | ||
const regExpLiterals = getAllRegExpLiterals() | ||
const regExpLiteralsByLine = regExpLiterals.reduce(groupByLineNumber, {}) | ||
const regExpLiteralsByLine = groupByLineNumber(regExpLiterals) | ||
const htmlAttributeValuesByLine = htmlAttributeValues.reduce( | ||
groupByLineNumber, | ||
{} | ||
) | ||
const htmlAttributeValuesByLine = groupByLineNumber(htmlAttributeValues) | ||
const htmlTextContents = getAllHTMLTextContents() | ||
const htmlTextContentsByLine = htmlTextContents.reduce( | ||
groupByLineNumber, | ||
{} | ||
) | ||
const htmlTextContentsByLine = groupByLineNumber(htmlTextContents) | ||
const commentsByLine = comments.reduce(groupByLineNumber, {}) | ||
const commentsByLine = groupByLineNumber(comments) | ||
lines.forEach((line, i) => { | ||
for (const [i, line] of lines.entries()) { | ||
// i is zero-indexed, line numbers are one-indexed | ||
@@ -447,10 +427,8 @@ const lineNumber = i + 1 | ||
// out of range. | ||
return | ||
continue | ||
} | ||
const maxLength = | ||
inScript && inTemplate | ||
? Math.max(scriptMaxLength, templateMaxLength) | ||
: inScript | ||
? scriptMaxLength | ||
: templateMaxLength | ||
const maxLength = Math.max( | ||
inScript ? scriptMaxLength : 0, | ||
inTemplate ? templateMaxLength : 0 | ||
) | ||
@@ -466,3 +444,3 @@ if ( | ||
// ignore this line | ||
return | ||
continue | ||
} | ||
@@ -512,3 +490,3 @@ | ||
// ignore this line | ||
return | ||
continue | ||
} | ||
@@ -520,3 +498,3 @@ | ||
if (lineIsComment && ignoreComments) { | ||
return | ||
continue | ||
} | ||
@@ -547,3 +525,3 @@ | ||
} | ||
}) | ||
} | ||
} | ||
@@ -550,0 +528,0 @@ |
@@ -32,3 +32,3 @@ /** | ||
{ | ||
ignores: ['pre', 'textarea'].concat(INLINE_ELEMENTS), | ||
ignores: ['pre', 'textarea', ...INLINE_ELEMENTS], | ||
ignoreWhenEmpty: true, | ||
@@ -135,7 +135,3 @@ allowEmptyLines: false | ||
function isInvalidLineBreaks(lineBreaks) { | ||
if (allowEmptyLines) { | ||
return lineBreaks === 0 | ||
} else { | ||
return lineBreaks !== 1 | ||
} | ||
return allowEmptyLines ? lineBreaks === 0 : lineBreaks !== 1 | ||
} | ||
@@ -142,0 +138,0 @@ |
@@ -103,3 +103,3 @@ /** | ||
ObjectExpression(node) { | ||
if (callStack.length) { | ||
if (callStack.length > 0) { | ||
return | ||
@@ -106,0 +106,0 @@ } |
@@ -131,3 +131,3 @@ /** | ||
if ( | ||
callExpression.arguments.length !== 0 || | ||
callExpression.arguments.length > 0 || | ||
!isAwaitedPromise(callExpression) | ||
@@ -134,0 +134,0 @@ ) { |
@@ -46,3 +46,3 @@ /** | ||
'>', | ||
'\u00b7', // "·" | ||
'\u00B7', // "·" | ||
'\u2022', // "•" | ||
@@ -49,0 +49,0 @@ '\u2010', // "‐" |
@@ -82,3 +82,3 @@ /** | ||
"disallow element's child contents which would be overwritten by a directive like `v-html` or `v-text`", | ||
categories: undefined, | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-child-content.html' | ||
@@ -141,3 +141,2 @@ }, | ||
directives.has(directiveName) && | ||
childNodes.length > 0 && | ||
childNodes.some((childNode) => !isWhiteSpaceTextNode(childNode)) | ||
@@ -144,0 +143,0 @@ ) { |
@@ -17,2 +17,26 @@ /** | ||
/** | ||
* @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 = { | ||
@@ -61,28 +85,4 @@ meta: { | ||
} | ||
/** | ||
* @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) | ||
} | ||
}) | ||
} | ||
} |
@@ -17,2 +17,23 @@ /** | ||
/** | ||
* @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 = { | ||
@@ -78,22 +99,2 @@ meta: { | ||
/** | ||
* @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 | ||
} | ||
return utils.defineVueVisitor(context, { | ||
@@ -100,0 +101,0 @@ onVueObjectEnter(node) { |
@@ -26,7 +26,8 @@ /** | ||
return components.reduce((prev, curr) => { | ||
prev.add(casing.kebabCase(curr)) | ||
prev.add(casing.pascalCase(curr)) | ||
return prev | ||
}, new Set()) | ||
return new Set( | ||
components.flatMap((component) => [ | ||
casing.kebabCase(component), | ||
casing.pascalCase(component) | ||
]) | ||
) | ||
} | ||
@@ -33,0 +34,0 @@ |
@@ -40,7 +40,7 @@ /** | ||
const modifier = node.modifiers.find((mod) => | ||
Number.isInteger(parseInt(mod.name, 10)) | ||
Number.isInteger(Number.parseInt(mod.name, 10)) | ||
) | ||
if (!modifier) return | ||
const keyCodes = parseInt(modifier.name, 10) | ||
const keyCodes = Number.parseInt(modifier.name, 10) | ||
if (keyCodes > 9 || keyCodes < 0) { | ||
@@ -47,0 +47,0 @@ context.report({ |
@@ -43,3 +43,3 @@ /** | ||
const options = context.options[0] || {} | ||
const groups = new Set(GROUP_NAMES.concat(options.groups || [])) | ||
const groups = new Set([...GROUP_NAMES, ...(options.groups || [])]) | ||
@@ -51,7 +51,8 @@ // ---------------------------------------------------------------------- | ||
return utils.executeOnVue(context, (obj) => { | ||
const usedNames = [] | ||
/** @type {Set<string>} */ | ||
const usedNames = new Set() | ||
const properties = utils.iterateProperties(obj, groups) | ||
for (const o of properties) { | ||
if (usedNames.indexOf(o.name) !== -1) { | ||
if (usedNames.has(o.name)) { | ||
context.report({ | ||
@@ -66,3 +67,3 @@ node: o.node, | ||
usedNames.push(o.name) | ||
usedNames.add(o.name) | ||
} | ||
@@ -69,0 +70,0 @@ }) |
@@ -170,9 +170,8 @@ /** | ||
const operands = (condition.operands = condition.operands.filter( | ||
(orOperand) => { | ||
return !currentOrOperands.operands.some((currentOrOperand) => | ||
(orOperand) => | ||
!currentOrOperands.operands.some((currentOrOperand) => | ||
isSubset(currentOrOperand, orOperand) | ||
) | ||
} | ||
)) | ||
if (!operands.length) { | ||
if (operands.length === 0) { | ||
context.report({ | ||
@@ -179,0 +178,0 @@ node: condition.node, |
@@ -24,10 +24,9 @@ /** | ||
const hasSrc = | ||
componentBlock.startTag.attributes.filter( | ||
(attribute) => | ||
!attribute.directive && | ||
attribute.key.name === 'src' && | ||
attribute.value && | ||
attribute.value.value !== '' | ||
).length > 0 | ||
const hasSrc = componentBlock.startTag.attributes.some( | ||
(attribute) => | ||
!attribute.directive && | ||
attribute.key.name === 'src' && | ||
attribute.value && | ||
attribute.value.value !== '' | ||
) | ||
@@ -34,0 +33,0 @@ return hasAttribute && hasSrc |
@@ -20,3 +20,3 @@ /** | ||
description: 'disallow `export` in `<script setup>`', | ||
categories: ['vue3-essential'], | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-export-in-script-setup.html' | ||
@@ -41,9 +41,8 @@ }, | ||
} | ||
if (tsNode.type === 'ExportNamedDeclaration') { | ||
if ( | ||
tsNode.specifiers.length > 0 && | ||
tsNode.specifiers.every((spec) => spec.exportKind === 'type') | ||
) { | ||
return | ||
} | ||
if ( | ||
tsNode.type === 'ExportNamedDeclaration' && | ||
tsNode.specifiers.length > 0 && | ||
tsNode.specifiers.every((spec) => spec.exportKind === 'type') | ||
) { | ||
return | ||
} | ||
@@ -50,0 +49,0 @@ context.report({ |
@@ -43,4 +43,3 @@ /** | ||
description: 'disallow asynchronously registered `expose`', | ||
categories: undefined, | ||
// categories: ['vue3-essential'], TODO Change with the major version | ||
categories: ['vue3-essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-expose-after-await.html' | ||
@@ -51,3 +50,3 @@ }, | ||
messages: { | ||
forbidden: 'The `expose` after `await` expression are forbidden.' | ||
forbidden: '`{{name}}` is forbidden after an `await` expression.' | ||
} | ||
@@ -61,4 +60,4 @@ }, | ||
* @property {[number,number]} range | ||
* @property {Set<Identifier>} exposeReferenceIds | ||
* @property {Set<Identifier>} contextReferenceIds | ||
* @property {(node: Identifier, callNode: CallExpression) => boolean} isExposeReferenceId | ||
* @property {(node: Identifier) => boolean} isContextReferenceId | ||
*/ | ||
@@ -68,5 +67,5 @@ /** | ||
* @property {ScopeStack | null} upper | ||
* @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} scopeNode | ||
* @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program} scopeNode | ||
*/ | ||
/** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression, SetupScopeData>} */ | ||
/** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program, SetupScopeData>} */ | ||
const setupScopes = new Map() | ||
@@ -77,131 +76,176 @@ | ||
return utils.defineVueVisitor(context, { | ||
onSetupFunctionEnter(node) { | ||
const contextParam = node.params[1] | ||
if (!contextParam) { | ||
// no arguments | ||
return | ||
return utils.compositingVisitors( | ||
{ | ||
/** | ||
* @param {Program} node | ||
*/ | ||
Program(node) { | ||
scopeStack = { | ||
upper: scopeStack, | ||
scopeNode: node | ||
} | ||
} | ||
if (contextParam.type === 'RestElement') { | ||
// cannot check | ||
return | ||
} | ||
if (contextParam.type === 'ArrayPattern') { | ||
// cannot check | ||
return | ||
} | ||
/** @type {Set<Identifier>} */ | ||
const contextReferenceIds = new Set() | ||
/** @type {Set<Identifier>} */ | ||
const exposeReferenceIds = new Set() | ||
if (contextParam.type === 'ObjectPattern') { | ||
const exposeProperty = utils.findAssignmentProperty( | ||
contextParam, | ||
'expose' | ||
) | ||
if (!exposeProperty) { | ||
}, | ||
{ | ||
/** | ||
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node | ||
*/ | ||
':function'(node) { | ||
scopeStack = { | ||
upper: scopeStack, | ||
scopeNode: node | ||
} | ||
}, | ||
':function:exit'() { | ||
scopeStack = scopeStack && scopeStack.upper | ||
}, | ||
/** @param {AwaitExpression} node */ | ||
AwaitExpression(node) { | ||
if (!scopeStack) { | ||
return | ||
} | ||
const exposeParam = exposeProperty.value | ||
// `setup(props, {emit})` | ||
const variable = | ||
exposeParam.type === 'Identifier' | ||
? findVariable(context.getScope(), exposeParam) | ||
: null | ||
if (!variable) { | ||
const setupScope = setupScopes.get(scopeStack.scopeNode) | ||
if (!setupScope || !utils.inRange(setupScope.range, node)) { | ||
return | ||
} | ||
for (const reference of variable.references) { | ||
if (!reference.isRead()) { | ||
continue | ||
} | ||
exposeReferenceIds.add(reference.identifier) | ||
setupScope.afterAwait = true | ||
}, | ||
/** @param {CallExpression} node */ | ||
CallExpression(node) { | ||
if (!scopeStack) { | ||
return | ||
} | ||
} else if (contextParam.type === 'Identifier') { | ||
// `setup(props, context)` | ||
const variable = findVariable(context.getScope(), contextParam) | ||
if (!variable) { | ||
const setupScope = setupScopes.get(scopeStack.scopeNode) | ||
if ( | ||
!setupScope || | ||
!setupScope.afterAwait || | ||
!utils.inRange(setupScope.range, node) | ||
) { | ||
return | ||
} | ||
for (const reference of variable.references) { | ||
if (!reference.isRead()) { | ||
continue | ||
const { isContextReferenceId, isExposeReferenceId } = setupScope | ||
if ( | ||
node.callee.type === 'Identifier' && | ||
isExposeReferenceId(node.callee, node) | ||
) { | ||
// setup(props,{expose}) {expose()} | ||
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) | ||
) { | ||
// setup(props,context) {context.emit()} | ||
context.report({ | ||
node, | ||
messageId: 'forbidden', | ||
data: { | ||
name: expose.name | ||
} | ||
}) | ||
} | ||
contextReferenceIds.add(reference.identifier) | ||
} | ||
} | ||
setupScopes.set(node, { | ||
afterAwait: false, | ||
range: node.range, | ||
exposeReferenceIds, | ||
contextReferenceIds | ||
}) | ||
}, | ||
/** | ||
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node | ||
*/ | ||
':function'(node) { | ||
scopeStack = { | ||
upper: scopeStack, | ||
scopeNode: node | ||
(() => { | ||
const scriptSetup = utils.getScriptSetupElement(context) | ||
if (!scriptSetup) { | ||
return {} | ||
} | ||
}, | ||
':function:exit'() { | ||
scopeStack = scopeStack && scopeStack.upper | ||
}, | ||
/** @param {AwaitExpression} node */ | ||
AwaitExpression(node) { | ||
if (!scopeStack) { | ||
return | ||
} | ||
const setupScope = setupScopes.get(scopeStack.scopeNode) | ||
if (!setupScope || !utils.inRange(setupScope.range, node)) { | ||
return | ||
} | ||
setupScope.afterAwait = true | ||
}, | ||
/** @param {CallExpression} node */ | ||
CallExpression(node) { | ||
if (!scopeStack) { | ||
return | ||
} | ||
const setupScope = setupScopes.get(scopeStack.scopeNode) | ||
if ( | ||
!setupScope || | ||
!setupScope.afterAwait || | ||
!utils.inRange(setupScope.range, node) | ||
) { | ||
return | ||
} | ||
const { contextReferenceIds, exposeReferenceIds } = setupScope | ||
if ( | ||
node.callee.type === 'Identifier' && | ||
exposeReferenceIds.has(node.callee) | ||
) { | ||
// setup(props,{expose}) {expose()} | ||
context.report({ | ||
node, | ||
messageId: 'forbidden' | ||
}) | ||
} else { | ||
const expose = getCalleeMemberNode(node) | ||
if ( | ||
expose && | ||
expose.name === 'expose' && | ||
expose.member.object.type === 'Identifier' && | ||
contextReferenceIds.has(expose.member.object) | ||
) { | ||
// setup(props,context) {context.emit()} | ||
context.report({ | ||
node, | ||
messageId: 'forbidden' | ||
return { | ||
/** | ||
* @param {Program} node | ||
*/ | ||
Program(node) { | ||
context | ||
.getScope() | ||
.references.find((ref) => ref.identifier.name === 'defineExpose') | ||
setupScopes.set(node, { | ||
afterAwait: false, | ||
range: scriptSetup.range, | ||
isExposeReferenceId: (_id, callNode) => | ||
callNode.parent.type === 'ExpressionStatement' && | ||
callNode.parent.parent === node, | ||
isContextReferenceId: () => false | ||
}) | ||
} | ||
} | ||
}, | ||
onSetupFunctionExit(node) { | ||
setupScopes.delete(node) | ||
} | ||
}) | ||
})(), | ||
utils.defineVueVisitor(context, { | ||
onSetupFunctionEnter(node) { | ||
const contextParam = node.params[1] | ||
if (!contextParam) { | ||
// no arguments | ||
return | ||
} | ||
if (contextParam.type === 'RestElement') { | ||
// cannot check | ||
return | ||
} | ||
if (contextParam.type === 'ArrayPattern') { | ||
// cannot check | ||
return | ||
} | ||
/** @type {Set<Identifier>} */ | ||
const contextReferenceIds = new Set() | ||
/** @type {Set<Identifier>} */ | ||
const exposeReferenceIds = new Set() | ||
if (contextParam.type === 'ObjectPattern') { | ||
const exposeProperty = utils.findAssignmentProperty( | ||
contextParam, | ||
'expose' | ||
) | ||
if (!exposeProperty) { | ||
return | ||
} | ||
const exposeParam = exposeProperty.value | ||
// `setup(props, {emit})` | ||
const variable = | ||
exposeParam.type === 'Identifier' | ||
? findVariable(context.getScope(), exposeParam) | ||
: null | ||
if (!variable) { | ||
return | ||
} | ||
for (const reference of variable.references) { | ||
if (!reference.isRead()) { | ||
continue | ||
} | ||
exposeReferenceIds.add(reference.identifier) | ||
} | ||
} else if (contextParam.type === 'Identifier') { | ||
// `setup(props, context)` | ||
const variable = findVariable(context.getScope(), 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) | ||
} | ||
}) | ||
) | ||
} | ||
} |
@@ -1,23 +0,16 @@ | ||
/** | ||
* @fileoverview Requires valid keys in model option. | ||
* @author Alex Sokolov | ||
*/ | ||
'use strict' | ||
const utils = require('../utils') | ||
const baseRule = require('./valid-model-definition') | ||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
const VALID_MODEL_KEYS = ['prop', 'event'] | ||
module.exports = { | ||
meta: { | ||
type: 'problem', | ||
...baseRule.meta, | ||
type: baseRule.meta.type, | ||
docs: { | ||
description: 'require valid keys in model option', | ||
description: baseRule.meta.docs.description, | ||
categories: undefined, | ||
url: 'https://eslint.vuejs.org/rules/no-invalid-model-keys.html' | ||
}, | ||
fixable: null, | ||
deprecated: true, | ||
replacedBy: ['valid-model-definition'], | ||
schema: [] | ||
@@ -27,32 +20,4 @@ }, | ||
create(context) { | ||
// ---------------------------------------------------------------------- | ||
// Public | ||
// ---------------------------------------------------------------------- | ||
return utils.executeOnVue(context, (obj) => { | ||
const modelProperty = utils.findProperty(obj, 'model') | ||
if (!modelProperty || modelProperty.value.type !== 'ObjectExpression') { | ||
return | ||
} | ||
for (const p of modelProperty.value.properties) { | ||
if (p.type !== 'Property') { | ||
continue | ||
} | ||
const name = utils.getStaticPropertyName(p) | ||
if (!name) { | ||
continue | ||
} | ||
if (VALID_MODEL_KEYS.indexOf(name) === -1) { | ||
context.report({ | ||
node: p, | ||
message: "Invalid key '{{name}}' in model option.", | ||
data: { | ||
name | ||
} | ||
}) | ||
} | ||
} | ||
}) | ||
return baseRule.create(context) | ||
} | ||
} |
@@ -19,5 +19,5 @@ /** | ||
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 | ||
/[\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 | ||
/[\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 | ||
@@ -121,7 +121,8 @@ | ||
if (shouldCheckStrings || shouldCheckRegExps) { | ||
// If we have irregular characters remove them from the errors list | ||
if (ALL_IRREGULARS.test(sourceCode.getText(node))) { | ||
removeWhitespaceError(node) | ||
} | ||
// If we have irregular characters, remove them from the errors list | ||
if ( | ||
(shouldCheckStrings || shouldCheckRegExps) && | ||
ALL_IRREGULARS.test(sourceCode.getText(node)) | ||
) { | ||
removeWhitespaceError(node) | ||
} | ||
@@ -196,3 +197,3 @@ } | ||
if (!errorIndexes.length) { | ||
if (errorIndexes.length === 0) { | ||
return {} | ||
@@ -230,5 +231,9 @@ } | ||
// First strip errors occurring in comment nodes. | ||
sourceCode.getAllComments().forEach(removeInvalidNodeErrorsInComment) | ||
for (const node of sourceCode.getAllComments()) { | ||
removeInvalidNodeErrorsInComment(node) | ||
} | ||
if (templateBody) { | ||
templateBody.comments.forEach(removeInvalidNodeErrorsInComment) | ||
for (const node of templateBody.comments) { | ||
removeInvalidNodeErrorsInComment(node) | ||
} | ||
} | ||
@@ -248,4 +253,4 @@ } | ||
// If we have any errors remaining report on them | ||
errorIndexes.forEach((errorIndex) => { | ||
// If we have any errors remaining, report on them | ||
for (const errorIndex of errorIndexes) { | ||
context.report({ | ||
@@ -255,3 +260,3 @@ loc: sourceCode.getLocFromIndex(errorIndex), | ||
}) | ||
}) | ||
} | ||
} | ||
@@ -258,0 +263,0 @@ } |
@@ -38,3 +38,3 @@ /** | ||
messages: { | ||
forbidden: 'The lifecycle hooks after `await` expression are forbidden.' | ||
forbidden: 'Lifecycle hooks are forbidden after an `await` expression.' | ||
} | ||
@@ -41,0 +41,0 @@ }, |
@@ -30,2 +30,23 @@ /** | ||
/** | ||
* @param {VAttribute | VDirective} attr | ||
*/ | ||
function getKeyName(attr) { | ||
if (attr.directive) { | ||
if (attr.key.name.name !== 'bind') { | ||
// no v-bind | ||
return null | ||
} | ||
if ( | ||
!attr.key.argument || | ||
attr.key.argument.type === 'VExpressionContainer' | ||
) { | ||
// unknown | ||
return null | ||
} | ||
return attr.key.argument.name | ||
} | ||
return attr.key.name | ||
} | ||
module.exports = { | ||
@@ -60,23 +81,2 @@ meta: { | ||
/** | ||
* @param {VAttribute | VDirective} attr | ||
*/ | ||
function getKeyName(attr) { | ||
if (attr.directive) { | ||
if (attr.key.name.name !== 'bind') { | ||
// no v-bind | ||
return null | ||
} | ||
if ( | ||
!attr.key.argument || | ||
attr.key.argument.type === 'VExpressionContainer' | ||
) { | ||
// unknown | ||
return null | ||
} | ||
return attr.key.argument.name | ||
} | ||
return attr.key.name | ||
} | ||
return utils.defineTemplateBodyVisitor(context, { | ||
@@ -83,0 +83,0 @@ /** @param {VStartTag} node */ |
@@ -65,3 +65,3 @@ /** | ||
if (!parent.arguments.length) { | ||
if (parent.arguments.length === 0) { | ||
return | ||
@@ -68,0 +68,0 @@ } |
@@ -42,2 +42,51 @@ /** | ||
/** | ||
* @param {ASTNode} node | ||
* @returns {VExpressionContainer} | ||
*/ | ||
function getVExpressionContainer(node) { | ||
let n = node | ||
while (n.type !== 'VExpressionContainer') { | ||
n = /** @type {ASTNode} */ (n.parent) | ||
} | ||
return n | ||
} | ||
/** | ||
* @param {ASTNode} node | ||
* @returns {node is Identifier} | ||
*/ | ||
function isVmReference(node) { | ||
if (node.type !== 'Identifier') { | ||
return false | ||
} | ||
const parent = node.parent | ||
if (parent.type === 'MemberExpression') { | ||
if (parent.property === node) { | ||
// foo.id | ||
return false | ||
} | ||
} else if ( | ||
parent.type === 'Property' && | ||
parent.key === node && | ||
!parent.computed | ||
) { | ||
// {id: foo} | ||
return false | ||
} | ||
const exprContainer = getVExpressionContainer(node) | ||
for (const reference of exprContainer.references) { | ||
if (reference.variable != null) { | ||
// Not vm reference | ||
continue | ||
} | ||
if (reference.id === node) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
module.exports = { | ||
@@ -78,13 +127,2 @@ meta: { | ||
/** | ||
* @param {ASTNode} node | ||
* @returns {VExpressionContainer} | ||
*/ | ||
function getVExpressionContainer(node) { | ||
let n = node | ||
while (n.type !== 'VExpressionContainer') { | ||
n = /** @type {ASTNode} */ (n.parent) | ||
} | ||
return n | ||
} | ||
/** | ||
* @param {MemberExpression|AssignmentProperty} node | ||
@@ -105,37 +143,3 @@ * @returns {string} | ||
} | ||
/** | ||
* @param {ASTNode} node | ||
* @returns {node is Identifier} | ||
*/ | ||
function isVmReference(node) { | ||
if (node.type !== 'Identifier') { | ||
return false | ||
} | ||
const parent = node.parent | ||
if (parent.type === 'MemberExpression') { | ||
if (parent.property === node) { | ||
// foo.id | ||
return false | ||
} | ||
} else if (parent.type === 'Property') { | ||
// {id: foo} | ||
if (parent.key === node && !parent.computed) { | ||
return false | ||
} | ||
} | ||
const exprContainer = getVExpressionContainer(node) | ||
for (const reference of exprContainer.references) { | ||
if (reference.variable != null) { | ||
// Not vm reference | ||
continue | ||
} | ||
if (reference.id === node) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
/** | ||
@@ -161,24 +165,32 @@ * @param {MemberExpression|Identifier} props | ||
} | ||
if (param.type === 'Identifier') { | ||
yield { | ||
node: param, | ||
path | ||
switch (param.type) { | ||
case 'Identifier': { | ||
yield { node: param, path } | ||
break | ||
} | ||
} else if (param.type === 'RestElement') { | ||
yield* iteratePatternProperties(param.argument, path) | ||
} else if (param.type === 'AssignmentPattern') { | ||
yield* iteratePatternProperties(param.left, path) | ||
} else if (param.type === 'ObjectPattern') { | ||
for (const prop of param.properties) { | ||
if (prop.type === 'Property') { | ||
const name = getPropertyNameText(prop) | ||
yield* iteratePatternProperties(prop.value, [...path, name]) | ||
} else if (prop.type === 'RestElement') { | ||
yield* iteratePatternProperties(prop.argument, path) | ||
case 'RestElement': { | ||
yield* iteratePatternProperties(param.argument, path) | ||
break | ||
} | ||
case 'AssignmentPattern': { | ||
yield* iteratePatternProperties(param.left, path) | ||
break | ||
} | ||
case 'ObjectPattern': { | ||
for (const prop of param.properties) { | ||
if (prop.type === 'Property') { | ||
const name = getPropertyNameText(prop) | ||
yield* iteratePatternProperties(prop.value, [...path, name]) | ||
} else if (prop.type === 'RestElement') { | ||
yield* iteratePatternProperties(prop.argument, path) | ||
} | ||
} | ||
break | ||
} | ||
} else if (param.type === 'ArrayPattern') { | ||
for (let index = 0; index < param.elements.length; index++) { | ||
const element = param.elements[index] | ||
yield* iteratePatternProperties(element, [...path, `${index}`]) | ||
case 'ArrayPattern': { | ||
for (let index = 0; index < param.elements.length; index++) { | ||
const element = param.elements[index] | ||
yield* iteratePatternProperties(element, [...path, `${index}`]) | ||
} | ||
break | ||
} | ||
@@ -230,3 +242,3 @@ } | ||
for (const variable of globalScope.variables) { | ||
if (variable.defs.length) { | ||
if (variable.defs.length > 0) { | ||
yield variable.name | ||
@@ -239,3 +251,3 @@ } | ||
for (const variable of (moduleScope && moduleScope.variables) || []) { | ||
if (variable.defs.length) { | ||
if (variable.defs.length > 0) { | ||
yield variable.name | ||
@@ -415,6 +427,9 @@ } | ||
} | ||
if (attr && attr.directive && attr.key.name.name === 'bind') { | ||
if (!attr.key.modifiers.some((mod) => mod.name === 'sync')) { | ||
return | ||
} | ||
if ( | ||
attr && | ||
attr.directive && | ||
attr.key.name.name === 'bind' && | ||
!attr.key.modifiers.some((mod) => mod.name === 'sync') | ||
) { | ||
return | ||
} | ||
@@ -421,0 +436,0 @@ |
@@ -70,6 +70,8 @@ /** | ||
type: 'object', | ||
properties: Object.keys(DEFAULT_OPTIONS).reduce((ret, code) => { | ||
ret[code] = { type: 'boolean' } | ||
return ret | ||
}, /** @type { { [key: string]: { type: 'boolean' } } } */ ({})), | ||
properties: Object.fromEntries( | ||
Object.keys(DEFAULT_OPTIONS).map((code) => [ | ||
code, | ||
{ type: 'boolean' } | ||
]) | ||
), | ||
additionalProperties: false | ||
@@ -76,0 +78,0 @@ } |
@@ -75,3 +75,3 @@ /** | ||
const candidateOptionList = [...candidateOptionSet] | ||
if (!candidateOptionList.length) { | ||
if (candidateOptionList.length === 0) { | ||
return {} | ||
@@ -95,10 +95,10 @@ } | ||
if (!componentInstanceOptions.length) { | ||
if (componentInstanceOptions.length === 0) { | ||
return | ||
} | ||
componentInstanceOptions.forEach((option) => { | ||
for (const option of componentInstanceOptions) { | ||
const id = option.key | ||
const name = option.name | ||
if (candidateOptionSet.has(name)) { | ||
return | ||
continue | ||
} | ||
@@ -109,3 +109,3 @@ const potentialTypoList = candidateOptionList | ||
.sort((a, b) => a.distance - b.distance) | ||
if (potentialTypoList.length) { | ||
if (potentialTypoList.length > 0) { | ||
context.report({ | ||
@@ -126,5 +126,5 @@ node: id, | ||
} | ||
}) | ||
} | ||
}) | ||
} | ||
} |
@@ -15,3 +15,3 @@ /** | ||
'disallow use of value wrapped by `ref()` (Composition API) as an operand', | ||
categories: ['vue3-essential'], | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-ref-as-operand.html' | ||
@@ -18,0 +18,0 @@ }, |
@@ -54,2 +54,26 @@ /** | ||
/** | ||
* @param {Expression | SpreadElement} node | ||
* @returns {node is (Literal | TemplateLiteral)} | ||
*/ | ||
function canVerify(node) { | ||
return ( | ||
node.type === 'Literal' || | ||
(node.type === 'TemplateLiteral' && | ||
node.expressions.length === 0 && | ||
node.quasis.length === 1) | ||
) | ||
} | ||
/** | ||
* @param {string} name | ||
* @returns {string} | ||
*/ | ||
function getMessageId(name) { | ||
if (RESERVED_NAMES_IN_HTML.has(name)) return 'reservedInHtml' | ||
if (RESERVED_NAMES_IN_VUE.has(name)) return 'reservedInVue' | ||
if (RESERVED_NAMES_IN_VUE3.has(name)) return 'reservedInVue3' | ||
return 'reserved' | ||
} | ||
module.exports = { | ||
@@ -61,3 +85,3 @@ meta: { | ||
'disallow the use of reserved names in component definitions', | ||
categories: undefined, | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-reserved-component-names.html' | ||
@@ -103,15 +127,2 @@ }, | ||
/** | ||
* @param {Expression | SpreadElement} node | ||
* @returns {node is (Literal | TemplateLiteral)} | ||
*/ | ||
function canVerify(node) { | ||
return ( | ||
node.type === 'Literal' || | ||
(node.type === 'TemplateLiteral' && | ||
node.expressions.length === 0 && | ||
node.quasis.length === 1) | ||
) | ||
} | ||
/** | ||
* @param {Literal | TemplateLiteral} node | ||
@@ -139,9 +150,3 @@ */ | ||
node, | ||
messageId: RESERVED_NAMES_IN_HTML.has(name) | ||
? 'reservedInHtml' | ||
: RESERVED_NAMES_IN_VUE.has(name) | ||
? 'reservedInVue' | ||
: RESERVED_NAMES_IN_VUE3.has(name) | ||
? 'reservedInVue3' | ||
: 'reserved', | ||
messageId: getMessageId(name), | ||
data: { | ||
@@ -166,6 +171,7 @@ name | ||
// Report if a component has been registered locally with a reserved name. | ||
utils | ||
.getRegisteredComponents(obj) | ||
.filter(({ name }) => reservedNames.has(name)) | ||
.forEach(({ node, name }) => report(node, name)) | ||
for (const { node, name } of utils.getRegisteredComponents(obj)) { | ||
if (reservedNames.has(name)) { | ||
report(node, name) | ||
} | ||
} | ||
@@ -172,0 +178,0 @@ const node = utils.findProperty(obj, 'name') |
@@ -60,4 +60,7 @@ /** | ||
const options = context.options[0] || {} | ||
const reservedKeys = new Set(RESERVED_KEYS.concat(options.reserved || [])) | ||
const groups = new Set(GROUP_NAMES.concat(options.groups || [])) | ||
const reservedKeys = new Set([ | ||
...RESERVED_KEYS, | ||
...(options.reserved || []) | ||
]) | ||
const groups = new Set([...GROUP_NAMES, ...(options.groups || [])]) | ||
@@ -64,0 +67,0 @@ // ---------------------------------------------------------------------- |
@@ -47,2 +47,9 @@ /** | ||
/** | ||
* @param {VElement} block | ||
*/ | ||
function defaultMessage(block) { | ||
return `Using \`<${block.rawName}>\` is not allowed.` | ||
} | ||
module.exports = { | ||
@@ -119,10 +126,3 @@ meta: { | ||
} | ||
/** | ||
* @param {VElement} block | ||
*/ | ||
function defaultMessage(block) { | ||
return `Using \`<${block.rawName}>\` is not allowed.` | ||
} | ||
} | ||
} |
@@ -16,2 +16,16 @@ /** | ||
/** | ||
* @param {string} id | ||
*/ | ||
function safeRequireResolve(id) { | ||
try { | ||
if (fs.statSync(id).isDirectory()) { | ||
return require.resolve(id) | ||
} | ||
} catch (_error) { | ||
// ignore | ||
} | ||
return id | ||
} | ||
module.exports = { | ||
@@ -79,16 +93,4 @@ meta: { | ||
let allLocalImports = null | ||
/** | ||
* @param {string} id | ||
*/ | ||
function safeRequireResolve(id) { | ||
try { | ||
if (fs.statSync(id).isDirectory()) { | ||
return require.resolve(id) | ||
} | ||
} catch (_e) { | ||
// ignore | ||
} | ||
return id | ||
} | ||
/** | ||
* @param {Program} ast | ||
@@ -172,5 +174,5 @@ */ | ||
option.message || | ||
`The \`${[`import("${module}")`, ...paths].join( | ||
`\`${[`import("${module}")`, ...paths].join( | ||
'.' | ||
)}\` after \`await\` expression are forbidden.` | ||
)}\` is forbidden after an \`await\` expression.` | ||
@@ -177,0 +179,0 @@ for (const { node } of tracker.iterateEsmReferences(traceMap)) { |
@@ -131,7 +131,5 @@ /** | ||
'VAttribute[directive=false][key.name="class"]'(node) { | ||
node.value.value | ||
.split(/\s+/) | ||
.forEach((className) => | ||
reportForbiddenClass(className, node, context, forbiddenClasses) | ||
) | ||
for (const className of node.value.value.split(/\s+/)) { | ||
reportForbiddenClass(className, node, context, forbiddenClasses) | ||
} | ||
}, | ||
@@ -138,0 +136,0 @@ |
@@ -111,2 +111,9 @@ /** | ||
/** | ||
* @param {string[]} path | ||
*/ | ||
function defaultMessage(path) { | ||
return `Using \`${path.join('.')}\` is not allowed.` | ||
} | ||
module.exports = { | ||
@@ -212,10 +219,3 @@ meta: { | ||
} | ||
/** | ||
* @param {string[]} path | ||
*/ | ||
function defaultMessage(path) { | ||
return `Using \`${path.join('.')}\` is not allowed.` | ||
} | ||
} | ||
} |
@@ -52,3 +52,3 @@ /** | ||
context.options.forEach((option) => { | ||
for (const option of context.options) { | ||
const message = | ||
@@ -65,3 +65,3 @@ option.message || | ||
} | ||
}) | ||
} | ||
} | ||
@@ -68,0 +68,0 @@ }) |
@@ -157,3 +157,3 @@ /** | ||
} else if (node.type === 'Identifier') { | ||
replaceText = /^[a-z]\w*$/iu.exec(option.suggest) | ||
replaceText = /^[a-z]\w*$/iu.test(option.suggest) | ||
? option.suggest | ||
@@ -160,0 +160,0 @@ : JSON.stringify(option.suggest) |
@@ -82,2 +82,20 @@ /** | ||
/** | ||
* @param {VAttribute} node | ||
* @param {ParsedOption} option | ||
*/ | ||
function defaultMessage(node, option) { | ||
const key = node.key.rawName | ||
let value = '' | ||
if (option.useValue) { | ||
value = node.value == null ? '` set to `true' : `="${node.value.value}"` | ||
} | ||
let on = '' | ||
if (option.useElement) { | ||
on = ` on \`<${node.parent.parent.rawName}>\`` | ||
} | ||
return `Using \`${key + value}\`${on} is not allowed.` | ||
} | ||
module.exports = { | ||
@@ -121,3 +139,3 @@ meta: { | ||
create(context) { | ||
if (!context.options.length) { | ||
if (context.options.length === 0) { | ||
return {} | ||
@@ -146,22 +164,3 @@ } | ||
}) | ||
/** | ||
* @param {VAttribute} node | ||
* @param {ParsedOption} option | ||
*/ | ||
function defaultMessage(node, option) { | ||
const key = node.key.rawName | ||
const value = !option.useValue | ||
? '' | ||
: node.value == null | ||
? '` set to `true' | ||
: `="${node.value.value}"` | ||
let on = '' | ||
if (option.useElement) { | ||
on = ` on \`<${node.parent.parent.rawName}>\`` | ||
} | ||
return `Using \`${key + value}\`${on} is not allowed.` | ||
} | ||
} | ||
} |
@@ -72,5 +72,5 @@ /** | ||
} | ||
return /** @type {string[]} */ (option.modifiers).every((modName) => { | ||
return key.modifiers.some((mid) => mid.name === modName) | ||
}) | ||
return /** @type {string[]} */ (option.modifiers).every((modName) => | ||
key.modifiers.some((mid) => mid.name === modName) | ||
) | ||
} | ||
@@ -95,2 +95,21 @@ parsed.modifiers = option.modifiers | ||
/** | ||
* @param {VDirectiveKey} key | ||
* @param {ParsedOption} option | ||
*/ | ||
function defaultMessage(key, option) { | ||
const vbind = key.name.rawName === ':' ? '' : 'v-bind' | ||
const arg = | ||
key.argument != null && key.argument.type === 'VIdentifier' | ||
? `:${key.argument.rawName}` | ||
: '' | ||
const mod = | ||
option.modifiers.length > 0 ? `.${option.modifiers.join('.')}` : '' | ||
let on = '' | ||
if (option.useElement) { | ||
on = ` on \`<${key.parent.parent.parent.rawName}>\`` | ||
} | ||
return `Using \`${vbind + arg + mod}\`${on} is not allowed.` | ||
} | ||
module.exports = { | ||
@@ -164,23 +183,3 @@ meta: { | ||
}) | ||
/** | ||
* @param {VDirectiveKey} key | ||
* @param {ParsedOption} option | ||
*/ | ||
function defaultMessage(key, option) { | ||
const vbind = key.name.rawName === ':' ? '' : 'v-bind' | ||
const arg = | ||
key.argument != null && key.argument.type === 'VIdentifier' | ||
? `:${key.argument.rawName}` | ||
: '' | ||
const mod = option.modifiers.length | ||
? `.${option.modifiers.join('.')}` | ||
: '' | ||
let on = '' | ||
if (option.useElement) { | ||
on = ` on \`<${key.parent.parent.parent.rawName}>\`` | ||
} | ||
return `Using \`${vbind + arg + mod}\`${on} is not allowed.` | ||
} | ||
} | ||
} |
@@ -14,3 +14,3 @@ /** | ||
description: 'disallow destructuring of `props` passed to `setup`', | ||
categories: ['vue3-essential'], | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-setup-props-destructure.html' | ||
@@ -17,0 +17,0 @@ }, |
@@ -77,4 +77,4 @@ /** | ||
info ? computedPropertiesMap.get(info.node) || [] : [] | ||
).find((cp) => { | ||
return ( | ||
).find( | ||
(cp) => | ||
cp.value && | ||
@@ -84,4 +84,3 @@ cp.value.range[0] <= node.range[0] && | ||
targetBody === cp.value | ||
) | ||
}) | ||
) | ||
if (computedProperty) { | ||
@@ -88,0 +87,0 @@ const mem = node.parent |
@@ -8,2 +8,78 @@ /** | ||
const utils = require('../utils') | ||
/** | ||
* Checks whether if the given property node is a static value. | ||
* @param {Property} prop property node to check | ||
* @returns {boolean} `true` if the given property node is a static value. | ||
*/ | ||
function isStaticValue(prop) { | ||
return ( | ||
!prop.computed && | ||
prop.value.type === 'Literal' && | ||
(prop.key.type === 'Identifier' || prop.key.type === 'Literal') | ||
) | ||
} | ||
/** | ||
* Gets the static properties of a given expression node. | ||
* - If `SpreadElement` or computed property exists, it gets only the static properties before it. | ||
* `:style="{ color: 'red', display: 'flex', ...spread, width: '16px' }"` | ||
* ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ | ||
* - If non-static object exists, it gets only the static properties up to that object. | ||
* `:style="[ { color: 'red' }, { display: 'flex', color, width: '16px' }, { height: '16px' } ]"` | ||
* ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ | ||
* - If all properties are static properties, it returns one root node. | ||
* `:style="[ { color: 'red' }, { display: 'flex', width: '16px' } ]"` | ||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
* @param {VDirective} node `:style` node to check | ||
* @returns {Property[] | [VDirective]} the static properties. | ||
*/ | ||
function getReportNodes(node) { | ||
const { value } = node | ||
if (!value) { | ||
return [] | ||
} | ||
const { expression } = value | ||
if (!expression) { | ||
return [] | ||
} | ||
let elements | ||
if (expression.type === 'ObjectExpression') { | ||
elements = [expression] | ||
} else if (expression.type === 'ArrayExpression') { | ||
elements = expression.elements | ||
} else { | ||
return [] | ||
} | ||
const staticProperties = [] | ||
for (const element of elements) { | ||
if (!element) { | ||
continue | ||
} | ||
if (element.type !== 'ObjectExpression') { | ||
return staticProperties | ||
} | ||
let isAllStatic = true | ||
for (const prop of element.properties) { | ||
if (prop.type === 'SpreadElement' || prop.computed) { | ||
// If `SpreadElement` or computed property exists, it gets only the static properties before it. | ||
return staticProperties | ||
} | ||
if (isStaticValue(prop)) { | ||
staticProperties.push(prop) | ||
} else { | ||
isAllStatic = false | ||
} | ||
} | ||
if (!isAllStatic) { | ||
// If non-static object exists, it gets only the static properties up to that object. | ||
return staticProperties | ||
} | ||
} | ||
// If all properties are static properties, it returns one root node. | ||
return [node] | ||
} | ||
module.exports = { | ||
@@ -37,77 +113,2 @@ meta: { | ||
/** | ||
* Checks whether if the given property node is a static value. | ||
* @param {Property} prop property node to check | ||
* @returns {boolean} `true` if the given property node is a static value. | ||
*/ | ||
function isStaticValue(prop) { | ||
return ( | ||
!prop.computed && | ||
prop.value.type === 'Literal' && | ||
(prop.key.type === 'Identifier' || prop.key.type === 'Literal') | ||
) | ||
} | ||
/** | ||
* Gets the static properties of a given expression node. | ||
* - If `SpreadElement` or computed property exists, it gets only the static properties before it. | ||
* `:style="{ color: 'red', display: 'flex', ...spread, width: '16px' }"` | ||
* ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ | ||
* - If non-static object exists, it gets only the static properties up to that object. | ||
* `:style="[ { color: 'red' }, { display: 'flex', color, width: '16px' }, { height: '16px' } ]"` | ||
* ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ | ||
* - If all properties are static properties, it returns one root node. | ||
* `:style="[ { color: 'red' }, { display: 'flex', width: '16px' } ]"` | ||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
* @param {VDirective} node `:style` node to check | ||
* @returns {Property[] | [VDirective]} the static properties. | ||
*/ | ||
function getReportNodes(node) { | ||
const { value } = node | ||
if (!value) { | ||
return [] | ||
} | ||
const { expression } = value | ||
if (!expression) { | ||
return [] | ||
} | ||
let elements | ||
if (expression.type === 'ObjectExpression') { | ||
elements = [expression] | ||
} else if (expression.type === 'ArrayExpression') { | ||
elements = expression.elements | ||
} else { | ||
return [] | ||
} | ||
const staticProperties = [] | ||
for (const element of elements) { | ||
if (!element) { | ||
continue | ||
} | ||
if (element.type !== 'ObjectExpression') { | ||
return staticProperties | ||
} | ||
let isAllStatic = true | ||
for (const prop of element.properties) { | ||
if (prop.type === 'SpreadElement' || prop.computed) { | ||
// If `SpreadElement` or computed property exists, it gets only the static properties before it. | ||
return staticProperties | ||
} | ||
if (isStaticValue(prop)) { | ||
staticProperties.push(prop) | ||
} else { | ||
isAllStatic = false | ||
} | ||
} | ||
if (!isAllStatic) { | ||
// If non-static object exists, it gets only the static properties up to that object. | ||
return staticProperties | ||
} | ||
} | ||
// If all properties are static properties, it returns one root node. | ||
return [node] | ||
} | ||
/** | ||
* Reports if the value is static. | ||
@@ -114,0 +115,0 @@ * @param {VDirective} node `:style` node to check |
@@ -98,5 +98,3 @@ /** | ||
utils.executeOnVue(context, (obj) => { | ||
const properties = Array.from( | ||
utils.iterateProperties(obj, new Set(GROUP_NAMES)) | ||
) | ||
const properties = utils.iterateProperties(obj, new Set(GROUP_NAMES)) | ||
for (const node of properties) { | ||
@@ -103,0 +101,0 @@ jsVars.add(node.name) |
@@ -164,6 +164,4 @@ /** | ||
const dotIndex = rawName.indexOf('.') | ||
if (dotIndex > 0) { | ||
if (existsSetupReference(rawName.slice(0, dotIndex))) { | ||
return | ||
} | ||
if (dotIndex > 0 && existsSetupReference(rawName.slice(0, dotIndex))) { | ||
return | ||
} | ||
@@ -231,9 +229,8 @@ | ||
const kebabCaseName = casing.kebabCase(rawName) | ||
if (registeredComponentKebabCaseNames.includes(kebabCaseName)) { | ||
if ( | ||
// Component registered as `foo-bar` cannot be used as `FooBar` | ||
!casing.isPascalCase(rawName) | ||
) { | ||
return | ||
} | ||
if ( | ||
registeredComponentKebabCaseNames.includes(kebabCaseName) && | ||
!casing.isPascalCase(rawName) | ||
) { | ||
// Component registered as `foo-bar` cannot be used as `FooBar` | ||
return | ||
} | ||
@@ -240,0 +237,0 @@ |
@@ -127,2 +127,9 @@ /** | ||
/** | ||
* @param {ASTNode} node | ||
*/ | ||
function isScriptSetupProgram(node) { | ||
return node === programNode | ||
} | ||
/** Vue component context */ | ||
@@ -144,3 +151,3 @@ class VueComponentContext { | ||
verifyReferences(references, options) { | ||
const that = this | ||
const report = this.report.bind(this) | ||
verifyUndefProperties(this.defineProperties, references, null) | ||
@@ -164,10 +171,8 @@ | ||
if (prop) { | ||
if (options && options.props) { | ||
if (!prop.isProps) { | ||
that.report(nodes[0], referencePathName, 'undefProps') | ||
continue | ||
} | ||
if (options && options.props && !prop.isProps) { | ||
report(nodes[0], referencePathName, 'undefProps') | ||
continue | ||
} | ||
} else { | ||
that.report(nodes[0], referencePathName, 'undef') | ||
report(nodes[0], referencePathName, 'undef') | ||
continue | ||
@@ -244,9 +249,2 @@ } | ||
return exported && vueComponentContextMap.get(exported) | ||
/** | ||
* @param {ASTNode} node | ||
*/ | ||
function isScriptSetupProgram(node) { | ||
return node === programNode | ||
} | ||
} | ||
@@ -253,0 +251,0 @@ |
@@ -109,32 +109,35 @@ /** | ||
registeredComponents | ||
.filter(({ name }) => { | ||
// If the component name is PascalCase or camelCase | ||
// it can be used in various of ways inside template, | ||
// like "theComponent", "The-component" etc. | ||
// but except snake_case | ||
if (casing.isPascalCase(name) || casing.isCamelCase(name)) { | ||
return ![...usedComponents].some((n) => { | ||
return ( | ||
n.indexOf('_') === -1 && | ||
for (const { node, name } of registeredComponents) { | ||
// If the component name is PascalCase or camelCase | ||
// it can be used in various of ways inside template, | ||
// like "theComponent", "The-component" etc. | ||
// but except snake_case | ||
if (casing.isPascalCase(name) || casing.isCamelCase(name)) { | ||
if ( | ||
[...usedComponents].some( | ||
(n) => | ||
!n.includes('_') && | ||
(name === casing.pascalCase(n) || | ||
casing.camelCase(n) === name) | ||
) | ||
}) | ||
} else { | ||
// In any other case the used component name must exactly match | ||
// the registered name | ||
return !usedComponents.has(name) | ||
name === casing.camelCase(n)) | ||
) | ||
) { | ||
return | ||
} | ||
} else { | ||
// In any other case the used component name must exactly match | ||
// the registered name | ||
if (usedComponents.has(name)) { | ||
return | ||
} | ||
} | ||
context.report({ | ||
node, | ||
message: | ||
'The "{{name}}" component has been registered but not used.', | ||
data: { | ||
name | ||
} | ||
}) | ||
.forEach(({ node, name }) => | ||
context.report({ | ||
node, | ||
message: | ||
'The "{{name}}" component has been registered but not used.', | ||
data: { | ||
name | ||
} | ||
}) | ||
) | ||
} | ||
} | ||
@@ -141,0 +144,0 @@ }, |
@@ -104,43 +104,52 @@ /** | ||
const parent = node.parent | ||
if (parent.type === 'AssignmentExpression') { | ||
if (parent.right === node) { | ||
if (parent.left.type === 'ObjectPattern') { | ||
// `({foo} = $refs)` | ||
extractUsedForObjectPattern(parent.left) | ||
} else if (parent.left.type === 'Identifier') { | ||
// `foo = $refs` | ||
hasUnknown = true | ||
switch (parent.type) { | ||
case 'AssignmentExpression': { | ||
if (parent.right === node) { | ||
if (parent.left.type === 'ObjectPattern') { | ||
// `({foo} = $refs)` | ||
extractUsedForObjectPattern(parent.left) | ||
} else if (parent.left.type === 'Identifier') { | ||
// `foo = $refs` | ||
hasUnknown = true | ||
} | ||
} | ||
break | ||
} | ||
} else if (parent.type === 'VariableDeclarator') { | ||
if (parent.init === node) { | ||
if (parent.id.type === 'ObjectPattern') { | ||
// `const {foo} = $refs` | ||
extractUsedForObjectPattern(parent.id) | ||
} else if (parent.id.type === 'Identifier') { | ||
// `const foo = $refs` | ||
hasUnknown = true | ||
case 'VariableDeclarator': { | ||
if (parent.init === node) { | ||
if (parent.id.type === 'ObjectPattern') { | ||
// `const {foo} = $refs` | ||
extractUsedForObjectPattern(parent.id) | ||
} else if (parent.id.type === 'Identifier') { | ||
// `const foo = $refs` | ||
hasUnknown = true | ||
} | ||
} | ||
break | ||
} | ||
} else if (parent.type === 'MemberExpression') { | ||
if (parent.object === node) { | ||
// `$refs.foo` | ||
const name = utils.getStaticPropertyName(parent) | ||
if (name) { | ||
usedRefs.add(name) | ||
} else { | ||
case 'MemberExpression': { | ||
if (parent.object === node) { | ||
// `$refs.foo` | ||
const name = utils.getStaticPropertyName(parent) | ||
if (name) { | ||
usedRefs.add(name) | ||
} else { | ||
hasUnknown = true | ||
} | ||
} | ||
break | ||
} | ||
case 'CallExpression': { | ||
const argIndex = parent.arguments.indexOf(node) | ||
if (argIndex > -1) { | ||
// `foo($refs)` | ||
hasUnknown = true | ||
} | ||
break | ||
} | ||
} else if (parent.type === 'CallExpression') { | ||
const argIndex = parent.arguments.indexOf(node) | ||
if (argIndex > -1) { | ||
// `foo($refs)` | ||
case 'ForInStatement': | ||
case 'ReturnStatement': { | ||
hasUnknown = true | ||
break | ||
} | ||
} else if ( | ||
parent.type === 'ForInStatement' || | ||
parent.type === 'ReturnStatement' | ||
) { | ||
hasUnknown = true | ||
} | ||
@@ -232,7 +241,8 @@ } | ||
let refsNode = id | ||
if (id.parent.type === 'MemberExpression') { | ||
if (id.parent.property === id) { | ||
// `this.$refs.foo` | ||
refsNode = id.parent | ||
} | ||
if ( | ||
id.parent.type === 'MemberExpression' && | ||
id.parent.property === id | ||
) { | ||
// `this.$refs.foo` | ||
refsNode = id.parent | ||
} | ||
@@ -239,0 +249,0 @@ extractUsedForPattern(refsNode) |
@@ -107,3 +107,3 @@ /** | ||
if (variable.references.length) { | ||
if (variable.references.length > 0) { | ||
hasAfterUsed = true | ||
@@ -110,0 +110,0 @@ continue |
@@ -67,3 +67,3 @@ /** | ||
if (!resolvedSet.size) { | ||
if (resolvedSet.size === 0) { | ||
resolvedSet.add(node) | ||
@@ -88,6 +88,7 @@ } | ||
} | ||
} else if (id.parent.type === 'AssignmentExpression') { | ||
if (id.parent.left === id) { | ||
yield* resolvedExpressionsInternal(id.parent.right) | ||
} | ||
} else if ( | ||
id.parent.type === 'AssignmentExpression' && | ||
id.parent.left === id | ||
) { | ||
yield* resolvedExpressionsInternal(id.parent.right) | ||
} | ||
@@ -206,9 +207,8 @@ } | ||
} | ||
if (expr.type === 'ConditionalExpression') { | ||
if ( | ||
!maybeFunction(context, expr.consequent) && | ||
!maybeFunction(context, expr.alternate) | ||
) { | ||
continue | ||
} | ||
if ( | ||
expr.type === 'ConditionalExpression' && | ||
!maybeFunction(context, expr.consequent) && | ||
!maybeFunction(context, expr.alternate) | ||
) { | ||
continue | ||
} | ||
@@ -295,26 +295,35 @@ const evaluated = eslintUtils.getStaticValue( | ||
} | ||
if (property.groupName === 'data') { | ||
maybeFunctions.set( | ||
property.name, | ||
maybeFunction(context, property.property.value) | ||
) | ||
} else if (property.groupName === 'props') { | ||
const types = getComponentPropsTypes(context, property) | ||
maybeFunctions.set( | ||
property.name, | ||
!types || types.some((type) => !NATIVE_NOT_FUNCTION_TYPES.has(type)) | ||
) | ||
} else if (property.groupName === 'computed') { | ||
let value = property.property.value | ||
if (value.type === 'ObjectExpression') { | ||
const getProp = utils.findProperty(value, 'get') | ||
if (getProp) { | ||
value = getProp.value | ||
switch (property.groupName) { | ||
case 'data': { | ||
maybeFunctions.set( | ||
property.name, | ||
maybeFunction(context, property.property.value) | ||
) | ||
break | ||
} | ||
case 'props': { | ||
const types = getComponentPropsTypes(context, property) | ||
maybeFunctions.set( | ||
property.name, | ||
!types || types.some((type) => !NATIVE_NOT_FUNCTION_TYPES.has(type)) | ||
) | ||
break | ||
} | ||
case 'computed': { | ||
let value = property.property.value | ||
if (value.type === 'ObjectExpression') { | ||
const getProp = utils.findProperty(value, 'get') | ||
if (getProp) { | ||
value = getProp.value | ||
} | ||
} | ||
processFunction(property.name, value, 'computed') | ||
break | ||
} | ||
processFunction(property.name, value, 'computed') | ||
} else if (property.groupName === 'methods') { | ||
const value = property.property.value | ||
processFunction(property.name, value, 'methods') | ||
maybeFunctions.set(property.name, true) | ||
case 'methods': { | ||
const value = property.property.value | ||
processFunction(property.name, value, 'methods') | ||
maybeFunctions.set(property.name, true) | ||
break | ||
} | ||
} | ||
@@ -417,31 +426,38 @@ } | ||
} | ||
if (expr.type === 'MemberExpression') { | ||
if (utils.isThis(expr.object, this.context)) { | ||
const name = utils.getStaticPropertyName(expr) | ||
if (name && !this.maybeFunctionProperty(name)) { | ||
continue | ||
switch (expr.type) { | ||
case 'MemberExpression': { | ||
if (utils.isThis(expr.object, this.context)) { | ||
const name = utils.getStaticPropertyName(expr) | ||
if (name && !this.maybeFunctionProperty(name)) { | ||
continue | ||
} | ||
} | ||
break | ||
} | ||
} else if (expr.type === 'CallExpression') { | ||
if ( | ||
expr.callee.type === 'MemberExpression' && | ||
utils.isThis(expr.callee.object, this.context) | ||
) { | ||
const name = utils.getStaticPropertyName(expr.callee) | ||
const fnData = this.functions.find((data) => data.name === name) | ||
case 'CallExpression': { | ||
if ( | ||
fnData && | ||
fnData.kind === 'methods' && | ||
!fnData.maybeReturnFunction(this) | ||
expr.callee.type === 'MemberExpression' && | ||
utils.isThis(expr.callee.object, this.context) | ||
) { | ||
const name = utils.getStaticPropertyName(expr.callee) | ||
const fnData = this.functions.find((data) => data.name === name) | ||
if ( | ||
fnData && | ||
fnData.kind === 'methods' && | ||
!fnData.maybeReturnFunction(this) | ||
) { | ||
continue | ||
} | ||
} | ||
break | ||
} | ||
case 'ConditionalExpression': { | ||
if ( | ||
!this.maybeFunctionExpression(expr.consequent) && | ||
!this.maybeFunctionExpression(expr.alternate) | ||
) { | ||
continue | ||
} | ||
break | ||
} | ||
} else if (expr.type === 'ConditionalExpression') { | ||
if ( | ||
!this.maybeFunctionExpression(expr.consequent) && | ||
!this.maybeFunctionExpression(expr.alternate) | ||
) { | ||
continue | ||
} | ||
} | ||
@@ -491,3 +507,3 @@ // It could be a function because we don't know what it is. | ||
description: 'disallow use computed property like method', | ||
categories: undefined, | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-use-computed-property-like-method.html' | ||
@@ -494,0 +510,0 @@ }, |
@@ -147,3 +147,3 @@ /** | ||
return fixer.replaceText(node, text.replace(/\\([\s\S])/g, '$1')) | ||
return fixer.replaceText(node, text.replace(/\\([\S\s])/g, '$1')) | ||
} | ||
@@ -150,0 +150,0 @@ }) |
@@ -29,3 +29,51 @@ /** | ||
// ------------------------------------------------------------------------------ | ||
/** | ||
* @param {VAttribute | VDirective} attr | ||
*/ | ||
function getKeyName(attr) { | ||
if (attr.directive) { | ||
if (attr.key.name.name !== 'bind') { | ||
// no v-bind | ||
return null | ||
} | ||
if ( | ||
!attr.key.argument || | ||
attr.key.argument.type === 'VExpressionContainer' | ||
) { | ||
// unknown | ||
return null | ||
} | ||
return attr.key.argument.name | ||
} | ||
return attr.key.name | ||
} | ||
/** | ||
* @param {VAttribute | VDirective} attr | ||
*/ | ||
function isFragmentTemplateAttribute(attr) { | ||
if (attr.directive) { | ||
const directiveName = attr.key.name.name | ||
if (SPECIAL_TEMPLATE_DIRECTIVES.has(directiveName)) { | ||
return true | ||
} | ||
if (directiveName === 'slot-scope') { | ||
// `slot-scope` is deprecated in Vue.js 2.6 | ||
return true | ||
} | ||
if (directiveName === 'scope') { | ||
// `scope` is deprecated in Vue.js 2.5 | ||
return true | ||
} | ||
} | ||
const keyName = getKeyName(attr) | ||
if (keyName === 'slot') { | ||
// `slot` is deprecated in Vue.js 2.6 | ||
return true | ||
} | ||
return false | ||
} | ||
module.exports = { | ||
@@ -48,51 +96,2 @@ meta: { | ||
create(context) { | ||
/** | ||
* @param {VAttribute | VDirective} attr | ||
*/ | ||
function getKeyName(attr) { | ||
if (attr.directive) { | ||
if (attr.key.name.name !== 'bind') { | ||
// no v-bind | ||
return null | ||
} | ||
if ( | ||
!attr.key.argument || | ||
attr.key.argument.type === 'VExpressionContainer' | ||
) { | ||
// unknown | ||
return null | ||
} | ||
return attr.key.argument.name | ||
} | ||
return attr.key.name | ||
} | ||
/** | ||
* @param {VAttribute | VDirective} attr | ||
*/ | ||
function isFragmentTemplateAttribute(attr) { | ||
if (attr.directive) { | ||
const directiveName = attr.key.name.name | ||
if (SPECIAL_TEMPLATE_DIRECTIVES.has(directiveName)) { | ||
return true | ||
} | ||
if (directiveName === 'slot-scope') { | ||
// `slot-scope` is deprecated in Vue.js 2.6 | ||
return true | ||
} | ||
if (directiveName === 'scope') { | ||
// `scope` is deprecated in Vue.js 2.5 | ||
return true | ||
} | ||
} | ||
const keyName = getKeyName(attr) | ||
if (keyName === 'slot') { | ||
// `slot` is deprecated in Vue.js 2.6 | ||
return true | ||
} | ||
return false | ||
} | ||
return utils.defineTemplateBodyVisitor(context, { | ||
@@ -99,0 +98,0 @@ /** @param {VStartTag} node */ |
@@ -51,4 +51,4 @@ /** | ||
function verify(node) { | ||
const { value } = node | ||
if (!value || node.key.modifiers.length) { | ||
const { value, key } = node | ||
if (!value || key.modifiers.length > 0) { | ||
return | ||
@@ -124,7 +124,7 @@ } | ||
const shorthand = node.key.name.rawName === ':' | ||
const shorthand = key.name.rawName === ':' | ||
/** @type {Range} */ | ||
const keyDirectiveRange = [ | ||
node.key.name.range[0], | ||
node.key.name.range[1] + (shorthand ? 0 : 1) | ||
key.name.range[0], | ||
key.name.range[1] + (shorthand ? 0 : 1) | ||
] | ||
@@ -131,0 +131,0 @@ |
@@ -22,5 +22,3 @@ /** | ||
description: 'disallow v-text / v-html on component', | ||
// TODO We will change it in the next major version. | ||
// categories: ['essential', 'vue3-essential'], | ||
categories: undefined, | ||
categories: ['essential', 'vue3-essential'], | ||
url: 'https://eslint.vuejs.org/rules/no-v-text-v-html-on-component.html' | ||
@@ -27,0 +25,0 @@ }, |
@@ -54,3 +54,3 @@ /** | ||
messages: { | ||
forbidden: 'The `watch` after `await` expression are forbidden.' | ||
forbidden: '`watch` is forbidden after an `await` expression.' | ||
} | ||
@@ -57,0 +57,0 @@ }, |
@@ -107,9 +107,11 @@ /** | ||
order.forEach((property, i) => { | ||
for (const [i, property] of order.entries()) { | ||
if (Array.isArray(property)) { | ||
property.forEach((p) => orderMap.set(p, i)) | ||
for (const p of property) { | ||
orderMap.set(p, i) | ||
} | ||
} else { | ||
orderMap.set(property, i) | ||
} | ||
}) | ||
} | ||
@@ -130,3 +132,3 @@ return orderMap | ||
const RELATIONAL_OPERATORS = ['in', 'instanceof'] | ||
const ALL_BINARY_OPERATORS = [ | ||
const ALL_BINARY_OPERATORS = new Set([ | ||
...ARITHMETIC_OPERATORS, | ||
@@ -136,4 +138,4 @@ ...BITWISE_OPERATORS, | ||
...RELATIONAL_OPERATORS | ||
] | ||
const LOGICAL_OPERATORS = ['&&', '||', '??' /* es2020 */] | ||
]) | ||
const LOGICAL_OPERATORS = new Set(['&&', '||', '??' /* es2020 */]) | ||
@@ -185,5 +187,5 @@ /** | ||
(node.type !== 'BinaryExpression' || | ||
!ALL_BINARY_OPERATORS.includes(node.operator)) && | ||
!ALL_BINARY_OPERATORS.has(node.operator)) && | ||
(node.type !== 'LogicalExpression' || | ||
!LOGICAL_OPERATORS.includes(node.operator)) && | ||
!LOGICAL_OPERATORS.has(node.operator)) && | ||
node.type !== 'MemberExpression' && | ||
@@ -264,16 +266,14 @@ node.type !== 'ConditionalExpression' && | ||
.filter(utils.isProperty) | ||
.map((property) => { | ||
return { | ||
node: property, | ||
name: | ||
utils.getStaticPropertyName(property) || | ||
(property.key.type === 'Identifier' && property.key.name) || | ||
'' | ||
} | ||
}) | ||
.map((property) => ({ | ||
node: property, | ||
name: | ||
utils.getStaticPropertyName(property) || | ||
(property.key.type === 'Identifier' && property.key.name) || | ||
'' | ||
})) | ||
properties.forEach((property, i) => { | ||
for (const [i, property] of properties.entries()) { | ||
const orderPos = getOrderPosition(property.name) | ||
if (orderPos < 0) { | ||
return | ||
continue | ||
} | ||
@@ -341,3 +341,3 @@ const propertiesAbove = properties.slice(0, i) | ||
} | ||
}) | ||
} | ||
} | ||
@@ -344,0 +344,0 @@ |
@@ -44,3 +44,3 @@ /** | ||
} | ||
if (!paddingLines.length) { | ||
if (paddingLines.length === 0) { | ||
return | ||
@@ -174,5 +174,6 @@ } | ||
...documentFragment.comments | ||
].sort((a, b) => | ||
a.range[0] > b.range[0] ? 1 : a.range[0] < b.range[0] ? -1 : 0 | ||
) | ||
].sort((a, b) => { | ||
if (a.range[0] > b.range[0]) return 1 | ||
return a.range[0] < b.range[0] ? -1 : 0 | ||
}) | ||
} | ||
@@ -179,0 +180,0 @@ |
@@ -31,8 +31,15 @@ /** | ||
for (const specifier of node.specifiers) { | ||
if (specifier.type === 'ImportDefaultSpecifier') { | ||
yield 'default' | ||
} else if (specifier.type === 'ImportNamespaceSpecifier') { | ||
yield null // all | ||
} else if (specifier.type === 'ImportSpecifier') { | ||
yield specifier.imported.name | ||
switch (specifier.type) { | ||
case 'ImportDefaultSpecifier': { | ||
yield 'default' | ||
break | ||
} | ||
case 'ImportNamespaceSpecifier': { | ||
yield null // all | ||
break | ||
} | ||
case 'ImportSpecifier': { | ||
yield specifier.imported.name | ||
break | ||
} | ||
} | ||
@@ -51,5 +58,3 @@ } | ||
description: "enforce import from 'vue' instead of import from '@vue/*'", | ||
// TODO We will change it in the next major version. | ||
// categories: ['vue3-essential'], | ||
categories: undefined, | ||
categories: ['vue3-essential'], | ||
url: 'https://eslint.vuejs.org/rules/prefer-import-from-vue.html' | ||
@@ -56,0 +61,0 @@ }, |
@@ -40,3 +40,3 @@ /** | ||
const elements = node.elements.slice() | ||
const elements = [...node.elements] | ||
elements.splice(booleanTypeIndex, 1) | ||
@@ -107,3 +107,5 @@ const code = elements | ||
onDefinePropsEnter(_, props) { | ||
props.forEach(checkProperty) | ||
for (const prop of props) { | ||
checkProperty(prop) | ||
} | ||
} | ||
@@ -113,3 +115,5 @@ }), | ||
const props = utils.getComponentPropsFromOptions(obj) | ||
props.forEach(checkProperty) | ||
for (const prop of props) { | ||
checkProperty(prop) | ||
} | ||
}) | ||
@@ -116,0 +120,0 @@ ) |
@@ -21,4 +21,3 @@ /** | ||
const options = context.options[0] | ||
const caseType = | ||
allowedCaseOptions.indexOf(options) !== -1 ? options : 'camelCase' | ||
const caseType = allowedCaseOptions.includes(options) ? options : 'camelCase' | ||
const checker = casing.getChecker(caseType) | ||
@@ -25,0 +24,0 @@ |
@@ -30,2 +30,22 @@ /** | ||
/** | ||
* Detects whether given value node is a Boolean type | ||
* @param {Expression} value | ||
* @return {boolean} | ||
*/ | ||
function isValueNodeOfBooleanType(value) { | ||
if (value.type === 'Identifier' && value.name === 'Boolean') { | ||
return true | ||
} | ||
if (value.type === 'ArrayExpression') { | ||
const elements = value.elements.filter(isDef) | ||
return ( | ||
elements.length === 1 && | ||
elements[0].type === 'Identifier' && | ||
elements[0].name === 'Boolean' | ||
) | ||
} | ||
return false | ||
} | ||
module.exports = { | ||
@@ -107,22 +127,2 @@ meta: { | ||
/** | ||
* Detects whether given value node is a Boolean type | ||
* @param {Expression} value | ||
* @return {boolean} | ||
*/ | ||
function isValueNodeOfBooleanType(value) { | ||
if (value.type === 'Identifier' && value.name === 'Boolean') { | ||
return true | ||
} | ||
if (value.type === 'ArrayExpression') { | ||
const elements = value.elements.filter(isDef) | ||
return ( | ||
elements.length === 1 && | ||
elements[0].type === 'Identifier' && | ||
elements[0].name === 'Boolean' | ||
) | ||
} | ||
return false | ||
} | ||
/** | ||
* Detects whether given prop node is a Boolean | ||
@@ -129,0 +129,0 @@ * @param {ComponentObjectProp} prop |
@@ -63,6 +63,7 @@ /** | ||
} = node | ||
if (firstArg && firstArg.type === 'ObjectExpression') { | ||
if ( | ||
(callee.type === 'Identifier' && | ||
callee.name === 'defineComponent') || | ||
if ( | ||
firstArg && | ||
firstArg.type === 'ObjectExpression' && | ||
((callee.type === 'Identifier' && | ||
callee.name === 'defineComponent') || | ||
(callee.type === 'MemberExpression' && | ||
@@ -72,6 +73,5 @@ callee.object.type === 'Identifier' && | ||
callee.property.type === 'Identifier' && | ||
callee.property.name === 'extend') | ||
) { | ||
return | ||
} | ||
callee.property.name === 'extend')) | ||
) { | ||
return | ||
} | ||
@@ -78,0 +78,0 @@ } |
@@ -92,7 +92,11 @@ /** | ||
utils.executeOnVue(context, (obj) => { | ||
utils.getComponentEmitsFromOptions(obj).forEach(checker) | ||
for (const emit of utils.getComponentEmitsFromOptions(obj)) { | ||
checker(emit) | ||
} | ||
}), | ||
utils.defineScriptSetupVisitor(context, { | ||
onDefineEmitsEnter(_node, emits) { | ||
emits.forEach(checker) | ||
for (const emit of emits) { | ||
checker(emit) | ||
} | ||
} | ||
@@ -99,0 +103,0 @@ }) |
@@ -30,3 +30,3 @@ /** | ||
const FIX_EMITS_AFTER_OPTIONS = [ | ||
const FIX_EMITS_AFTER_OPTIONS = new Set([ | ||
'setup', | ||
@@ -57,3 +57,3 @@ 'data', | ||
'errorCaptured' | ||
] | ||
]) | ||
// ------------------------------------------------------------------------------ | ||
@@ -388,16 +388,13 @@ // Rule Definition | ||
if ( | ||
!vueTemplateDefineData || | ||
(vueTemplateDefineData.type !== 'export' && | ||
vueTemplateDefineData.type !== 'setup') | ||
(!vueTemplateDefineData || | ||
(vueTemplateDefineData.type !== 'export' && | ||
vueTemplateDefineData.type !== 'setup')) && | ||
emits && | ||
(type === 'mark' || type === 'export' || type === 'definition') | ||
) { | ||
if ( | ||
emits && | ||
(type === 'mark' || type === 'export' || type === 'definition') | ||
) { | ||
vueTemplateDefineData = { | ||
type, | ||
define: node, | ||
emits, | ||
props: vuePropsDeclarations.get(node) || [] | ||
} | ||
vueTemplateDefineData = { | ||
type, | ||
define: node, | ||
emits, | ||
props: vuePropsDeclarations.get(node) || [] | ||
} | ||
@@ -426,3 +423,3 @@ } | ||
const certainEmits = emits.filter((e) => e.key) | ||
if (certainEmits.length) { | ||
if (certainEmits.length > 0) { | ||
const last = certainEmits[certainEmits.length - 1] | ||
@@ -483,3 +480,3 @@ return [ | ||
`'${nameNode.value}'${ | ||
emitsOptionValue.elements.length ? ',' : '' | ||
emitsOptionValue.elements.length > 0 ? ',' : '' | ||
}` | ||
@@ -502,3 +499,3 @@ ) | ||
`'${nameNode.value}': null${ | ||
emitsOptionValue.properties.length ? ',' : '' | ||
emitsOptionValue.properties.length > 0 ? ',' : '' | ||
}` | ||
@@ -515,3 +512,3 @@ ) | ||
const afterOptionNode = propertyNodes.find((p) => | ||
FIX_EMITS_AFTER_OPTIONS.includes(utils.getStaticPropertyName(p) || '') | ||
FIX_EMITS_AFTER_OPTIONS.has(utils.getStaticPropertyName(p) || '') | ||
) | ||
@@ -528,3 +525,3 @@ return [ | ||
) | ||
} else if (object.properties.length) { | ||
} else if (object.properties.length > 0) { | ||
const before = | ||
@@ -564,3 +561,3 @@ propertyNodes[propertyNodes.length - 1] || | ||
) | ||
} else if (object.properties.length) { | ||
} else if (object.properties.length > 0) { | ||
const before = | ||
@@ -567,0 +564,0 @@ propertyNodes[propertyNodes.length - 1] || |
@@ -19,3 +19,3 @@ /** | ||
const FIX_EXPOSE_BEFORE_OPTIONS = [ | ||
const FIX_EXPOSE_BEFORE_OPTIONS = new Set([ | ||
'name', | ||
@@ -31,3 +31,3 @@ 'components', | ||
'emits' | ||
] | ||
]) | ||
@@ -136,6 +136,4 @@ /** | ||
} | ||
if (def.type === 'Variable') { | ||
if (def.node.init) { | ||
return isFunction(def.node.init) | ||
} | ||
if (def.type === 'Variable' && def.node.init) { | ||
return isFunction(def.node.init) | ||
} | ||
@@ -245,6 +243,8 @@ } | ||
if (node.type === 'ArrowFunctionExpression' && node.expression) { | ||
if (isFunction(node.body)) { | ||
scopeStack.returnFunction = true | ||
} | ||
if ( | ||
node.type === 'ArrowFunctionExpression' && | ||
node.expression && | ||
isFunction(node.body) | ||
) { | ||
scopeStack.returnFunction = true | ||
} | ||
@@ -256,6 +256,8 @@ }, | ||
} | ||
if (!scopeStack.returnFunction && node.argument) { | ||
if (isFunction(node.argument)) { | ||
scopeStack.returnFunction = true | ||
} | ||
if ( | ||
!scopeStack.returnFunction && | ||
node.argument && | ||
isFunction(node.argument) | ||
) { | ||
scopeStack.returnFunction = true | ||
} | ||
@@ -313,3 +315,3 @@ }, | ||
const beforeOptionNode = propertyNodes.find((p) => | ||
FIX_EXPOSE_BEFORE_OPTIONS.includes(utils.getStaticPropertyName(p) || '') | ||
FIX_EXPOSE_BEFORE_OPTIONS.has(utils.getStaticPropertyName(p) || '') | ||
) | ||
@@ -329,3 +331,3 @@ const allProps = [ | ||
}, | ||
...(allProps.length | ||
...(allProps.length > 0 | ||
? [ | ||
@@ -354,3 +356,3 @@ { | ||
return fixer.insertTextAfter(beforeOptionNode, `,\n${text}`) | ||
} else if (object.properties.length) { | ||
} else if (object.properties.length > 0) { | ||
const after = propertyNodes[0] || object.properties[0] | ||
@@ -357,0 +359,0 @@ return fixer.insertTextAfter( |
@@ -20,3 +20,3 @@ /** | ||
const forbiddenTypes = [ | ||
const forbiddenTypes = new Set([ | ||
'Literal', | ||
@@ -26,3 +26,3 @@ 'TemplateLiteral', | ||
'UpdateExpression' | ||
] | ||
]) | ||
@@ -34,3 +34,3 @@ /** | ||
return ( | ||
forbiddenTypes.indexOf(node.type) > -1 && | ||
forbiddenTypes.has(node.type) && | ||
!(node.type === 'Literal' && node.value == null && !node.bigint) | ||
@@ -63,23 +63,23 @@ ) | ||
nodes | ||
.filter((prop) => isForbiddenType(prop)) | ||
.forEach((prop) => | ||
context.report({ | ||
node: prop, | ||
message, | ||
data: { | ||
name: propName | ||
}, | ||
fix: (fixer) => { | ||
if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') { | ||
const newText = utils.getStringLiteralValue(prop, true) | ||
for (const prop of nodes) { | ||
if (!isForbiddenType(prop)) continue | ||
if (newText) { | ||
return fixer.replaceText(prop, newText) | ||
} | ||
context.report({ | ||
node: prop, | ||
message, | ||
data: { | ||
name: propName | ||
}, | ||
fix: (fixer) => { | ||
if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') { | ||
const newText = utils.getStringLiteralValue(prop, true) | ||
if (newText) { | ||
return fixer.replaceText(prop, newText) | ||
} | ||
return null | ||
} | ||
}) | ||
) | ||
return null | ||
} | ||
}) | ||
} | ||
} | ||
@@ -86,0 +86,0 @@ |
@@ -67,14 +67,22 @@ /** | ||
hasType = false | ||
} else if (value.type === 'ObjectExpression') { | ||
// foo: { | ||
hasType = objectHasType(value) | ||
} else if (value.type === 'ArrayExpression') { | ||
// foo: [ | ||
hasType = value.elements.length > 0 | ||
} else if ( | ||
value.type === 'FunctionExpression' || | ||
value.type === 'ArrowFunctionExpression' | ||
) { | ||
hasType = false | ||
} else { | ||
switch (value.type) { | ||
case 'ObjectExpression': { | ||
// foo: { | ||
hasType = objectHasType(value) | ||
break | ||
} | ||
case 'ArrayExpression': { | ||
// foo: [ | ||
hasType = value.elements.length > 0 | ||
break | ||
} | ||
case 'FunctionExpression': | ||
case 'ArrowFunctionExpression': { | ||
hasType = false | ||
break | ||
} | ||
} | ||
} | ||
if (!hasType) { | ||
@@ -81,0 +89,0 @@ const name = |
@@ -154,60 +154,70 @@ /** | ||
const node = utils.skipChainExpression(targetNode) | ||
if (node.type === 'CallExpression') { | ||
// Symbol(), Number() ... | ||
if ( | ||
node.callee.type === 'Identifier' && | ||
NATIVE_TYPES.has(node.callee.name) | ||
) { | ||
switch (node.type) { | ||
case 'CallExpression': { | ||
// Symbol(), Number() ... | ||
if ( | ||
node.callee.type === 'Identifier' && | ||
NATIVE_TYPES.has(node.callee.name) | ||
) { | ||
return { | ||
function: false, | ||
type: node.callee.name | ||
} | ||
} | ||
break | ||
} | ||
case 'TemplateLiteral': { | ||
// String | ||
return { | ||
function: false, | ||
type: node.callee.name | ||
type: 'String' | ||
} | ||
} | ||
} else if (node.type === 'TemplateLiteral') { | ||
// String | ||
return { | ||
function: false, | ||
type: 'String' | ||
case 'Literal': { | ||
// String, Boolean, Number | ||
if (node.value === null && !node.bigint) return null | ||
const type = node.bigint ? 'BigInt' : capitalize(typeof node.value) | ||
if (NATIVE_TYPES.has(type)) { | ||
return { | ||
function: false, | ||
type | ||
} | ||
} | ||
break | ||
} | ||
} else if (node.type === 'Literal') { | ||
// String, Boolean, Number | ||
if (node.value === null && !node.bigint) return null | ||
const type = node.bigint ? 'BigInt' : capitalize(typeof node.value) | ||
if (NATIVE_TYPES.has(type)) { | ||
case 'ArrayExpression': { | ||
// Array | ||
return { | ||
function: false, | ||
type | ||
type: 'Array' | ||
} | ||
} | ||
} else if (node.type === 'ArrayExpression') { | ||
// Array | ||
return { | ||
function: false, | ||
type: 'Array' | ||
case 'ObjectExpression': { | ||
// Object | ||
return { | ||
function: false, | ||
type: 'Object' | ||
} | ||
} | ||
} else if (node.type === 'ObjectExpression') { | ||
// Object | ||
return { | ||
function: false, | ||
type: 'Object' | ||
} | ||
} else if (node.type === 'FunctionExpression') { | ||
return { | ||
function: true, | ||
expression: false, | ||
type: 'Function', | ||
functionBody: node.body, | ||
returnTypes: [] | ||
} | ||
} else if (node.type === 'ArrowFunctionExpression') { | ||
if (node.expression) { | ||
const valueType = getValueType(node.body) | ||
case 'FunctionExpression': { | ||
return { | ||
function: true, | ||
expression: true, | ||
expression: false, | ||
type: 'Function', | ||
functionBody: node.body, | ||
returnType: valueType ? valueType.type : null | ||
returnTypes: [] | ||
} | ||
} else { | ||
} | ||
case 'ArrowFunctionExpression': { | ||
if (node.expression) { | ||
const valueType = getValueType(node.body) | ||
return { | ||
function: true, | ||
expression: true, | ||
type: 'Function', | ||
functionBody: node.body, | ||
returnType: valueType ? valueType.type : null | ||
} | ||
} | ||
return { | ||
@@ -241,3 +251,3 @@ function: true, | ||
name: propName, | ||
types: Array.from(expectedTypeNames).join(' or ').toLowerCase() | ||
types: [...expectedTypeNames].join(' or ').toLowerCase() | ||
} | ||
@@ -284,6 +294,7 @@ }) | ||
if (!defType.function) { | ||
if (typeNames.has(defType.type)) { | ||
if (!FUNCTION_VALUE_TYPES.has(defType.type)) { | ||
continue | ||
} | ||
if ( | ||
typeNames.has(defType.type) && | ||
!FUNCTION_VALUE_TYPES.has(defType.type) | ||
) { | ||
continue | ||
} | ||
@@ -293,3 +304,3 @@ report( | ||
prop, | ||
Array.from(typeNames).map((type) => | ||
[...typeNames].map((type) => | ||
FUNCTION_VALUE_TYPES.has(type) ? 'Function' : type | ||
@@ -296,0 +307,0 @@ ) |
@@ -90,3 +90,3 @@ /** | ||
(node) => { | ||
computedProperties.forEach((cp) => { | ||
for (const cp of computedProperties) { | ||
if (cp.value && cp.value.parent === node) { | ||
@@ -102,4 +102,4 @@ context.report({ | ||
} | ||
}) | ||
computedFunctionNodes.forEach((cf) => { | ||
} | ||
for (const cf of computedFunctionNodes) { | ||
if (cf === node) { | ||
@@ -111,3 +111,3 @@ context.report({ | ||
} | ||
}) | ||
} | ||
} | ||
@@ -114,0 +114,0 @@ ) |
@@ -23,11 +23,12 @@ /** | ||
return node.bigint === '0' | ||
} else if (!node.value) { | ||
return true | ||
} | ||
} else if (node.type === 'Identifier') { | ||
if (node.name === 'undefined' || node.name === 'NaN') { | ||
if (!node.value) { | ||
return true | ||
} | ||
} | ||
return false | ||
return ( | ||
node.type === 'Identifier' && | ||
(node.name === 'undefined' || node.name === 'NaN') | ||
) | ||
} | ||
@@ -44,3 +45,3 @@ // ------------------------------------------------------------------------------ | ||
'enforce that a return statement is present in emits validator', | ||
categories: ['vue3-essential'], | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/return-in-emits-validator.html' | ||
@@ -47,0 +48,0 @@ }, |
@@ -19,2 +19,12 @@ /** | ||
/** | ||
* `casing.camelCase()` converts the beginning to lowercase, | ||
* but does not convert the case of the beginning character when converting with Vue3. | ||
* @see https://github.com/vuejs/vue-next/blob/2749c15170ad4913e6530a257db485d4e7ed2283/packages/shared/src/index.ts#L116 | ||
* @param {string} str | ||
*/ | ||
function camelize(str) { | ||
return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : '')) | ||
} | ||
module.exports = { | ||
@@ -26,5 +36,6 @@ meta: { | ||
'prevent `<script setup>` variables used in `<template>` to be marked as unused', // eslint-disable-line eslint-plugin/require-meta-docs-description | ||
categories: ['base'], | ||
categories: undefined, | ||
url: 'https://eslint.vuejs.org/rules/script-setup-uses-vars.html' | ||
}, | ||
deprecated: true, | ||
schema: [] | ||
@@ -56,11 +67,2 @@ }, | ||
/** | ||
* `casing.camelCase()` converts the beginning to lowercase, | ||
* but does not convert the case of the beginning character when converting with Vue3. | ||
* @see https://github.com/vuejs/vue-next/blob/2749c15170ad4913e6530a257db485d4e7ed2283/packages/shared/src/index.ts#L116 | ||
* @param {string} str | ||
*/ | ||
function camelize(str) { | ||
return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : '')) | ||
} | ||
/** | ||
* @see https://github.com/vuejs/vue-next/blob/2749c15170ad4913e6530a257db485d4e7ed2283/packages/compiler-core/src/transforms/transformElement.ts#L333 | ||
@@ -67,0 +69,0 @@ * @param {string} name |
@@ -32,3 +32,3 @@ /** | ||
{ | ||
ignores: ['pre', 'textarea'].concat(INLINE_ELEMENTS), | ||
ignores: ['pre', 'textarea', ...INLINE_ELEMENTS], | ||
ignoreWhenNoAttributes: true, | ||
@@ -35,0 +35,0 @@ ignoreWhenEmpty: true |
@@ -138,13 +138,16 @@ /** | ||
/** @type {string[]} */ | ||
const ignoreGrandchildrenOf = (options && | ||
options.ignoreGrandchildrenOf) || [ | ||
'computed', | ||
'directives', | ||
'inject', | ||
'props', | ||
'watch' | ||
] | ||
/** @type {string[]} */ | ||
const ignoreChildrenOf = (options && options.ignoreChildrenOf) || ['model'] | ||
/** @type {Set<string>} */ | ||
const ignoreGrandchildrenOf = new Set( | ||
(options && options.ignoreGrandchildrenOf) || [ | ||
'computed', | ||
'directives', | ||
'inject', | ||
'props', | ||
'watch' | ||
] | ||
) | ||
/** @type {Set<string>} */ | ||
const ignoreChildrenOf = new Set( | ||
(options && options.ignoreChildrenOf) || ['model'] | ||
) | ||
const insensitive = options && options.caseSensitive === false | ||
@@ -219,10 +222,7 @@ const minKeys = options && options.minKeys | ||
// Judge whether to ignore the property. | ||
if (chainLevel === 1) { | ||
if (ignoreChildrenOf.includes(propName)) { | ||
vueState.ignore = true | ||
} | ||
} else if (chainLevel === 2) { | ||
if (ignoreGrandchildrenOf.includes(propName)) { | ||
vueState.ignore = true | ||
} | ||
if ( | ||
(chainLevel === 1 && ignoreChildrenOf.has(propName)) || | ||
(chainLevel === 2 && ignoreGrandchildrenOf.has(propName)) | ||
) { | ||
vueState.ignore = true | ||
} | ||
@@ -229,0 +229,0 @@ } else { |
@@ -28,4 +28,4 @@ /** | ||
/** @param {RuleContext} context */ | ||
create: (context) => { | ||
return defineTemplateBodyVisitor(context, { | ||
create: (context) => | ||
defineTemplateBodyVisitor(context, { | ||
/** @param {VAttribute} node */ | ||
@@ -61,3 +61,2 @@ "VAttribute[directive=false][key.name='class']"(node) { | ||
}) | ||
} | ||
} |
@@ -77,7 +77,6 @@ /** | ||
) | ||
const nameArgument = slotName | ||
? vBind | ||
? `:[${slotName}]` | ||
: `:${slotName}` | ||
: '' | ||
let nameArgument = '' | ||
if (slotName) { | ||
nameArgument = vBind ? `:[${slotName}]` : `:${slotName}` | ||
} | ||
const scopeValue = | ||
@@ -84,0 +83,0 @@ scopeAttr && scopeAttr.value |
@@ -83,3 +83,3 @@ /** | ||
expr && getUsingIterationVars(slot.value, slot.parent.parent) | ||
return expr && variables && variables.length ? { expr, variables } : null | ||
return expr && variables && variables.length > 0 ? { expr, variables } : null | ||
} | ||
@@ -208,10 +208,7 @@ | ||
} | ||
for (const v of a.variables) { | ||
if (!checkedVarNames.has(v.id.name)) { | ||
if (b.variables.every((bv) => v.id.name !== bv.id.name)) { | ||
return false | ||
} | ||
} | ||
} | ||
return true | ||
return a.variables.every( | ||
(v) => | ||
checkedVarNames.has(v.id.name) || | ||
b.variables.some((bv) => v.id.name === bv.id.name) | ||
) | ||
@@ -218,0 +215,0 @@ /** |
@@ -6,2 +6,12 @@ /** | ||
'use strict' | ||
/** | ||
* Checks whether the given node can convert to the `slot`. | ||
* @param {VDirective} vSlotAttr node of `v-slot` | ||
* @returns {boolean} `true` if the given node can convert to the `slot` | ||
*/ | ||
function canConvertToSlot(vSlotAttr) { | ||
return vSlotAttr.parent.parent.name === 'template' | ||
} | ||
module.exports = { | ||
@@ -14,13 +24,2 @@ supported: '>=2.6.0', | ||
/** | ||
* Checks whether the given node can convert to the `slot`. | ||
* @param {VDirective} vSlotAttr node of `v-slot` | ||
* @returns {boolean} `true` if the given node can convert to the `slot` | ||
*/ | ||
function canConvertToSlot(vSlotAttr) { | ||
if (vSlotAttr.parent.parent.name !== 'template') { | ||
return false | ||
} | ||
return true | ||
} | ||
/** | ||
* Convert to `slot` and `slot-scope`. | ||
@@ -33,3 +32,3 @@ * @param {RuleFixer} fixer fixer | ||
const key = vSlotAttr.key | ||
if (key.modifiers.length) { | ||
if (key.modifiers.length > 0) { | ||
// unknown modifiers | ||
@@ -60,3 +59,3 @@ return null | ||
} | ||
if (!attrs.length) { | ||
if (attrs.length === 0) { | ||
attrs.push('slot') // useless | ||
@@ -63,0 +62,0 @@ } |
@@ -57,3 +57,3 @@ /** | ||
nodes: scopeStack | ||
? scopeStack.nodes.slice() // make copy | ||
? [...scopeStack.nodes] // make copy | ||
: [] | ||
@@ -87,3 +87,3 @@ } | ||
RESERVED_NAMES.has(propertyName) || // this.class | this['class'] | ||
/^[0-9].*$|[^a-zA-Z0-9_$]/.test(propertyName) // this['0aaaa'] | this['foo-bar bas'] | ||
/^\d.*$|[^\w$]/.test(propertyName) // this['0aaaa'] | this['foo-bar bas'] | ||
) { | ||
@@ -90,0 +90,0 @@ return |
@@ -85,13 +85,14 @@ /** | ||
* @param {EventDirective[]} events | ||
* @returns { { [key: string]: EventDirective[] } } { click: [], keypress: [] } | ||
* @returns { { [key: string]: EventDirective[] } } { click: [], keypress: [] } | ||
*/ | ||
function groupEvents(events) { | ||
return events.reduce((acc, event) => { | ||
if (acc[event.name]) { | ||
acc[event.name].push(event) | ||
} else { | ||
acc[event.name] = [event] | ||
/** @type { { [key: string]: EventDirective[] } } */ | ||
const grouped = {} | ||
for (const event of events) { | ||
if (!grouped[event.name]) { | ||
grouped[event.name] = [] | ||
} | ||
return acc | ||
}, /** @type { { [key: string]: EventDirective[] } }*/ ({})) | ||
grouped[event.name].push(event) | ||
} | ||
return grouped | ||
} | ||
@@ -145,5 +146,5 @@ | ||
return ( | ||
baseEvent.modifiers.length >= 1 && | ||
baseEvent.modifiers.length > 0 && | ||
baseEventSystemModifiers !== eventSystemModifiers && | ||
baseEventSystemModifiers.indexOf(eventSystemModifiers) > -1 | ||
baseEventSystemModifiers.includes(eventSystemModifiers) | ||
) | ||
@@ -159,10 +160,12 @@ } | ||
function findConflictedEvents(events) { | ||
return events.reduce((acc, event) => { | ||
return [ | ||
...acc, | ||
/** @type {EventDirective[]} */ | ||
const conflictedEvents = [] | ||
for (const event of events) { | ||
conflictedEvents.push( | ||
...events | ||
.filter((evt) => !acc.find((e) => evt === e)) // No duplicates | ||
.filter((evt) => !conflictedEvents.includes(evt)) // No duplicates | ||
.filter(hasConflictedModifiers.bind(null, event)) | ||
] | ||
}, /** @type {EventDirective[]} */ ([])) | ||
) | ||
} | ||
return conflictedEvents | ||
} | ||
@@ -210,3 +213,3 @@ | ||
Object.keys(grouppedEvents).forEach((eventName) => { | ||
for (const eventName of Object.keys(grouppedEvents)) { | ||
const eventsInGroup = grouppedEvents[eventName] | ||
@@ -217,7 +220,7 @@ const hasEventWithKeyModifier = eventsInGroup.some((event) => | ||
if (!hasEventWithKeyModifier) return | ||
if (!hasEventWithKeyModifier) continue | ||
const conflictedEvents = findConflictedEvents(eventsInGroup) | ||
conflictedEvents.forEach((e) => { | ||
for (const e of conflictedEvents) { | ||
context.report({ | ||
@@ -228,4 +231,4 @@ node: e.node, | ||
}) | ||
}) | ||
}) | ||
} | ||
} | ||
} | ||
@@ -232,0 +235,0 @@ }) |
@@ -44,10 +44,13 @@ /** | ||
let message = "Expected 'v-bind' before ':'." | ||
if (preferShorthand) { | ||
message = "Unexpected 'v-bind' before ':'." | ||
} else if (shorthandProp) { | ||
message = "Expected 'v-bind:' instead of '.'." | ||
} | ||
context.report({ | ||
node, | ||
loc: node.loc, | ||
message: preferShorthand | ||
? "Unexpected 'v-bind' before ':'." | ||
: shorthandProp | ||
? "Expected 'v-bind:' instead of '.'." | ||
: /* otherwise */ "Expected 'v-bind' before ':'.", | ||
message, | ||
*fix(fixer) { | ||
@@ -54,0 +57,0 @@ if (preferShorthand) { |
@@ -45,3 +45,3 @@ /** | ||
tokenStore.getTokenAfter( | ||
node.left.length | ||
node.left.length > 0 | ||
? node.left[node.left.length - 1] | ||
@@ -48,0 +48,0 @@ : tokenStore.getFirstToken(node), |
@@ -77,5 +77,3 @@ 'use strict' | ||
!name.includes('_') | ||
? (fixer) => { | ||
return fixer.replaceText(argument, caseConverter(name)) | ||
} | ||
? (fixer) => fixer.replaceText(argument, caseConverter(name)) | ||
: null | ||
@@ -89,5 +87,3 @@ }) | ||
function isIgnoredAttribute(value) { | ||
const isIgnored = ignoredAttributes.some((attr) => { | ||
return value.includes(attr) | ||
}) | ||
const isIgnored = ignoredAttributes.some((attr) => value.includes(attr)) | ||
@@ -94,0 +90,0 @@ if (isIgnored) { |
@@ -33,2 +33,41 @@ /** | ||
/** | ||
* @param {VOnExpression} node | ||
* @returns {CallExpression | null} | ||
*/ | ||
function getInvalidNeverCallExpression(node) { | ||
/** @type {ExpressionStatement} */ | ||
let exprStatement | ||
let body = node.body | ||
while (true) { | ||
const statements = body.filter((st) => st.type !== 'EmptyStatement') | ||
if (statements.length !== 1) { | ||
return null | ||
} | ||
const statement = statements[0] | ||
if (statement.type === 'ExpressionStatement') { | ||
exprStatement = statement | ||
break | ||
} | ||
if (statement.type === 'BlockStatement') { | ||
body = statement.body | ||
continue | ||
} | ||
return null | ||
} | ||
const expression = exprStatement.expression | ||
if (expression.type !== 'CallExpression' || expression.arguments.length > 0) { | ||
return null | ||
} | ||
if (expression.optional) { | ||
// Allow optional chaining | ||
return null | ||
} | ||
const callee = expression.callee | ||
if (callee.type !== 'Identifier') { | ||
return null | ||
} | ||
return expression | ||
} | ||
// ------------------------------------------------------------------------------ | ||
@@ -65,41 +104,2 @@ // Rule Definition | ||
/** | ||
* @param {VOnExpression} node | ||
* @returns {CallExpression | null} | ||
*/ | ||
function getInvalidNeverCallExpression(node) { | ||
/** @type {ExpressionStatement} */ | ||
let exprStatement | ||
let body = node.body | ||
while (true) { | ||
const statements = body.filter((st) => st.type !== 'EmptyStatement') | ||
if (statements.length !== 1) { | ||
return null | ||
} | ||
const statement = statements[0] | ||
if (statement.type === 'ExpressionStatement') { | ||
exprStatement = statement | ||
break | ||
} | ||
if (statement.type === 'BlockStatement') { | ||
body = statement.body | ||
continue | ||
} | ||
return null | ||
} | ||
const expression = exprStatement.expression | ||
if (expression.type !== 'CallExpression' || expression.arguments.length) { | ||
return null | ||
} | ||
if (expression.optional) { | ||
// Allow optional chaining | ||
return null | ||
} | ||
const callee = expression.callee | ||
if (callee.type !== 'Identifier') { | ||
return null | ||
} | ||
return expression | ||
} | ||
if (always) { | ||
@@ -158,7 +158,8 @@ return utils.defineTemplateBodyVisitor(context, { | ||
if (expression.callee.type === 'Identifier') { | ||
if (useArgsMethods.has(expression.callee.name)) { | ||
// The behavior of target method can change given the arguments. | ||
return | ||
} | ||
if ( | ||
expression.callee.type === 'Identifier' && | ||
useArgsMethods.has(expression.callee.name) | ||
) { | ||
// The behavior of target method can change given the arguments. | ||
return | ||
} | ||
@@ -165,0 +166,0 @@ |
@@ -15,3 +15,3 @@ /** | ||
description: 'enforce valid `defineEmits` compiler macro', | ||
categories: ['vue3-essential'], | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/valid-define-emits.html' | ||
@@ -51,4 +51,4 @@ }, | ||
if (node.arguments.length >= 1) { | ||
if (node.typeParameters && node.typeParameters.params.length >= 1) { | ||
if (node.arguments.length > 0) { | ||
if (node.typeParameters && node.typeParameters.params.length > 0) { | ||
// `defineEmits` has both a literal type and an argument. | ||
@@ -78,22 +78,19 @@ context.report({ | ||
variable && | ||
variable.references.some((ref) => ref.identifier === node) | ||
variable.references.some((ref) => ref.identifier === node) && | ||
variable.defs.length > 0 && | ||
variable.defs.every( | ||
(def) => | ||
def.type !== 'ImportBinding' && | ||
utils.inRange(scriptSetup.range, def.name) && | ||
!utils.inRange(defineEmits.range, def.name) | ||
) | ||
) { | ||
if ( | ||
variable.defs.length && | ||
variable.defs.every( | ||
(def) => | ||
def.type !== 'ImportBinding' && | ||
utils.inRange(scriptSetup.range, def.name) && | ||
!utils.inRange(defineEmits.range, def.name) | ||
) | ||
) { | ||
if (utils.withinTypeNode(node)) { | ||
continue | ||
} | ||
//`defineEmits` are referencing locally declared variables. | ||
context.report({ | ||
node, | ||
messageId: 'referencingLocally' | ||
}) | ||
if (utils.withinTypeNode(node)) { | ||
continue | ||
} | ||
//`defineEmits` are referencing locally declared variables. | ||
context.report({ | ||
node, | ||
messageId: 'referencingLocally' | ||
}) | ||
} | ||
@@ -115,3 +112,3 @@ } | ||
'Program:exit'() { | ||
if (!defineEmitsNodes.length) { | ||
if (defineEmitsNodes.length === 0) { | ||
return | ||
@@ -118,0 +115,0 @@ } |
@@ -15,3 +15,3 @@ /** | ||
description: 'enforce valid `defineProps` compiler macro', | ||
categories: ['vue3-essential'], | ||
categories: ['vue3-essential', 'essential'], | ||
url: 'https://eslint.vuejs.org/rules/valid-define-props.html' | ||
@@ -52,4 +52,4 @@ }, | ||
if (node.arguments.length >= 1) { | ||
if (node.typeParameters && node.typeParameters.params.length >= 1) { | ||
if (node.arguments.length > 0) { | ||
if (node.typeParameters && node.typeParameters.params.length > 0) { | ||
// `defineProps` has both a literal type and an argument. | ||
@@ -79,22 +79,19 @@ context.report({ | ||
variable && | ||
variable.references.some((ref) => ref.identifier === node) | ||
variable.references.some((ref) => ref.identifier === node) && | ||
variable.defs.length > 0 && | ||
variable.defs.every( | ||
(def) => | ||
def.type !== 'ImportBinding' && | ||
utils.inRange(scriptSetup.range, def.name) && | ||
!utils.inRange(defineProps.range, def.name) | ||
) | ||
) { | ||
if ( | ||
variable.defs.length && | ||
variable.defs.every( | ||
(def) => | ||
def.type !== 'ImportBinding' && | ||
utils.inRange(scriptSetup.range, def.name) && | ||
!utils.inRange(defineProps.range, def.name) | ||
) | ||
) { | ||
if (utils.withinTypeNode(node)) { | ||
continue | ||
} | ||
//`defineProps` are referencing locally declared variables. | ||
context.report({ | ||
node, | ||
messageId: 'referencingLocally' | ||
}) | ||
if (utils.withinTypeNode(node)) { | ||
continue | ||
} | ||
//`defineProps` are referencing locally declared variables. | ||
context.report({ | ||
node, | ||
messageId: 'referencingLocally' | ||
}) | ||
} | ||
@@ -116,3 +113,3 @@ } | ||
'Program:exit'() { | ||
if (!definePropsNodes.length) { | ||
if (definePropsNodes.length === 0) { | ||
return | ||
@@ -119,0 +116,0 @@ } |
@@ -50,3 +50,3 @@ /** | ||
if (hasSrc && rootElements.length) { | ||
if (hasSrc && rootElements.length > 0) { | ||
for (const element of rootElements) { | ||
@@ -53,0 +53,0 @@ context.report({ |
@@ -88,27 +88,36 @@ /** | ||
while ((expression = expressions.pop())) { | ||
if ( | ||
expression.type === 'ObjectExpression' || | ||
expression.type === 'ClassExpression' || | ||
expression.type === 'ArrowFunctionExpression' || | ||
expression.type === 'FunctionExpression' || | ||
expression.type === 'Literal' || | ||
expression.type === 'TemplateLiteral' || | ||
expression.type === 'UnaryExpression' || | ||
expression.type === 'BinaryExpression' || | ||
expression.type === 'UpdateExpression' | ||
) { | ||
context.report({ | ||
node: expression, | ||
messageId: 'expectedArray' | ||
}) | ||
} else if (expression.type === 'AssignmentExpression') { | ||
expressions.push(expression.right) | ||
} else if (expression.type === 'TSAsExpression') { | ||
expressions.push(expression.expression) | ||
} else if (expression.type === 'SequenceExpression') { | ||
expressions.push( | ||
expression.expressions[expression.expressions.length - 1] | ||
) | ||
} else if (expression.type === 'ConditionalExpression') { | ||
expressions.push(expression.consequent, expression.alternate) | ||
switch (expression.type) { | ||
case 'ObjectExpression': | ||
case 'ClassExpression': | ||
case 'ArrowFunctionExpression': | ||
case 'FunctionExpression': | ||
case 'Literal': | ||
case 'TemplateLiteral': | ||
case 'UnaryExpression': | ||
case 'BinaryExpression': | ||
case 'UpdateExpression': { | ||
context.report({ | ||
node: expression, | ||
messageId: 'expectedArray' | ||
}) | ||
break | ||
} | ||
case 'AssignmentExpression': { | ||
expressions.push(expression.right) | ||
break | ||
} | ||
case 'TSAsExpression': { | ||
expressions.push(expression.expression) | ||
break | ||
} | ||
case 'SequenceExpression': { | ||
expressions.push( | ||
expression.expressions[expression.expressions.length - 1] | ||
) | ||
break | ||
} | ||
case 'ConditionalExpression': { | ||
expressions.push(expression.consequent, expression.alternate) | ||
break | ||
} | ||
} | ||
@@ -115,0 +124,0 @@ } |
@@ -59,5 +59,5 @@ /** | ||
// keyCode | ||
Number.isInteger(parseInt(modifier, 10)) || | ||
Number.isInteger(Number.parseInt(modifier, 10)) || | ||
// keyAlias (an Unicode character) | ||
Array.from(modifier).length === 1 || | ||
[...modifier].length === 1 || | ||
// keyAlias (special keys) | ||
@@ -64,0 +64,0 @@ KEY_ALIASES.has(modifier) || |
@@ -103,3 +103,3 @@ /** | ||
) | ||
.filter((slots) => slots.length >= 1) | ||
.filter((slots) => slots.length > 0) | ||
} | ||
@@ -146,10 +146,7 @@ | ||
} | ||
for (const v of a.variables) { | ||
if (!checkedVarNames.has(v.id.name)) { | ||
if (b.variables.every((bv) => v.id.name !== bv.id.name)) { | ||
return false | ||
} | ||
} | ||
} | ||
return true | ||
return a.variables.every( | ||
(v) => | ||
checkedVarNames.has(v.id.name) || | ||
b.variables.some((bv) => v.id.name === bv.id.name) | ||
) | ||
@@ -181,3 +178,3 @@ /** | ||
expr && getUsingIterationVars(vSlot.key.argument, vSlot.parent.parent) | ||
return expr && variables && variables.length ? { expr, variables } : null | ||
return expr && variables && variables.length > 0 ? { expr, variables } : null | ||
} | ||
@@ -241,4 +238,4 @@ | ||
return allowModifiers | ||
? vSlot.key.argument == null && vSlot.key.modifiers.length >= 1 | ||
: vSlot.key.modifiers.length >= 1 | ||
? vSlot.key.argument == null && vSlot.key.modifiers.length > 0 | ||
: vSlot.key.modifiers.length > 0 | ||
} | ||
@@ -324,3 +321,3 @@ | ||
} | ||
if (ownerElement === element && vSlotGroupsOnChildren.length >= 1) { | ||
if (ownerElement === element && vSlotGroupsOnChildren.length > 0) { | ||
context.report({ | ||
@@ -327,0 +324,0 @@ node, |
@@ -44,11 +44,8 @@ // ------------------------------------------------------------------------------ | ||
function isKebabCase(str) { | ||
if ( | ||
hasUpper(str) || | ||
hasSymbols(str) || | ||
/^-/u.exec(str) || // starts with hyphen is not kebab-case | ||
/_|--|\s/u.exec(str) | ||
) { | ||
return false | ||
} | ||
return true | ||
return ( | ||
!hasUpper(str) && | ||
!hasSymbols(str) && | ||
!str.startsWith('-') && // starts with hyphen is not kebab-case | ||
!/_|--|\s/u.test(str) | ||
) | ||
} | ||
@@ -73,6 +70,3 @@ | ||
function isSnakeCase(str) { | ||
if (hasUpper(str) || hasSymbols(str) || /-|__|\s/u.exec(str)) { | ||
return false | ||
} | ||
return true | ||
return !hasUpper(str) && !hasSymbols(str) && !/-|__|\s/u.test(str) | ||
} | ||
@@ -97,10 +91,3 @@ | ||
function isCamelCase(str) { | ||
if ( | ||
hasSymbols(str) || | ||
/^[A-Z]/u.exec(str) || | ||
/-|_|\s/u.exec(str) // kebab or snake or space | ||
) { | ||
return false | ||
} | ||
return true | ||
return !hasSymbols(str) && !/^[A-Z]/u.test(str) && !/-|_|\s/u.test(str) | ||
} | ||
@@ -122,10 +109,3 @@ | ||
function isPascalCase(str) { | ||
if ( | ||
hasSymbols(str) || | ||
/^[a-z]/u.exec(str) || | ||
/-|_|\s/u.exec(str) // kebab or snake or space | ||
) { | ||
return false | ||
} | ||
return true | ||
return !hasSymbols(str) && !/^[a-z]/u.test(str) && !/-|_|\s/u.test(str) | ||
} | ||
@@ -132,0 +112,0 @@ |
@@ -25,3 +25,3 @@ /** | ||
const IE_CONDITIONAL_IF = /^\[if\s+/ | ||
const IE_CONDITIONAL_ENDIF = /\[endif\]$/ | ||
const IE_CONDITIONAL_ENDIF = /\[endif]$/ | ||
@@ -28,0 +28,0 @@ /** @type { 'HTMLCommentOpen' } */ |
@@ -194,3 +194,3 @@ /** | ||
} | ||
if (node.implements != null && node.implements.length) { | ||
if (node.implements != null && node.implements.length > 0) { | ||
const classToken = tokenStore.getFirstToken(node) | ||
@@ -727,3 +727,3 @@ const implementsToken = tokenStore.getTokenBefore(node.implements[0]) | ||
} | ||
if (node.extends != null && node.extends.length) { | ||
if (node.extends != null && node.extends.length > 0) { | ||
const extendsToken = tokenStore.getTokenBefore(node.extends[0]) | ||
@@ -1282,3 +1282,3 @@ setOffset(extendsToken, 1, interfaceToken) | ||
const parent = /** @type {any} */ (node.parent) | ||
const { decorators } = parent | ||
const { decorators, range } = parent | ||
if (!decorators || decorators.length === 0) { | ||
@@ -1288,3 +1288,3 @@ return | ||
if (decorators[0] === node) { | ||
if (parent.range[0] === node.range[0]) { | ||
if (range[0] === node.range[0]) { | ||
const startParentToken = tokenStore.getTokenAfter( | ||
@@ -1291,0 +1291,0 @@ decorators[decorators.length - 1] |
@@ -212,19 +212,19 @@ /** | ||
: NEVER | ||
} | ||
} | ||
/** | ||
* @param {Pattern} pattern | ||
* @returns {IPropertyReferences} | ||
*/ | ||
function getNestFromPattern(pattern) { | ||
if (pattern.type === 'ObjectPattern') { | ||
return extractFromObjectPattern(pattern) | ||
} | ||
if (pattern.type === 'Identifier') { | ||
return extractFromIdentifier(pattern) | ||
} else if (pattern.type === 'AssignmentPattern') { | ||
return getNestFromPattern(pattern.left) | ||
} | ||
return ANY | ||
} | ||
/** | ||
* @param {Pattern} pattern | ||
* @returns {IPropertyReferences} | ||
*/ | ||
function getNestFromPattern(pattern) { | ||
if (pattern.type === 'ObjectPattern') { | ||
return extractFromObjectPattern(pattern) | ||
} | ||
if (pattern.type === 'Identifier') { | ||
return extractFromIdentifier(pattern) | ||
} else if (pattern.type === 'AssignmentPattern') { | ||
return getNestFromPattern(pattern.left) | ||
} | ||
return ANY | ||
} | ||
@@ -250,54 +250,43 @@ | ||
const parent = node.parent | ||
if (parent.type === 'AssignmentExpression') { | ||
if (withInTemplate) { | ||
return NEVER | ||
} | ||
if (parent.right === node) { | ||
switch (parent.type) { | ||
case 'AssignmentExpression': { | ||
// `({foo} = arg)` | ||
return extractFromPattern(parent.left) | ||
return !withInTemplate && parent.right === node | ||
? extractFromPattern(parent.left) | ||
: NEVER | ||
} | ||
return NEVER | ||
} else if (parent.type === 'VariableDeclarator') { | ||
if (withInTemplate) { | ||
return NEVER | ||
} | ||
if (parent.init === node) { | ||
case 'VariableDeclarator': { | ||
// `const {foo} = arg` | ||
// `const foo = arg` | ||
return extractFromPattern(parent.id) | ||
return !withInTemplate && parent.init === node | ||
? extractFromPattern(parent.id) | ||
: NEVER | ||
} | ||
return NEVER | ||
} else if (parent.type === 'MemberExpression') { | ||
if (parent.object === node) { | ||
// `arg.foo` | ||
const name = utils.getStaticPropertyName(parent) | ||
if (name) { | ||
return new PropertyReferencesForMember(parent, name, withInTemplate) | ||
} else { | ||
return ANY | ||
case 'MemberExpression': { | ||
if (parent.object === node) { | ||
// `arg.foo` | ||
const name = utils.getStaticPropertyName(parent) | ||
return name | ||
? new PropertyReferencesForMember(parent, name, withInTemplate) | ||
: ANY | ||
} | ||
} | ||
return NEVER | ||
} else if (parent.type === 'CallExpression') { | ||
if (withInTemplate) { | ||
return NEVER | ||
} | ||
const argIndex = parent.arguments.indexOf(node) | ||
if (argIndex > -1) { | ||
case 'CallExpression': { | ||
const argIndex = parent.arguments.indexOf(node) | ||
// `foo(arg)` | ||
return extractFromCall(parent, argIndex) | ||
return !withInTemplate && argIndex > -1 | ||
? extractFromCall(parent, argIndex) | ||
: NEVER | ||
} | ||
} else if (parent.type === 'ChainExpression') { | ||
return extractFromExpression(parent, withInTemplate) | ||
} else if ( | ||
parent.type === 'ArrowFunctionExpression' || | ||
parent.type === 'ReturnStatement' || | ||
parent.type === 'VExpressionContainer' || | ||
parent.type === 'Property' || | ||
parent.type === 'ArrayExpression' | ||
) { | ||
// Maybe used externally. | ||
if (maybeExternalUsed(parent)) { | ||
return ANY | ||
case 'ChainExpression': { | ||
return extractFromExpression(parent, withInTemplate) | ||
} | ||
case 'ArrowFunctionExpression': | ||
case 'ReturnStatement': | ||
case 'VExpressionContainer': | ||
case 'Property': | ||
case 'ArrayExpression': { | ||
return maybeExternalUsed(parent) ? ANY : NEVER | ||
} | ||
} | ||
@@ -485,3 +474,3 @@ return NEVER | ||
function extractFromSegments(segments) { | ||
if (!segments.length) { | ||
if (segments.length === 0) { | ||
return ANY | ||
@@ -509,11 +498,9 @@ } | ||
: null | ||
if (referenceName) { | ||
return { | ||
hasProperty: (name) => name === referenceName, | ||
allProperties: () => new Map([[referenceName, { nodes: [node] }]]), | ||
getNest: (name) => (name === referenceName ? ANY : NEVER) | ||
} | ||
} else { | ||
return NEVER | ||
} | ||
return referenceName | ||
? { | ||
hasProperty: (name) => name === referenceName, | ||
allProperties: () => new Map([[referenceName, { nodes: [node] }]]), | ||
getNest: (name) => (name === referenceName ? ANY : NEVER) | ||
} | ||
: NEVER | ||
} | ||
@@ -553,5 +540,5 @@ | ||
} | ||
return extractFromName(refName, nameNode, () => { | ||
return extractFromExpression(node, false).getNest('value') | ||
}) | ||
return extractFromName(refName, nameNode, () => | ||
extractFromExpression(node, false).getNest('value') | ||
) | ||
} | ||
@@ -558,0 +545,0 @@ |
@@ -41,3 +41,3 @@ const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu | ||
function isRegExp(string) { | ||
return Boolean(RE_REGEXP_STR.exec(string)) | ||
return RE_REGEXP_STR.test(string) | ||
} | ||
@@ -44,0 +44,0 @@ |
@@ -26,3 +26,3 @@ 'use strict' | ||
astSelector = parser().astSync(selector) | ||
} catch (e) { | ||
} catch (error) { | ||
context.report({ | ||
@@ -45,7 +45,7 @@ loc: { line: 0, column: 0 }, | ||
} | ||
} catch (e) { | ||
if (e instanceof SelectorError) { | ||
} catch (error) { | ||
if (error instanceof SelectorError) { | ||
context.report({ | ||
loc: { line: 0, column: 0 }, | ||
message: e.message | ||
message: error.message | ||
}) | ||
@@ -56,3 +56,3 @@ return { | ||
} | ||
throw e | ||
throw error | ||
} | ||
@@ -81,2 +81,10 @@ } | ||
/** | ||
* @param {parser.Node|null} node | ||
* @returns {node is parser.Combinator} | ||
*/ | ||
function isDescendantCombinator(node) { | ||
return Boolean(node && node.type === 'combinator' && !node.value.trim()) | ||
} | ||
/** | ||
* Clean and get the selector child nodes. | ||
@@ -116,10 +124,2 @@ * @param {parser.Selector} selector | ||
return nodes | ||
/** | ||
* @param {parser.Node|null} node | ||
* @returns {node is parser.Combinator} | ||
*/ | ||
function isDescendantCombinator(node) { | ||
return Boolean(node && node.type === 'combinator' && !node.value.trim()) | ||
} | ||
} | ||
@@ -381,5 +381,3 @@ /** | ||
const selectors = selectorsToVElementMatcher(selector.nodes) | ||
return (element, subject) => { | ||
return !selectors(element, subject) | ||
} | ||
return (element, subject) => !selectors(element, subject) | ||
} | ||
@@ -488,22 +486,22 @@ case ':is': | ||
) | ||
} | ||
/** | ||
* @param {VElementMatcher} selectors | ||
* @param {(element: VElement) => VElement[]} getStartElements | ||
* @returns {VElementMatcher} | ||
*/ | ||
function buildVElementMatcher(selectors, getStartElements) { | ||
return (element) => { | ||
const elements = [...getStartElements(element)] | ||
/** @type {VElement|undefined} */ | ||
let curr | ||
while ((curr = elements.shift())) { | ||
const el = curr | ||
if (selectors(el, element)) { | ||
return true | ||
} | ||
elements.push(...el.children.filter(isVElement)) | ||
/** | ||
* @param {VElementMatcher} selectors | ||
* @param {(element: VElement) => VElement[]} getStartElements | ||
* @returns {VElementMatcher} | ||
*/ | ||
function buildVElementMatcher(selectors, getStartElements) { | ||
return (element) => { | ||
const elements = [...getStartElements(element)] | ||
/** @type {VElement|undefined} */ | ||
let curr | ||
while ((curr = elements.shift())) { | ||
const el = curr | ||
if (selectors(el, element)) { | ||
return true | ||
} | ||
return false | ||
elements.push(...el.children.filter(isVElement)) | ||
} | ||
return false | ||
} | ||
@@ -535,3 +533,3 @@ } | ||
return nthCheck(argument) | ||
} catch (e) { | ||
} catch (error) { | ||
throw new SelectorError( | ||
@@ -538,0 +536,0 @@ `Cannot parse An+B micro syntax (:nth-xxx() argument): '${argument}'.` |
@@ -52,3 +52,3 @@ const { isVElement } = require('..') | ||
.filter((e) => e.name === 'style') | ||
if (!styles.length) { | ||
if (styles.length === 0) { | ||
return null | ||
@@ -55,0 +55,0 @@ } |
@@ -167,39 +167,39 @@ const { findVariable } = require('eslint-utils') | ||
} | ||
} | ||
/** | ||
* @param {TSESTreeParameter} eventName | ||
* @param {TSCallSignatureDeclaration | TSFunctionType} member | ||
* @returns {IterableIterator<ComponentTypeEmit>} | ||
*/ | ||
function* extractEventNames(eventName, member) { | ||
/** | ||
* @param {TSESTreeParameter} eventName | ||
* @param {TSCallSignatureDeclaration | TSFunctionType} member | ||
* @returns {IterableIterator<ComponentTypeEmit>} | ||
*/ | ||
function* extractEventNames(eventName, member) { | ||
if ( | ||
eventName && | ||
eventName.type === 'Identifier' && | ||
eventName.typeAnnotation && | ||
eventName.typeAnnotation.type === 'TSTypeAnnotation' | ||
) { | ||
const typeNode = eventName.typeAnnotation.typeAnnotation | ||
if ( | ||
eventName && | ||
eventName.type === 'Identifier' && | ||
eventName.typeAnnotation && | ||
eventName.typeAnnotation.type === 'TSTypeAnnotation' | ||
typeNode.type === 'TSLiteralType' && | ||
typeNode.literal.type === 'Literal' | ||
) { | ||
const typeNode = eventName.typeAnnotation.typeAnnotation | ||
if ( | ||
typeNode.type === 'TSLiteralType' && | ||
typeNode.literal.type === 'Literal' | ||
) { | ||
const emitName = String(typeNode.literal.value) | ||
yield { | ||
type: 'type', | ||
key: /** @type {TSLiteralType} */ (typeNode), | ||
emitName, | ||
value: null, | ||
node: member | ||
} | ||
} else if (typeNode.type === 'TSUnionType') { | ||
for (const t of typeNode.types) { | ||
if (t.type === 'TSLiteralType' && t.literal.type === 'Literal') { | ||
const emitName = String(t.literal.value) | ||
yield { | ||
type: 'type', | ||
key: /** @type {TSLiteralType} */ (t), | ||
emitName, | ||
value: null, | ||
node: member | ||
} | ||
const emitName = String(typeNode.literal.value) | ||
yield { | ||
type: 'type', | ||
key: /** @type {TSLiteralType} */ (typeNode), | ||
emitName, | ||
value: null, | ||
node: member | ||
} | ||
} else if (typeNode.type === 'TSUnionType') { | ||
for (const t of typeNode.types) { | ||
if (t.type === 'TSLiteralType' && t.literal.type === 'Literal') { | ||
const emitName = String(t.literal.value) | ||
yield { | ||
type: 'type', | ||
key: /** @type {TSLiteralType} */ (t), | ||
emitName, | ||
value: null, | ||
node: member | ||
} | ||
@@ -206,0 +206,0 @@ } |
{ | ||
"name": "eslint-plugin-vue", | ||
"version": "8.7.1", | ||
"version": "9.0.0", | ||
"description": "Official ESLint plugin for Vue.js", | ||
@@ -51,3 +51,3 @@ "main": "lib/index.js", | ||
"engines": { | ||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" | ||
"node": "^14.17.0 || >=16.0.0" | ||
}, | ||
@@ -63,32 +63,34 @@ "peerDependencies": { | ||
"semver": "^7.3.5", | ||
"vue-eslint-parser": "^8.0.1" | ||
"vue-eslint-parser": "^9.0.1", | ||
"xml-name-validator": "^4.0.0" | ||
}, | ||
"devDependencies": { | ||
"@types/eslint": "^7.28.1", | ||
"@types/eslint": "^8.4.2", | ||
"@types/eslint-visitor-keys": "^1.0.0", | ||
"@types/natural-compare": "^1.4.0", | ||
"@types/natural-compare": "^1.4.1", | ||
"@types/node": "^13.13.5", | ||
"@types/semver": "^7.2.0", | ||
"@typescript-eslint/parser": "^5.5.0", | ||
"@vuepress/plugin-pwa": "^1.4.1", | ||
"acorn": "^8.5.0", | ||
"@types/semver": "^7.3.9", | ||
"@types/xml-name-validator": "^4.0.0", | ||
"@typescript-eslint/parser": "^5.23.0", | ||
"@vuepress/plugin-pwa": "^1.9.7", | ||
"acorn": "^8.7.1", | ||
"env-cmd": "^10.1.0", | ||
"eslint": "^8.0.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint": "^8.15.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-eslint-plugin": "^3.5.3", | ||
"eslint-plugin-import": "^2.20.2", | ||
"eslint-plugin-jsonc": "^1.4.0", | ||
"eslint-plugin-import": "^2.26.0", | ||
"eslint-plugin-jsonc": "^2.2.1", | ||
"eslint-plugin-node-dependencies": ">=0.5.0 <1.0.0", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"eslint-plugin-unicorn": "^42.0.0", | ||
"eslint-plugin-vue": "file:.", | ||
"espree": "^9.0.0", | ||
"lodash": "^4.17.21", | ||
"espree": "^9.3.2", | ||
"markdownlint-cli": "^0.31.1", | ||
"mocha": "^7.1.2", | ||
"mocha": "^10.0.0", | ||
"nyc": "^15.1.0", | ||
"prettier": "^2.4.1", | ||
"typescript": "^4.5.0", | ||
"prettier": "^2.6.2", | ||
"typescript": "^4.6.4", | ||
"vue-eslint-editor": "^1.1.0", | ||
"vuepress": "^1.8.2" | ||
"vuepress": "^1.9.7" | ||
} | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3
1149461
8
27
266
36228
+ Addedxml-name-validator@^4.0.0
+ Addedvue-eslint-parser@9.4.3(transitive)
+ Addedxml-name-validator@4.0.0(transitive)
- Removedvue-eslint-parser@8.3.0(transitive)
Updatedvue-eslint-parser@^9.0.1