diff --git a/docs/rules/use-v-on-exact.md b/docs/rules/use-v-on-exact.md
index b89b15e2c..08e0629fe 100644
--- a/docs/rules/use-v-on-exact.md
+++ b/docs/rules/use-v-on-exact.md
@@ -32,11 +32,7 @@ This rule enforce usage of `exact` modifier on `v-on` when there is another `v-o
## :wrench: Options
-```json
-{
- "vue/use-v-on-exact": ["error"]
-}
-```
+Nothing.
## :couple: Related Rules
diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js
index fc1849b18..487af7714 100644
--- a/lib/rules/component-name-in-template-casing.js
+++ b/lib/rules/component-name-in-template-casing.js
@@ -123,7 +123,8 @@ module.exports = {
if (
(!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
utils.isHtmlWellKnownElementName(node.rawName) ||
- utils.isSvgWellKnownElementName(node.rawName)
+ utils.isSvgWellKnownElementName(node.rawName) ||
+ utils.isVueBuiltInElementName(node.rawName)
) {
return false
}
diff --git a/lib/rules/define-emits-declaration.js b/lib/rules/define-emits-declaration.js
index 4daf4115c..478e1d840 100644
--- a/lib/rules/define-emits-declaration.js
+++ b/lib/rules/define-emits-declaration.js
@@ -47,7 +47,8 @@ module.exports = {
}
case 'runtime': {
- if (node.typeParameters && node.typeParameters.params.length > 0) {
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments && typeArguments.params.length > 0) {
context.report({
node,
messageId: 'hasTypeArg'
diff --git a/lib/rules/define-props-declaration.js b/lib/rules/define-props-declaration.js
index c6fe4ffdc..d355bb2fc 100644
--- a/lib/rules/define-props-declaration.js
+++ b/lib/rules/define-props-declaration.js
@@ -47,7 +47,8 @@ module.exports = {
}
case 'runtime': {
- if (node.typeParameters && node.typeParameters.params.length > 0) {
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments && typeArguments.params.length > 0) {
context.report({
node,
messageId: 'hasTypeArg'
diff --git a/lib/rules/prefer-define-options.js b/lib/rules/prefer-define-options.js
index cd9240445..90c83fa9a 100644
--- a/lib/rules/prefer-define-options.js
+++ b/lib/rules/prefer-define-options.js
@@ -34,9 +34,14 @@ module.exports = {
let defineOptionsNode = null
/** @type {ExportDefaultDeclaration | null} */
let exportDefaultDeclaration = null
+ /** @type {ImportDeclaration|null} */
+ let lastImportDeclaration = null
return utils.compositingVisitors(
utils.defineScriptSetupVisitor(context, {
+ ImportDeclaration(node) {
+ lastImportDeclaration = node
+ },
onDefineOptionsEnter(node) {
defineOptionsNode = node
}
@@ -109,10 +114,13 @@ module.exports = {
})
}
+ /** @type {VStartTag | ImportDeclaration} */
+ const insertAfterTag = lastImportDeclaration || scriptSetup.startTag
+
return [
fixer.removeRange(removeRange),
fixer.insertTextAfter(
- scriptSetup.startTag,
+ insertAfterTag,
`\ndefineOptions(${sourceCode.getText(node.declaration)})\n`
)
]
diff --git a/lib/rules/require-toggle-inside-transition.js b/lib/rules/require-toggle-inside-transition.js
index 365fbc713..bdbb5c2dd 100644
--- a/lib/rules/require-toggle-inside-transition.js
+++ b/lib/rules/require-toggle-inside-transition.js
@@ -47,7 +47,8 @@ module.exports = {
return utils.defineTemplateBodyVisitor(context, {
/** @param {VElement} node */
"VElement[name='transition'] > VElement"(node) {
- if (node.parent.children[0] !== node) {
+ const child = node.parent.children.find(utils.isVElement)
+ if (child !== node) {
return
}
verifyInsideElement(node)
diff --git a/lib/rules/require-typed-ref.js b/lib/rules/require-typed-ref.js
index 4ae12b040..2f691fec1 100644
--- a/lib/rules/require-typed-ref.js
+++ b/lib/rules/require-typed-ref.js
@@ -83,7 +83,9 @@ module.exports = {
continue
}
- if (ref.node.typeParameters == null) {
+ const typeArguments =
+ ref.node.typeArguments || ref.node.typeParameters
+ if (typeArguments == null) {
if (
ref.node.parent.type === 'VariableDeclarator' &&
ref.node.parent.id.type === 'Identifier'
diff --git a/lib/rules/valid-define-emits.js b/lib/rules/valid-define-emits.js
index 64d3fd15a..994302c60 100644
--- a/lib/rules/valid-define-emits.js
+++ b/lib/rules/valid-define-emits.js
@@ -47,8 +47,9 @@ module.exports = {
onDefineEmitsEnter(node) {
defineEmitsNodes.push(node)
+ const typeArguments = node.typeArguments || node.typeParameters
if (node.arguments.length > 0) {
- if (node.typeParameters && node.typeParameters.params.length > 0) {
+ if (typeArguments && typeArguments.params.length > 0) {
// `defineEmits` has both a literal type and an argument.
context.report({
node,
@@ -59,10 +60,7 @@ module.exports = {
emitsDefExpressions.add(node.arguments[0])
} else {
- if (
- !node.typeParameters ||
- node.typeParameters.params.length === 0
- ) {
+ if (!typeArguments || typeArguments.params.length === 0) {
emptyDefineEmits = node
}
}
diff --git a/lib/rules/valid-define-options.js b/lib/rules/valid-define-options.js
index 784ac3fbd..c1cd5b993 100644
--- a/lib/rules/valid-define-options.js
+++ b/lib/rules/valid-define-options.js
@@ -74,9 +74,10 @@ module.exports = {
})
}
- if (node.typeParameters) {
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments) {
context.report({
- node: node.typeParameters,
+ node: typeArguments,
messageId: 'typeArgs'
})
}
diff --git a/lib/rules/valid-define-props.js b/lib/rules/valid-define-props.js
index 43cce1d3c..ecde56f15 100644
--- a/lib/rules/valid-define-props.js
+++ b/lib/rules/valid-define-props.js
@@ -48,8 +48,9 @@ module.exports = {
onDefinePropsEnter(node) {
definePropsNodes.push(node)
+ const typeArguments = node.typeArguments || node.typeParameters
if (node.arguments.length > 0) {
- if (node.typeParameters && node.typeParameters.params.length > 0) {
+ if (typeArguments && typeArguments.params.length > 0) {
// `defineProps` has both a literal type and an argument.
context.report({
node,
@@ -60,10 +61,7 @@ module.exports = {
propsDefExpressions.add(node.arguments[0])
} else {
- if (
- !node.typeParameters ||
- node.typeParameters.params.length === 0
- ) {
+ if (!typeArguments || typeArguments.params.length === 0) {
emptyDefineProps = node
}
}
diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js
index 5b97f37b5..6119406e7 100644
--- a/lib/utils/indent-common.js
+++ b/lib/utils/indent-common.js
@@ -1168,21 +1168,22 @@ module.exports.defineVisitor = function create(
},
/** @param {CallExpression} node */
CallExpression(node) {
+ const typeArguments = node.typeArguments || node.typeParameters
const firstToken = tokenStore.getFirstToken(node)
const rightToken = tokenStore.getLastToken(node)
const leftToken = /** @type {Token} */ (
tokenStore.getTokenAfter(
- node.typeParameters || node.callee,
+ typeArguments || node.callee,
isOpeningParenToken
)
)
- if (node.typeParameters) {
- setOffset(tokenStore.getFirstToken(node.typeParameters), 1, firstToken)
+ if (typeArguments) {
+ setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken)
}
for (const optionalToken of tokenStore.getTokensBetween(
- tokenStore.getLastToken(node.typeParameters || node.callee),
+ tokenStore.getLastToken(typeArguments || node.callee),
leftToken,
isOptionalToken
)) {
@@ -1694,19 +1695,20 @@ module.exports.defineVisitor = function create(
},
/** @param {NewExpression} node */
NewExpression(node) {
+ const typeArguments = node.typeArguments || node.typeParameters
const newToken = tokenStore.getFirstToken(node)
const calleeToken = tokenStore.getTokenAfter(newToken)
const rightToken = tokenStore.getLastToken(node)
const leftToken = isClosingParenToken(rightToken)
? tokenStore.getFirstTokenBetween(
- node.typeParameters || node.callee,
+ typeArguments || node.callee,
rightToken,
isOpeningParenToken
)
: null
- if (node.typeParameters) {
- setOffset(tokenStore.getFirstToken(node.typeParameters), 1, calleeToken)
+ if (typeArguments) {
+ setOffset(tokenStore.getFirstToken(typeArguments), 1, calleeToken)
}
setOffset(calleeToken, 1, newToken)
diff --git a/lib/utils/indent-ts.js b/lib/utils/indent-ts.js
index c6f146f66..a4323b38f 100644
--- a/lib/utils/indent-ts.js
+++ b/lib/utils/indent-ts.js
@@ -332,9 +332,10 @@ function defineVisitor({
* @param {TSTypeReference | TSInstantiationExpression} node
*/
'TSTypeReference, TSInstantiationExpression'(node) {
- if (node.typeParameters) {
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments) {
const firstToken = tokenStore.getFirstToken(node)
- setOffset(tokenStore.getFirstToken(node.typeParameters), 1, firstToken)
+ setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken)
}
},
/**
diff --git a/lib/utils/index.js b/lib/utils/index.js
index 0c9a4ac99..22119ae98 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -54,6 +54,7 @@ const VUE2_BUILTIN_COMPONENT_NAMES = new Set(
const VUE3_BUILTIN_COMPONENT_NAMES = new Set(
require('./vue3-builtin-components')
)
+const VUE_BUILTIN_ELEMENT_NAMES = new Set(require('./vue-builtin-elements'))
const path = require('path')
const vueEslintParser = require('vue-eslint-parser')
const { traverseNodes, getFallbackKeys, NS } = vueEslintParser.AST
@@ -867,6 +868,15 @@ module.exports = {
)
},
+ /**
+ * Check whether the given name is Vue builtin element name or not.
+ * @param {string} name The name to check.
+ * @returns {boolean} `true` if the name is a builtin Vue element name
+ */
+ isVueBuiltInElementName(name) {
+ return VUE_BUILTIN_ELEMENT_NAMES.has(name.toLowerCase())
+ },
+
/**
* Check whether the given name is Vue builtin directive name or not.
* @param {string} name The name to check.
@@ -2990,11 +3000,9 @@ function getComponentPropsFromDefineProps(context, node) {
}
]
}
- if (node.typeParameters && node.typeParameters.params.length > 0) {
- return getComponentPropsFromTypeDefine(
- context,
- node.typeParameters.params[0]
- )
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments && typeArguments.params.length > 0) {
+ return getComponentPropsFromTypeDefine(context, typeArguments.params[0])
}
return [
{
@@ -3025,11 +3033,9 @@ function getComponentEmitsFromDefineEmits(context, node) {
}
]
}
- if (node.typeParameters && node.typeParameters.params.length > 0) {
- return getComponentEmitsFromTypeDefine(
- context,
- node.typeParameters.params[0]
- )
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments && typeArguments.params.length > 0) {
+ return getComponentEmitsFromTypeDefine(context, typeArguments.params[0])
}
return [
{
diff --git a/lib/utils/ts-utils/ts-ast.js b/lib/utils/ts-utils/ts-ast.js
index d019b77cd..8a95dd9b1 100644
--- a/lib/utils/ts-utils/ts-ast.js
+++ b/lib/utils/ts-utils/ts-ast.js
@@ -430,33 +430,28 @@ function inferRuntimeType(context, node, checked = new Set()) {
return ['Array']
}
case 'NonNullable': {
- if (node.typeParameters && node.typeParameters.params[0]) {
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments && typeArguments.params[0]) {
return inferRuntimeType(
context,
- node.typeParameters.params[0],
+ typeArguments.params[0],
checked
).filter((t) => t !== 'null')
}
break
}
case 'Extract': {
- if (node.typeParameters && node.typeParameters.params[1]) {
- return inferRuntimeType(
- context,
- node.typeParameters.params[1],
- checked
- )
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments && typeArguments.params[1]) {
+ return inferRuntimeType(context, typeArguments.params[1], checked)
}
break
}
case 'Exclude':
case 'OmitThisParameter': {
- if (node.typeParameters && node.typeParameters.params[0]) {
- return inferRuntimeType(
- context,
- node.typeParameters.params[0],
- checked
- )
+ const typeArguments = node.typeArguments || node.typeParameters
+ if (typeArguments && typeArguments.params[0]) {
+ return inferRuntimeType(context, typeArguments.params[0], checked)
}
break
}
diff --git a/lib/utils/vue-builtin-elements.js b/lib/utils/vue-builtin-elements.js
new file mode 100644
index 000000000..70ddb6b7d
--- /dev/null
+++ b/lib/utils/vue-builtin-elements.js
@@ -0,0 +1 @@
+module.exports = ['template', 'slot', 'component']
diff --git a/lib/utils/vue3-export-names.json b/lib/utils/vue3-export-names.json
index 15b72e2fc..f676e8440 100644
--- a/lib/utils/vue3-export-names.json
+++ b/lib/utils/vue3-export-names.json
@@ -276,6 +276,7 @@
"IframeHTMLAttributes",
"ImgHTMLAttributes",
"InsHTMLAttributes",
+ "InputTypeHTMLAttribute",
"InputHTMLAttributes",
"KeygenHTMLAttributes",
"LabelHTMLAttributes",
diff --git a/package.json b/package.json
index eb3750f4a..274aa2da2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-vue",
- "version": "9.17.0",
+ "version": "9.18.0",
"description": "Official ESLint plugin for Vue.js",
"main": "lib/index.js",
"scripts": {
diff --git a/tests/integrations/eslint-plugin-import.js b/tests/integrations/eslint-plugin-import.js
index a408fdcdd..4e26508ce 100644
--- a/tests/integrations/eslint-plugin-import.js
+++ b/tests/integrations/eslint-plugin-import.js
@@ -30,10 +30,12 @@ describe('Integration with eslint-plugin-import', () => {
if (
!semver.satisfies(
process.version,
- require(path.join(
- __dirname,
- 'eslint-plugin-import/node_modules/eslint/package.json'
- )).engines.node
+ require(
+ path.join(
+ __dirname,
+ 'eslint-plugin-import/node_modules/eslint/package.json'
+ )
+ ).engines.node
)
) {
return
diff --git a/tests/lib/rules/component-name-in-template-casing.js b/tests/lib/rules/component-name-in-template-casing.js
index 3aa4ee43d..992063808 100644
--- a/tests/lib/rules/component-name-in-template-casing.js
+++ b/tests/lib/rules/component-name-in-template-casing.js
@@ -86,6 +86,10 @@ tester.run('component-name-in-template-casing', rule, {
code: '',
options: ['PascalCase', { registeredComponentsOnly: false }]
},
+ {
+ code: '',
+ options: ['PascalCase', { registeredComponentsOnly: false }]
+ },
// kebab-case
{
@@ -108,6 +112,10 @@ tester.run('component-name-in-template-casing', rule, {
code: '',
options: ['kebab-case', { registeredComponentsOnly: false }]
},
+ {
+ code: '',
+ options: ['kebab-case', { registeredComponentsOnly: false }]
+ },
// ignores
{
@@ -859,7 +867,7 @@ tester.run('component-name-in-template-casing', rule, {
`,
output: `
-
+
@@ -868,7 +876,6 @@ tester.run('component-name-in-template-casing', rule, {
`,
options: ['PascalCase', { registeredComponentsOnly: false }],
errors: [
- 'Component name "component" is not PascalCase.',
'Component name "suspense" is not PascalCase.',
'Component name "teleport" is not PascalCase.',
'Component name "client-only" is not PascalCase.',
@@ -1025,7 +1032,7 @@ tester.run('component-name-in-template-casing', rule, {
-
+
`,
errors: [
@@ -1058,11 +1065,6 @@ tester.run('component-name-in-template-casing', rule, {
message: 'Component name "hello-world5" is not PascalCase.',
line: 18,
column: 17
- },
- {
- message: 'Component name "component" is not PascalCase.',
- line: 19,
- column: 17
}
]
}
diff --git a/tests/lib/rules/prefer-define-options.js b/tests/lib/rules/prefer-define-options.js
index 2fd9bdfec..f3d9d88b0 100644
--- a/tests/lib/rules/prefer-define-options.js
+++ b/tests/lib/rules/prefer-define-options.js
@@ -104,6 +104,32 @@ defineOptions({ name: 'Foo' })
line: 4
}
]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+ `,
+ output: `
+
+ `,
+ errors: [
+ {
+ message: 'Use `defineOptions` instead of default export.',
+ line: 7
+ }
+ ]
}
]
})
diff --git a/tests/lib/rules/require-toggle-inside-transition.js b/tests/lib/rules/require-toggle-inside-transition.js
index 73ec47206..0b64daaa3 100644
--- a/tests/lib/rules/require-toggle-inside-transition.js
+++ b/tests/lib/rules/require-toggle-inside-transition.js
@@ -97,6 +97,11 @@ tester.run('require-toggle-inside-transition', rule, {
filename: 'test.vue',
code: '',
errors: [{ messageId: 'expected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: ' ',
+ errors: [{ messageId: 'expected' }]
}
]
})
diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts
index 944246411..db3a7f39c 100644
--- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts
+++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts
@@ -519,7 +519,10 @@ export interface CallExpression extends HasParentNode {
callee: Expression | Super
arguments: (Expression | SpreadElement)[]
optional: boolean
- typeParameters?: TS.TSTypeParameterInstantiation
+ typeArguments?: TS.TSTypeParameterInstantiation
+
+ /* @deprecated */
+ typeParameters: never
}
export interface Super extends HasParentNode {
type: 'Super'
@@ -528,7 +531,10 @@ export interface NewExpression extends HasParentNode {
type: 'NewExpression'
callee: Expression
arguments: (Expression | SpreadElement)[]
- typeParameters?: TSTypeParameterInstantiation
+ typeArguments?: TSTypeParameterInstantiation
+
+ /* @deprecated */
+ typeParameters: never
}
interface BaseMemberExpression extends HasParentNode {
type: 'MemberExpression'