diff --git a/docs/rules/README.md b/docs/rules/README.md
index e2353b631..046ef672e 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -58,6 +58,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-deprecated-v-on-number-modifiers](./no-deprecated-v-on-number-modifiers.md) | disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+) | :wrench: |
| [vue/no-deprecated-vue-config-keycodes](./no-deprecated-vue-config-keycodes.md) | disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+) | |
| [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names | |
+| [vue/no-dupe-v-else-if](./no-dupe-v-else-if.md) | disallow duplicate conditions in `v-if` / `v-else-if` chains | |
| [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes | |
| [vue/no-lifecycle-after-await](./no-lifecycle-after-await.md) | disallow asynchronously registered lifecycle hooks | |
| [vue/no-mutating-props](./no-mutating-props.md) | disallow mutation of component props | |
@@ -170,6 +171,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | |
| [vue/no-custom-modifiers-on-v-model](./no-custom-modifiers-on-v-model.md) | disallow custom modifiers on v-model used on the component | |
| [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names | |
+| [vue/no-dupe-v-else-if](./no-dupe-v-else-if.md) | disallow duplicate conditions in `v-if` / `v-else-if` chains | |
| [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes | |
| [vue/no-multiple-template-root](./no-multiple-template-root.md) | disallow adding multiple root nodes to the template | |
| [vue/no-mutating-props](./no-mutating-props.md) | disallow mutation of component props | |
diff --git a/docs/rules/no-dupe-v-else-if.md b/docs/rules/no-dupe-v-else-if.md
new file mode 100644
index 000000000..2960bdbb1
--- /dev/null
+++ b/docs/rules/no-dupe-v-else-if.md
@@ -0,0 +1,98 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-dupe-v-else-if
+description: disallow duplicate conditions in `v-if` / `v-else-if` chains
+---
+# vue/no-dupe-v-else-if
+> disallow duplicate conditions in `v-if` / `v-else-if` chains
+
+- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+
+## :book: Rule Details
+
+This rule disallows duplicate conditions in the same `v-if` / `v-else-if` chain.
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+This rule can also detect some cases where the conditions are not identical, but the branch can never execute due to the logic of `||` and `&&` operators.
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [no-dupe-else-if]
+
+[no-dupe-else-if]: https://eslint.org/docs/rules/no-dupe-else-if
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-dupe-v-else-if.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-dupe-v-else-if.js)
diff --git a/lib/configs/essential.js b/lib/configs/essential.js
index 9005982b7..722cf1fa1 100644
--- a/lib/configs/essential.js
+++ b/lib/configs/essential.js
@@ -11,6 +11,7 @@ module.exports = {
'vue/no-async-in-computed-properties': 'error',
'vue/no-custom-modifiers-on-v-model': 'error',
'vue/no-dupe-keys': 'error',
+ 'vue/no-dupe-v-else-if': 'error',
'vue/no-duplicate-attributes': 'error',
'vue/no-multiple-template-root': 'error',
'vue/no-mutating-props': 'error',
diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js
index 432df355b..5adbbcd23 100644
--- a/lib/configs/vue3-essential.js
+++ b/lib/configs/vue3-essential.js
@@ -26,6 +26,7 @@ module.exports = {
'vue/no-deprecated-v-on-number-modifiers': 'error',
'vue/no-deprecated-vue-config-keycodes': 'error',
'vue/no-dupe-keys': 'error',
+ 'vue/no-dupe-v-else-if': 'error',
'vue/no-duplicate-attributes': 'error',
'vue/no-lifecycle-after-await': 'error',
'vue/no-mutating-props': 'error',
diff --git a/lib/index.js b/lib/index.js
index ae86686a7..d8ab65c63 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -67,6 +67,7 @@ module.exports = {
'no-deprecated-v-on-number-modifiers': require('./rules/no-deprecated-v-on-number-modifiers'),
'no-deprecated-vue-config-keycodes': require('./rules/no-deprecated-vue-config-keycodes'),
'no-dupe-keys': require('./rules/no-dupe-keys'),
+ 'no-dupe-v-else-if': require('./rules/no-dupe-v-else-if'),
'no-duplicate-attr-inheritance': require('./rules/no-duplicate-attr-inheritance'),
'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
'no-empty-component-block': require('./rules/no-empty-component-block'),
diff --git a/lib/rules/no-dupe-v-else-if.js b/lib/rules/no-dupe-v-else-if.js
new file mode 100644
index 000000000..8e3a3dec1
--- /dev/null
+++ b/lib/rules/no-dupe-v-else-if.js
@@ -0,0 +1,193 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * @typedef {NonNullable} VExpression
+ */
+/**
+ * @typedef {object} OrOperands
+ * @property {VExpression} OrOperands.node
+ * @property {AndOperands[]} OrOperands.operands
+ *
+ * @typedef {object} AndOperands
+ * @property {VExpression} AndOperands.node
+ * @property {VExpression[]} AndOperands.operands
+ */
+/**
+ * Splits the given node by the given logical operator.
+ * @param {string} operator Logical operator `||` or `&&`.
+ * @param {VExpression} node The node to split.
+ * @returns {VExpression[]} Array of conditions that makes the node when joined by the operator.
+ */
+function splitByLogicalOperator(operator, node) {
+ if (node.type === 'LogicalExpression' && node.operator === operator) {
+ return [
+ ...splitByLogicalOperator(operator, node.left),
+ ...splitByLogicalOperator(operator, node.right)
+ ]
+ }
+ return [node]
+}
+
+/**
+ * @param {VExpression} node
+ */
+function splitByOr(node) {
+ return splitByLogicalOperator('||', node)
+}
+/**
+ * @param {VExpression} node
+ */
+function splitByAnd(node) {
+ return splitByLogicalOperator('&&', node)
+}
+
+/**
+ * @param {VExpression} node
+ * @returns {OrOperands}
+ */
+function buildOrOperands(node) {
+ const orOperands = splitByOr(node)
+ return {
+ node,
+ operands: orOperands.map((orOperand) => {
+ const andOperands = splitByAnd(orOperand)
+ return {
+ node: orOperand,
+ operands: andOperands
+ }
+ })
+ }
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description:
+ 'disallow duplicate conditions in `v-if` / `v-else-if` chains',
+ categories: ['vue3-essential', 'essential'],
+ url: 'https://eslint.vuejs.org/rules/no-dupe-v-else-if.html'
+ },
+ fixable: null,
+ schema: [],
+ messages: {
+ unexpected:
+ 'This branch can never execute. Its condition is a duplicate or covered by previous conditions in the `v-if` / `v-else-if` chain.'
+ }
+ },
+ /** @param {RuleContext} context */
+ create(context) {
+ const tokenStore =
+ context.parserServices.getTemplateBodyTokenStore &&
+ context.parserServices.getTemplateBodyTokenStore()
+ /**
+ * Determines whether the two given nodes are considered to be equal. In particular, given that the nodes
+ * represent expressions in a boolean context, `||` and `&&` can be considered as commutative operators.
+ * @param {VExpression} a First node.
+ * @param {VExpression} b Second node.
+ * @returns {boolean} `true` if the nodes are considered to be equal.
+ */
+ function equal(a, b) {
+ if (a.type !== b.type) {
+ return false
+ }
+
+ if (
+ a.type === 'LogicalExpression' &&
+ b.type === 'LogicalExpression' &&
+ (a.operator === '||' || a.operator === '&&') &&
+ a.operator === b.operator
+ ) {
+ return (
+ (equal(a.left, b.left) && equal(a.right, b.right)) ||
+ (equal(a.left, b.right) && equal(a.right, b.left))
+ )
+ }
+
+ return utils.equalTokens(a, b, tokenStore)
+ }
+
+ /**
+ * Determines whether the first given AndOperands is a subset of the second given AndOperands.
+ *
+ * e.g. A: (a && b), B: (a && b && c): B is a subset of A.
+ *
+ * @param {AndOperands} operandsA The AndOperands to compare from.
+ * @param {AndOperands} operandsB The AndOperands to compare against.
+ * @returns {boolean} `true` if the `andOperandsA` is a subset of the `andOperandsB`.
+ */
+ function isSubset(operandsA, operandsB) {
+ return operandsA.operands.every((operandA) =>
+ operandsB.operands.some((operandB) => equal(operandA, operandB))
+ )
+ }
+
+ return utils.defineTemplateBodyVisitor(context, {
+ "VAttribute[directive=true][key.name.name='else-if']"(node) {
+ if (!node.value || !node.value.expression) {
+ return
+ }
+ const test = node.value.expression
+ const conditionsToCheck =
+ test.type === 'LogicalExpression' && test.operator === '&&'
+ ? [...splitByAnd(test), test]
+ : [test]
+ const listToCheck = conditionsToCheck.map(buildOrOperands)
+
+ /** @type {VElement | null} */
+ let current = node.parent.parent
+ while (current && (current = utils.prevSibling(current))) {
+ const vIf = utils.getDirective(current, 'if')
+ const currentTestDir = vIf || utils.getDirective(current, 'else-if')
+ if (!currentTestDir) {
+ return
+ }
+ if (currentTestDir.value && currentTestDir.value.expression) {
+ const currentOrOperands = buildOrOperands(
+ currentTestDir.value.expression
+ )
+
+ for (const condition of listToCheck) {
+ const operands = (condition.operands = condition.operands.filter(
+ (orOperand) => {
+ return !currentOrOperands.operands.some((currentOrOperand) =>
+ isSubset(currentOrOperand, orOperand)
+ )
+ }
+ ))
+ if (!operands.length) {
+ context.report({
+ node: condition.node,
+ messageId: 'unexpected'
+ })
+ return
+ }
+ }
+ }
+
+ if (vIf) {
+ return
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/lib/utils/index.js b/lib/utils/index.js
index a0710a8d9..7fae65530 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -1530,6 +1530,32 @@ module.exports = {
return null
}
+ },
+
+ /**
+ * Checks whether or not the tokens of two given nodes are same.
+ * @param {ASTNode} left A node 1 to compare.
+ * @param {ASTNode} right A node 2 to compare.
+ * @param {ParserServices.TokenStore | SourceCode} sourceCode The ESLint source code object.
+ * @returns {boolean} the source code for the given node.
+ */
+ equalTokens(left, right, sourceCode) {
+ const tokensL = sourceCode.getTokens(left)
+ const tokensR = sourceCode.getTokens(right)
+
+ if (tokensL.length !== tokensR.length) {
+ return false
+ }
+ for (let i = 0; i < tokensL.length; ++i) {
+ if (
+ tokensL[i].type !== tokensR[i].type ||
+ tokensL[i].value !== tokensR[i].value
+ ) {
+ return false
+ }
+ }
+
+ return true
}
}
diff --git a/tests/lib/rules/no-dupe-v-else-if.js b/tests/lib/rules/no-dupe-v-else-if.js
new file mode 100644
index 000000000..0b5698fe8
--- /dev/null
+++ b/tests/lib/rules/no-dupe-v-else-if.js
@@ -0,0 +1,662 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-dupe-v-else-if')
+
+const tester = new RuleTester({
+ parser: require.resolve('vue-eslint-parser'),
+ parserOptions: {
+ ecmaVersion: 2019,
+ sourceType: 'module'
+ }
+})
+
+tester.run('no-dupe-v-else-if', rule, {
+ valid: [
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `
+ },
+ // parse error
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `
+ },
+
+ // Referred to the ESLint core rule.
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ ''
+ ],
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `,
+ errors: [
+ {
+ message:
+ 'This branch can never execute. Its condition is a duplicate or covered by previous conditions in the `v-if` / `v-else-if` chain.',
+ line: 4,
+ column: 25,
+ endLine: 4,
+ endColumn: 28
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 4
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 6
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 6
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 4
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 5
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 4
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 4
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 4
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 5
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+ `,
+ errors: [
+ {
+ messageId: 'unexpected',
+ line: 4,
+ column: 25,
+ endLine: 4,
+ endColumn: 28
+ },
+ {
+ messageId: 'unexpected',
+ line: 5,
+ column: 32,
+ endLine: 5,
+ endColumn: 35
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+ `,
+ errors: [
+ { messageId: 'unexpected', line: 4 },
+ { messageId: 'unexpected', line: 5 }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+
+
+ `,
+ errors: [
+ { messageId: 'unexpected', line: 4 },
+ { messageId: 'unexpected', line: 5 }
+ ]
+ },
+ {
+ filename: 'foo.vue',
+ code: `
+
+
+
+
+
+ `,
+ errors: [{ messageId: 'unexpected' }, { messageId: 'unexpected' }]
+ },
+
+ // Referred to the ESLint core rule.
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }, { messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [
+ { messageId: 'unexpected' },
+ { messageId: 'unexpected' },
+ { messageId: 'unexpected' }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }, { messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code:
+ '',
+ errors: [{ messageId: 'unexpected' }]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [{ messageId: 'unexpected' }]
+ }
+ ]
+})
diff --git a/typings/eslint-plugin-vue/util-types/ast/ast.ts b/typings/eslint-plugin-vue/util-types/ast/ast.ts
index bcdeb93c6..357b56400 100644
--- a/typings/eslint-plugin-vue/util-types/ast/ast.ts
+++ b/typings/eslint-plugin-vue/util-types/ast/ast.ts
@@ -12,32 +12,104 @@ export type VNodeListenerMap = {
'VAttribute:exit': V.VAttribute | V.VDirective
'VAttribute[directive=false]': V.VAttribute
'VAttribute[directive=false]:exit': V.VAttribute
- "VAttribute[directive=true][key.name.name='bind']": V.VDirective
- "VAttribute[directive=true][key.name.name='bind']:exit": V.VDirective
+ "VAttribute[directive=true][key.name.name='bind']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & {
+ expression: ES.Expression | V.VFilterSequenceExpression | null
+ })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='bind']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & {
+ expression: ES.Expression | V.VFilterSequenceExpression | null
+ })
+ | null
+ }
"VAttribute[directive=true][key.name.name='cloak']": V.VDirective
"VAttribute[directive=true][key.name.name='cloak']:exit": V.VDirective
- "VAttribute[directive=true][key.name.name='else-if']": V.VDirective
- "VAttribute[directive=true][key.name.name='else-if']:exit": V.VDirective
+ "VAttribute[directive=true][key.name.name='else-if']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='else-if']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
"VAttribute[directive=true][key.name.name='else']": V.VDirective
"VAttribute[directive=true][key.name.name='else']:exit": V.VDirective
- "VAttribute[directive=true][key.name.name='for']": V.VDirective
- "VAttribute[directive=true][key.name.name='for']:exit": V.VDirective
+ "VAttribute[directive=true][key.name.name='for']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: V.VForExpression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='for']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: V.VForExpression | null })
+ | null
+ }
"VAttribute[directive=true][key.name.name='html']": V.VDirective
"VAttribute[directive=true][key.name.name='html']:exit": V.VDirective
- "VAttribute[directive=true][key.name.name='if']": V.VDirective
- "VAttribute[directive=true][key.name.name='if']:exit": V.VDirective
- "VAttribute[directive=true][key.name.name='model']": V.VDirective
- "VAttribute[directive=true][key.name.name='model']:exit": V.VDirective
- "VAttribute[directive=true][key.name.name='on']": V.VDirective
- "VAttribute[directive=true][key.name.name='on']:exit": V.VDirective
+ "VAttribute[directive=true][key.name.name='if']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='if']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='model']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='model']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='on']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & {
+ expression: ES.Expression | V.VOnExpression | null
+ })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='on']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & {
+ expression: ES.Expression | V.VOnExpression | null
+ })
+ | null
+ }
"VAttribute[directive=true][key.name.name='once']": V.VDirective
"VAttribute[directive=true][key.name.name='once']:exit": V.VDirective
"VAttribute[directive=true][key.name.name='pre']": V.VDirective
"VAttribute[directive=true][key.name.name='pre']:exit": V.VDirective
- "VAttribute[directive=true][key.name.name='show']": V.VDirective
- "VAttribute[directive=true][key.name.name='show']:exit": V.VDirective
- "VAttribute[directive=true][key.name.name='slot']": V.VDirective
- "VAttribute[directive=true][key.name.name='slot']:exit": V.VDirective
+ "VAttribute[directive=true][key.name.name='show']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='show']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: ES.Expression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='slot']": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: V.VSlotScopeExpression | null })
+ | null
+ }
+ "VAttribute[directive=true][key.name.name='slot']:exit": V.VDirective & {
+ value:
+ | (V.VExpressionContainer & { expression: V.VSlotScopeExpression | null })
+ | null
+ }
"VAttribute[directive=true][key.name.name='text']": V.VDirective
"VAttribute[directive=true][key.name.name='text']:exit": V.VDirective
'VAttribute[value!=null]':