Socket
Socket
Sign inDemoInstall

eslint-plugin-vue

Package Overview
Dependencies
Maintainers
5
Versions
170
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-vue - npm Package Compare versions

Comparing version 8.7.1 to 9.0.0

lib/removed-rules.js

3

lib/configs/base.js

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc