+
+
+
diff --git a/docs/.vuepress/components/eslint-code-block.vue b/docs/.vitepress/theme/components/eslint-code-block.vue
similarity index 50%
rename from docs/.vuepress/components/eslint-code-block.vue
rename to docs/.vitepress/theme/components/eslint-code-block.vue
index 2c6abd2cc..77d4fd7cf 100644
--- a/docs/.vuepress/components/eslint-code-block.vue
+++ b/docs/.vitepress/theme/components/eslint-code-block.vue
@@ -3,7 +3,7 @@
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
new file mode 100644
index 000000000..757e63cab
--- /dev/null
+++ b/docs/.vitepress/theme/index.ts
@@ -0,0 +1,32 @@
+// @ts-expect-error -- Browser
+if (typeof window !== 'undefined') {
+ if (typeof require === 'undefined') {
+ // @ts-expect-error -- Browser
+ ;(window as any).require = () => {
+ const e = new Error('require is not defined')
+ ;(e as any).code = 'MODULE_NOT_FOUND'
+ throw e
+ }
+ }
+}
+// @ts-expect-error -- Cannot change `module` option
+import type { Theme } from 'vitepress'
+// @ts-expect-error -- Cannot change `module` option
+import DefaultTheme from 'vitepress/theme'
+// @ts-expect-error -- ignore
+import Layout from './Layout.vue'
+// @ts-expect-error -- ignore
+import ESLintCodeBlock from './components/eslint-code-block.vue'
+// @ts-expect-error -- ignore
+import RulesTable from './components/rules-table.vue'
+
+const theme: Theme = {
+ ...DefaultTheme,
+ Layout,
+ enhanceApp(ctx) {
+ DefaultTheme.enhanceApp(ctx)
+ ctx.app.component('eslint-code-block', ESLintCodeBlock)
+ ctx.app.component('rules-table', RulesTable)
+ }
+}
+export default theme
diff --git a/docs/.vitepress/vite-plugin.mts b/docs/.vitepress/vite-plugin.mts
new file mode 100644
index 000000000..cd6811cb6
--- /dev/null
+++ b/docs/.vitepress/vite-plugin.mts
@@ -0,0 +1,83 @@
+import type { UserConfig } from 'vitepress'
+import path from 'pathe'
+import { fileURLToPath } from 'url'
+import esbuild from 'esbuild'
+type Plugin = Extract<
+ NonNullable['plugins']>[number],
+ { name: string }
+>
+
+const libRoot = path.join(fileURLToPath(import.meta.url), '../../../lib')
+export function vitePluginRequireResolve(): Plugin {
+ return {
+ name: 'vite-plugin-require.resolve',
+ transform(code, id, _options) {
+ if (id.startsWith(libRoot)) {
+ return code.replace(/require\.resolve/gu, '(function(){return 0})')
+ }
+ return undefined
+ }
+ }
+}
+
+export function viteCommonjs(): Plugin {
+ return {
+ name: 'vite-plugin-cjs-to-esm',
+ apply: () => true,
+ async transform(code, id) {
+ if (!id.startsWith(libRoot)) {
+ return undefined
+ }
+ const base = transformRequire(code)
+ try {
+ const transformed = esbuild.transformSync(base, {
+ format: 'esm'
+ })
+ return transformed.code
+ } catch (e) {
+ console.error('Transform error. base code:\n' + base, e)
+ }
+ return undefined
+ }
+ }
+}
+
+/**
+ * Transform `require()` to `import`
+ */
+function transformRequire(code: string) {
+ if (!code.includes('require')) {
+ return code
+ }
+ const modules = new Map()
+ const replaced = code.replace(
+ /(\/\/[^\n\r]*|\/\*[\s\S]*?\*\/)|\brequire\s*\(\s*(["'].*?["'])\s*\)/gu,
+ (match, comment, moduleString) => {
+ if (comment) {
+ return match
+ }
+
+ let id =
+ '__' +
+ moduleString.replace(/[^a-zA-Z0-9_$]+/gu, '_') +
+ Math.random().toString(32).substring(2)
+ while (code.includes(id) || modules.has(id)) {
+ id += Math.random().toString(32).substring(2)
+ }
+ modules.set(id, moduleString)
+ return id + '()'
+ }
+ )
+
+ return (
+ [...modules]
+ .map(([id, moduleString]) => {
+ return `import * as __temp_${id} from ${moduleString};
+const ${id} = () => __temp_${id}.default || __temp_${id};
+`
+ })
+ .join('') +
+ ';\n' +
+ replaced
+ )
+}
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
deleted file mode 100644
index 68aec1af9..000000000
--- a/docs/.vuepress/config.js
+++ /dev/null
@@ -1,192 +0,0 @@
-/**
- * @author Toru Nagashima
- * See LICENSE file in root directory for full license.
- */
-'use strict'
-
-const rules = require('../../tools/lib/rules')
-const path = require('path')
-
-const uncategorizedRules = rules.filter(
- (rule) =>
- !rule.meta.docs.categories &&
- !rule.meta.docs.extensionRule &&
- !rule.meta.deprecated
-)
-const uncategorizedExtensionRule = rules.filter(
- (rule) =>
- !rule.meta.docs.categories &&
- rule.meta.docs.extensionRule &&
- !rule.meta.deprecated
-)
-const deprecatedRules = rules.filter((rule) => rule.meta.deprecated)
-
-const sidebarCategories = [
- { title: 'Base Rules', categoryIds: ['base'] },
- {
- title: 'Priority A: Essential',
- categoryIds: ['vue3-essential', 'essential']
- },
- {
- title: 'Priority A: Essential for Vue.js 3.x',
- categoryIds: ['vue3-essential']
- },
- { title: 'Priority A: Essential for Vue.js 2.x', categoryIds: ['essential'] },
- {
- title: 'Priority B: Strongly Recommended',
- categoryIds: ['vue3-strongly-recommended', 'strongly-recommended']
- },
- {
- title: 'Priority B: Strongly Recommended for Vue.js 3.x',
- categoryIds: ['vue3-strongly-recommended']
- },
- {
- title: 'Priority B: Strongly Recommended for Vue.js 2.x',
- categoryIds: ['strongly-recommended']
- },
- {
- title: 'Priority C: Recommended',
- categoryIds: ['vue3-recommended', 'recommended']
- },
- {
- title: 'Priority C: Recommended for Vue.js 3.x',
- categoryIds: ['vue3-recommended']
- },
- {
- title: 'Priority C: Recommended for Vue.js 2.x',
- categoryIds: ['recommended']
- }
-]
-
-const categorizedRules = []
-for (const { title, categoryIds } of sidebarCategories) {
- const categoryRules = rules
- .filter((rule) => rule.meta.docs.categories && !rule.meta.deprecated)
- .filter((rule) =>
- categoryIds.every((categoryId) =>
- rule.meta.docs.categories.includes(categoryId)
- )
- )
- const children = categoryRules
- .filter(({ ruleId }) => {
- const exists = categorizedRules.some(({ children }) =>
- children.some(([, alreadyRuleId]) => alreadyRuleId === ruleId)
- )
- return !exists
- })
- .map(({ ruleId, name }) => [`/rules/${name}`, ruleId])
-
- if (children.length === 0) {
- continue
- }
- categorizedRules.push({
- title,
- collapsable: false,
- children
- })
-}
-
-const extraCategories = []
-if (uncategorizedRules.length > 0) {
- extraCategories.push({
- title: 'Uncategorized',
- collapsable: false,
- children: uncategorizedRules.map(({ ruleId, name }) => [
- `/rules/${name}`,
- ruleId
- ])
- })
-}
-if (uncategorizedExtensionRule.length > 0) {
- extraCategories.push({
- title: 'Extension Rules',
- collapsable: false,
- children: uncategorizedExtensionRule.map(({ ruleId, name }) => [
- `/rules/${name}`,
- ruleId
- ])
- })
-}
-if (deprecatedRules.length > 0) {
- extraCategories.push({
- title: 'Deprecated',
- collapsable: false,
- children: deprecatedRules.map(({ ruleId, name }) => [
- `/rules/${name}`,
- ruleId
- ])
- })
-}
-
-module.exports = {
- configureWebpack(_config, _isServer) {
- return {
- resolve: {
- alias: {
- module: require.resolve('./shim/module'),
- eslint$: require.resolve('./shim/eslint'),
- esquery: path.resolve(
- __dirname,
- '../../node_modules/esquery/dist/esquery.min.js'
- ),
- '@eslint/eslintrc/universal': path.resolve(
- __dirname,
- '../../node_modules/@eslint/eslintrc/dist/eslintrc-universal.cjs'
- )
- }
- }
- }
- },
-
- base: '/',
- title: 'eslint-plugin-vue',
- description: 'Official ESLint plugin for Vue.js',
- evergreen: true,
- head: [['link', { rel: 'icon', href: '/favicon.png' }]],
-
- plugins: {
- '@vuepress/pwa': {
- serviceWorker: true,
- updatePopup: true
- }
- },
-
- themeConfig: {
- repo: 'vuejs/eslint-plugin-vue',
- docsRepo: 'vuejs/eslint-plugin-vue',
- docsDir: 'docs',
- docsBranch: 'master',
- editLinks: true,
- lastUpdated: true,
-
- nav: [
- { text: 'User Guide', link: '/user-guide/' },
- { text: 'Developer Guide', link: '/developer-guide/' },
- { text: 'Rules', link: '/rules/' },
- {
- text: 'Demo',
- link: 'https://ota-meshi.github.io/eslint-plugin-vue-demo/'
- }
- ],
-
- sidebar: {
- '/rules/': [
- '/rules/',
-
- // Rules in each category.
- ...categorizedRules,
-
- // Rules in no category.
- ...extraCategories
- ],
-
- '/': ['/', '/user-guide/', '/developer-guide/', '/rules/']
- },
-
- algolia: {
- appId: '2L4MGZSULB',
- apiKey: 'fdf57932b27a6c230d01a890492ab76d',
- indexName: 'eslint-plugin-vue'
- }
- }
-}
diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js
deleted file mode 100644
index dc7e30771..000000000
--- a/docs/.vuepress/enhanceApp.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/* globals window */
-export default (
- // eslint-disable-next-line no-empty-pattern
- {
- // Vue, // the version of Vue being used in the VuePress app
- // options, // the options for the root Vue instance
- // router, // the router instance for the app
- // siteData, // site metadata
- }
-) => {
- if (typeof window !== 'undefined') {
- if (typeof window.process === 'undefined') {
- window.process = new Proxy(
- {
- env: {},
- cwd: () => undefined
- },
- {
- get(target, name) {
- // For debug
- // console.log(name)
- return target[name]
- }
- }
- )
- }
- }
-}
diff --git a/docs/.vuepress/shim/eslint.js b/docs/.vuepress/shim/eslint.js
deleted file mode 100644
index 08af6dfcd..000000000
--- a/docs/.vuepress/shim/eslint.js
+++ /dev/null
@@ -1,4 +0,0 @@
-const { Linter } = require('eslint/lib/linter')
-module.exports = {
- Linter
-}
diff --git a/docs/.vuepress/shim/module.js b/docs/.vuepress/shim/module.js
deleted file mode 100644
index 66ab9785e..000000000
--- a/docs/.vuepress/shim/module.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- createRequire: () => () => null
-}
diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl
deleted file mode 100644
index 48680bbf3..000000000
--- a/docs/.vuepress/styles/index.styl
+++ /dev/null
@@ -1,20 +0,0 @@
-.theme-container.rule-details .theme-default-content > h1 {
- font-size: 1.8rem;
-
- + blockquote {
- margin-top: -15px;
- padding: 0;
- border: 0;
- font-weight: 500;
- font-size: 1.4rem;
- color: currentColor;
-
- ::first-letter {
- text-transform: uppercase;
- }
-
- p {
- line-height: 1.2;
- }
- }
-}
diff --git a/docs/README.md b/docs/README.md
deleted file mode 100644
index 672aec594..000000000
--- a/docs/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-sidebarDepth: 0
----
-
-# Introduction
-
-Official ESLint plugin for Vue.js.
-
-This plugin allows us to check the `` and `
+...
+
+```
+
+
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+
+
+```vue
+
+
+
+...
+```
+
+
+
+### `{ "order": ["template", "script", "style"] }`
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+
+
+```vue
+
+
+...
+
+```
+
+
+
+### `{ "order": ["docs", "template", "script", "style"] }`
+
+
+
+```vue
+
+ documentation
+...
+
+
+```
+
+
+
+
+
+```vue
+
+...
+
+ documentation
+
+```
+
+
+
+### `{ 'order': ['template', 'script:not([setup])', 'script[setup]'] }`
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+### `{ 'order': ['template', 'style:not([scoped])', 'style[scoped]'] }`
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+### `{ 'order': ['template', 'i18n:not([locale=en])', 'i18n[locale=en]'] }`
+
+
+
+```vue
+
+...
+/* ... */
+/* ... */
+```
+
+
+
+
+
+```vue
+
+...
+/* ... */
+/* ... */
+```
+
+
+
+## :books: Further Reading
+
+- [Style guide - Single-file component top-level element order](https://vuejs.org/style-guide/rules-recommended.html#single-file-component-top-level-element-order)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.16.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/block-order.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/block-order.js)
diff --git a/docs/rules/block-spacing.md b/docs/rules/block-spacing.md
index 72a4d495c..e9f16b8d0 100644
--- a/docs/rules/block-spacing.md
+++ b/docs/rules/block-spacing.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/block-spacing
-description: disallow or enforce spaces inside of blocks after opening block and before closing block
+description: Disallow or enforce spaces inside of blocks after opening block and before closing block in ``
since: v5.2.0
---
+
# vue/block-spacing
-> disallow or enforce spaces inside of blocks after opening block and before closing block
+> Disallow or enforce spaces inside of blocks after opening block and before closing block in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/block-spacing] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [block-spacing] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/block-spacing]
- [block-spacing]
+[@stylistic/block-spacing]: https://eslint.style/rules/default/block-spacing
[block-spacing]: https://eslint.org/docs/rules/block-spacing
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v5.2.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/block-spacing.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/block-spacing.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/block-spacing)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/block-spacing)
diff --git a/docs/rules/block-tag-newline.md b/docs/rules/block-tag-newline.md
index 59d43a14b..8e336b284 100644
--- a/docs/rules/block-tag-newline.md
+++ b/docs/rules/block-tag-newline.md
@@ -5,11 +5,12 @@ title: vue/block-tag-newline
description: enforce line breaks after opening and before closing block-level tags
since: v7.1.0
---
+
# vue/block-tag-newline
> enforce line breaks after opening and before closing block-level tags
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
diff --git a/docs/rules/brace-style.md b/docs/rules/brace-style.md
index 99bf8e0ce..1727f42fb 100644
--- a/docs/rules/brace-style.md
+++ b/docs/rules/brace-style.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/brace-style
-description: enforce consistent brace style for blocks
+description: Enforce consistent brace style for blocks in ``
since: v5.2.0
---
+
# vue/brace-style
-> enforce consistent brace style for blocks
+> Enforce consistent brace style for blocks in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/brace-style] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [brace-style] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/brace-style]
- [brace-style]
+[@stylistic/brace-style]: https://eslint.style/rules/default/brace-style
[brace-style]: https://eslint.org/docs/rules/brace-style
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v5.2.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/brace-style.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/brace-style.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/brace-style)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/brace-style)
diff --git a/docs/rules/camelcase.md b/docs/rules/camelcase.md
index 2ee39bdcc..6ed97876a 100644
--- a/docs/rules/camelcase.md
+++ b/docs/rules/camelcase.md
@@ -2,12 +2,13 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/camelcase
-description: enforce camelcase naming convention
+description: Enforce camelcase naming convention in ``
since: v5.2.0
---
+
# vue/camelcase
-> enforce camelcase naming convention
+> Enforce camelcase naming convention in ``
This rule is the same rule as core [camelcase] rule but it applies to the expressions in ``.
@@ -26,4 +27,4 @@ This rule was introduced in eslint-plugin-vue v5.2.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/camelcase.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/camelcase.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/camelcase)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/camelcase)
diff --git a/docs/rules/comma-dangle.md b/docs/rules/comma-dangle.md
index 1ef275f42..aceaa6dc1 100644
--- a/docs/rules/comma-dangle.md
+++ b/docs/rules/comma-dangle.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/comma-dangle
-description: require or disallow trailing commas
+description: Require or disallow trailing commas in ``
since: v5.2.0
---
+
# vue/comma-dangle
-> require or disallow trailing commas
+> Require or disallow trailing commas in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/comma-dangle] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [comma-dangle] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/comma-dangle]
- [comma-dangle]
+[@stylistic/comma-dangle]: https://eslint.style/rules/default/comma-dangle
[comma-dangle]: https://eslint.org/docs/rules/comma-dangle
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v5.2.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/comma-dangle.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/comma-dangle.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/comma-dangle)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/comma-dangle)
diff --git a/docs/rules/comma-spacing.md b/docs/rules/comma-spacing.md
index f3531c8cf..b585fadf7 100644
--- a/docs/rules/comma-spacing.md
+++ b/docs/rules/comma-spacing.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/comma-spacing
-description: enforce consistent spacing before and after commas
+description: Enforce consistent spacing before and after commas in ``
since: v7.0.0
---
+
# vue/comma-spacing
-> enforce consistent spacing before and after commas
+> Enforce consistent spacing before and after commas in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/comma-spacing] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [comma-spacing] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/comma-spacing]
- [comma-spacing]
+[@stylistic/comma-spacing]: https://eslint.style/rules/default/comma-spacing
[comma-spacing]: https://eslint.org/docs/rules/comma-spacing
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/comma-spacing.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/comma-spacing.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/comma-spacing)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/comma-spacing)
diff --git a/docs/rules/comma-style.md b/docs/rules/comma-style.md
index 2dfb24660..9e08fdaaf 100644
--- a/docs/rules/comma-style.md
+++ b/docs/rules/comma-style.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/comma-style
-description: enforce consistent comma style
+description: Enforce consistent comma style in ``
since: v7.0.0
---
+
# vue/comma-style
-> enforce consistent comma style
+> Enforce consistent comma style in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/comma-style] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [comma-style] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/comma-style]
- [comma-style]
+[@stylistic/comma-style]: https://eslint.style/rules/default/comma-style
[comma-style]: https://eslint.org/docs/rules/comma-style
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/comma-style.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/comma-style.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/comma-style)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/js/comma-style)
diff --git a/docs/rules/comment-directive.md b/docs/rules/comment-directive.md
index 77c3986c5..294dcc15c 100644
--- a/docs/rules/comment-directive.md
+++ b/docs/rules/comment-directive.md
@@ -5,11 +5,12 @@ title: vue/comment-directive
description: support comment-directives in ``
since: v4.1.0
---
+
# vue/comment-directive
> support comment-directives in ``
-- :gear: This rule is included in all of `"plugin:vue/base"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-essential"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/base"`, `*.configs["flat/base"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-recommended"`, `*.configs["flat/vue2-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
Sole purpose of this rule is to provide `eslint-disable` functionality in the `` and in the block level.
It supports usage of the following comments:
diff --git a/docs/rules/component-api-style.md b/docs/rules/component-api-style.md
index af29316e3..7a81176c9 100644
--- a/docs/rules/component-api-style.md
+++ b/docs/rules/component-api-style.md
@@ -5,6 +5,7 @@ title: vue/component-api-style
description: enforce component API style
since: v7.18.0
---
+
# vue/component-api-style
> enforce component API style
@@ -55,7 +56,7 @@ export default {
@@ -106,7 +108,7 @@ export default {
-
+
@@ -129,11 +131,11 @@ export default {
```vue
-
+
-
+
@@ -141,9 +143,25 @@ export default {
+### `"PascalCase", { globals: ["RouterView"] }`
+
+
+
+```vue
+
+
+
+
+
+
+
+```
+
+
+
## :books: Further Reading
-- [Style guide - Component name casing in templates](https://v3.vuejs.org/style-guide/#component-name-casing-in-templates-strongly-recommended)
+- [Style guide - Component name casing in templates](https://vuejs.org/style-guide/rules-strongly-recommended.html#component-name-casing-in-templates)
## :rocket: Version
diff --git a/docs/rules/component-options-name-casing.md b/docs/rules/component-options-name-casing.md
new file mode 100644
index 000000000..469865478
--- /dev/null
+++ b/docs/rules/component-options-name-casing.md
@@ -0,0 +1,170 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/component-options-name-casing
+description: enforce the casing of component name in `components` options
+since: v8.2.0
+---
+
+# vue/component-options-name-casing
+
+> enforce the casing of component name in `components` options
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+This rule aims to enforce casing of the component names in `components` options.
+
+## :wrench: Options
+
+```json
+{
+ "vue/component-options-name-casing": ["error", "PascalCase" | "kebab-case" | "camelCase"]
+}
+```
+
+This rule has an option which can be one of these values:
+
+- `"PascalCase"` (default) ... enforce component names to pascal case.
+- `"kebab-case"` ... enforce component names to kebab case.
+- `"camelCase"` ... enforce component names to camel case.
+
+Please note that if you use kebab case in `components` options,
+you can **only** use kebab case in template;
+and if you use camel case in `components` options,
+you **can't** use pascal case in template.
+
+For demonstration, the code example is invalid:
+
+```vue
+
+
+
+
+
+
+
+
+
+
+```
+
+### `"PascalCase"` (default)
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+### `"kebab-case"`
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+### `"camelCase"`
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.2.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/component-options-name-casing.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/component-options-name-casing.js)
diff --git a/docs/rules/component-tags-order.md b/docs/rules/component-tags-order.md
index e0c0c99f3..a5037cdee 100644
--- a/docs/rules/component-tags-order.md
+++ b/docs/rules/component-tags-order.md
@@ -5,15 +5,16 @@ title: vue/component-tags-order
description: enforce order of component top-level elements
since: v6.1.0
---
+
# vue/component-tags-order
> enforce order of component top-level elements
-- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :no_entry: This rule was **removed** in eslint-plugin-vue v10.0.0 and replaced by [vue/block-order](block-order.md) rule.
## :book: Rule Details
-This rule warns about the order of the `
+
+```
+
+
+
+
+
+```vue
+
...
+ documentation
```
-
+### `{ 'order': ['template', 'script:not([setup])', 'script[setup]'] }`
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+
```vue
...
+
- documents
+```
+
+
+
+### `{ 'order': ['template', 'style:not([scoped])', 'style[scoped]'] }`
+
+
+
+```vue
+
+...
+
+
+```
+
+
+
+
+
+```vue
+
+...
+
```
+### `{ 'order': ['template', 'i18n:not([locale=en])', 'i18n[locale=en]'] }`
+
+
+
+```vue
+
+...
+/* ... */
+/* ... */
+```
+
+
+
+
+
+```vue
+
+...
+/* ... */
+/* ... */
+```
+
+
+
## :books: Further Reading
-- [Style guide - Single-file component top-level element order](https://v3.vuejs.org/style-guide/#single-file-component-top-level-element-order-recommended)
+- [Style guide - Single-file component top-level element order](https://vuejs.org/style-guide/rules-recommended.html#single-file-component-top-level-element-order)
## :rocket: Version
diff --git a/docs/rules/custom-event-name-casing.md b/docs/rules/custom-event-name-casing.md
index ae08fc25d..a57e0eb80 100644
--- a/docs/rules/custom-event-name-casing.md
+++ b/docs/rules/custom-event-name-casing.md
@@ -5,6 +5,7 @@ title: vue/custom-event-name-casing
description: enforce specific casing for custom event name
since: v7.0.0
---
+
# vue/custom-event-name-casing
> enforce specific casing for custom event name
@@ -13,7 +14,7 @@ Define a style for custom event name casing for consistency purposes.
## :book: Rule Details
-This rule aims to warn the custom event names other than the configured casing.
+This rule aims to warn the custom event names other than the configured casing. (Default is **camelCase**.)
Vue 2 recommends using kebab-case for custom event names.
@@ -27,28 +28,28 @@ In Vue 3, using either camelCase or kebab-case for your custom event name does n
See [Guide - Custom Events] for more details.
-This rule enforces kebab-case by default.
+This rule enforces camelCase by default.
```vue
-
+
-
+
+```
+
+
+
+## :wrench: Options
+
+```json
+ "vue/define-emits-declaration": ["error", "type-based" | "type-literal" | "runtime"]
+```
+
+- `type-based` (default) enforces type based declaration
+- `type-literal` enforces strict "type literal" type based declaration
+- `runtime` enforces runtime declaration
+
+### `runtime`
+
+
+
+```vue
+
+```
+
+
+
+### `type-literal`
+
+
+
+```vue
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/define-props-declaration](./define-props-declaration.md)
+- [vue/valid-define-emits](./valid-define-emits.md)
+
+## :books: Further Reading
+
+- [`defineEmits`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
+- [Typescript-only-features of `defineEmits`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
+- [Guide - Typing-component-emits](https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-emits-declaration.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-emits-declaration.js)
diff --git a/docs/rules/define-macros-order.md b/docs/rules/define-macros-order.md
new file mode 100644
index 000000000..14729f991
--- /dev/null
+++ b/docs/rules/define-macros-order.md
@@ -0,0 +1,191 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/define-macros-order
+description: enforce order of compiler macros (`defineProps`, `defineEmits`, etc.)
+since: v8.7.0
+---
+
+# vue/define-macros-order
+
+> enforce order of compiler macros (`defineProps`, `defineEmits`, etc.)
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+This rule reports compiler macros (like `defineProps` or `defineEmits` but also custom ones) when they are not the first statements in `
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+### `{ "order": ["defineOptions", "defineModel", "defineProps", "defineEmits", "defineSlots"] }`
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+### `{ "order": ["definePage", "defineModel", "defineCustom", "defineEmits", "defineSlots"] }`
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+### `{ "defineExposeLast": true }`
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.7.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-macros-order.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-macros-order.js)
diff --git a/docs/rules/define-props-declaration.md b/docs/rules/define-props-declaration.md
new file mode 100644
index 000000000..40c5ea0b0
--- /dev/null
+++ b/docs/rules/define-props-declaration.md
@@ -0,0 +1,88 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/define-props-declaration
+description: enforce declaration style of `defineProps`
+since: v9.5.0
+---
+
+# vue/define-props-declaration
+
+> enforce declaration style of `defineProps`
+
+## :book: Rule Details
+
+This rule enforces `defineProps` typing style which you should use `type-based` or `runtime` declaration.
+
+This rule only works in setup script and `lang="ts"`.
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+```json
+ "vue/define-props-declaration": ["error", "type-based" | "runtime"]
+```
+
+- `type-based` (default) enforces type-based declaration
+- `runtime` enforces runtime declaration
+
+### `"runtime"`
+
+
+
+```vue
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/define-emits-declaration](./define-emits-declaration.md)
+- [vue/valid-define-props](./valid-define-props.md)
+
+## :books: Further Reading
+
+- [`defineProps`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
+- [Typescript-only-features of `defineProps`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
+- [Guide - Typing-component-props](https://vuejs.org/guide/typescript/composition-api.html#typing-component-props)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-props-declaration.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-props-declaration.js)
diff --git a/docs/rules/define-props-destructuring.md b/docs/rules/define-props-destructuring.md
new file mode 100644
index 000000000..e3c2b2745
--- /dev/null
+++ b/docs/rules/define-props-destructuring.md
@@ -0,0 +1,98 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/define-props-destructuring
+description: enforce consistent style for props destructuring
+since: v10.1.0
+---
+
+# vue/define-props-destructuring
+
+> enforce consistent style for props destructuring
+
+## :book: Rule Details
+
+This rule enforces a consistent style for handling Vue 3 Composition API props, allowing you to choose between requiring destructuring or prohibiting it.
+
+By default, the rule requires you to use destructuring syntax when using `defineProps` instead of storing props in a variable and warns against combining `withDefaults` with destructuring.
+
+
+
+```vue
+
+```
+
+
+
+The rule applies to both JavaScript and TypeScript props:
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+```js
+{
+ "vue/define-props-destructuring": ["error", {
+ "destructure": "always" | "never"
+ }]
+}
+```
+
+- `destructure` - Sets the destructuring preference for props
+ - `"always"` (default) - Requires destructuring when using `defineProps` and warns against using `withDefaults` with destructuring
+ - `"never"` - Requires using a variable to store props and prohibits destructuring
+
+### `"destructure": "never"`
+
+
+
+```vue
+
+```
+
+
+
+## :books: Further Reading
+
+- [Reactive Props Destructure](https://vuejs.org/guide/components/props.html#reactive-props-destructure)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v10.1.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-props-destructuring.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-props-destructuring.js)
diff --git a/docs/rules/dot-location.md b/docs/rules/dot-location.md
index aafef433a..edb785080 100644
--- a/docs/rules/dot-location.md
+++ b/docs/rules/dot-location.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/dot-location
-description: enforce consistent newlines before and after dots
+description: Enforce consistent newlines before and after dots in ``
since: v6.0.0
---
+
# vue/dot-location
-> enforce consistent newlines before and after dots
+> Enforce consistent newlines before and after dots in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/dot-location] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [dot-location] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/dot-location]
- [dot-location]
+[@stylistic/dot-location]: https://eslint.style/rules/default/dot-location
[dot-location]: https://eslint.org/docs/rules/dot-location
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v6.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/dot-location.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/dot-location.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/dot-location)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/js/dot-location)
diff --git a/docs/rules/dot-notation.md b/docs/rules/dot-notation.md
index d94fac957..101caf5cf 100644
--- a/docs/rules/dot-notation.md
+++ b/docs/rules/dot-notation.md
@@ -2,14 +2,15 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/dot-notation
-description: enforce dot notation whenever possible
+description: Enforce dot notation whenever possible in ``
since: v7.0.0
---
+
# vue/dot-notation
-> enforce dot notation whenever possible
+> Enforce dot notation whenever possible in ``
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
This rule is the same rule as core [dot-notation] rule but it applies to the expressions in ``.
@@ -28,4 +29,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/dot-notation.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/dot-notation.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/dot-notation)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/dot-notation)
diff --git a/docs/rules/enforce-style-attribute.md b/docs/rules/enforce-style-attribute.md
new file mode 100644
index 000000000..fcffefbae
--- /dev/null
+++ b/docs/rules/enforce-style-attribute.md
@@ -0,0 +1,89 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/enforce-style-attribute
+description: enforce or forbid the use of the `scoped` and `module` attributes in SFC top level style tags
+since: v9.20.0
+---
+
+# vue/enforce-style-attribute
+
+> enforce or forbid the use of the `scoped` and `module` attributes in SFC top level style tags
+
+## :book: Rule Details
+
+This rule allows you to explicitly allow the use of the `scoped` and `module` attributes on your top level style tags.
+
+### `"scoped"`
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+```
+
+
+
+### `"module"`
+
+
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+
+
+### `"plain"`
+
+
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/enforce-style-attribute": [
+ "error",
+ { "allow": ["scoped", "module", "plain"] }
+ ]
+}
+```
+
+- `"allow"` (`["scoped" | "module" | "plain"]`) Array of attributes to allow on a top level style tag. The option `plain` is used to allow style tags that have neither the `scoped` nor `module` attributes. Default: `["scoped"]`
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.20.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/enforce-style-attribute.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/enforce-style-attribute.js)
diff --git a/docs/rules/eqeqeq.md b/docs/rules/eqeqeq.md
index 08fdaacea..392deff7f 100644
--- a/docs/rules/eqeqeq.md
+++ b/docs/rules/eqeqeq.md
@@ -2,14 +2,15 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/eqeqeq
-description: require the use of `===` and `!==`
+description: Require the use of `===` and `!==` in ``
since: v5.2.0
---
+
# vue/eqeqeq
-> require the use of `===` and `!==`
+> Require the use of `===` and `!==` in ``
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
This rule is the same rule as core [eqeqeq] rule but it applies to the expressions in ``.
@@ -28,4 +29,4 @@ This rule was introduced in eslint-plugin-vue v5.2.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/eqeqeq.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/eqeqeq.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/eqeqeq)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/eqeqeq)
diff --git a/docs/rules/experimental-script-setup-vars.md b/docs/rules/experimental-script-setup-vars.md
index d13710246..82e7de884 100644
--- a/docs/rules/experimental-script-setup-vars.md
+++ b/docs/rules/experimental-script-setup-vars.md
@@ -5,15 +5,12 @@ title: vue/experimental-script-setup-vars
description: prevent variables defined in `
```
@@ -128,11 +131,11 @@ export default {
```vue
```
@@ -143,11 +146,11 @@ export default {
```vue
```
@@ -158,10 +161,10 @@ export default {
```vue
```
@@ -308,7 +311,7 @@ export default {
## :books: Further Reading
-- [Style guide - Single-file component filename casing](https://v3.vuejs.org/style-guide/#single-file-component-filename-casing-strongly-recommended)
+- [Style guide - Single-file component filename casing](https://vuejs.org/style-guide/rules-strongly-recommended.html#single-file-component-filename-casing)
## :rocket: Version
diff --git a/docs/rules/match-component-import-name.md b/docs/rules/match-component-import-name.md
new file mode 100644
index 000000000..522b90a02
--- /dev/null
+++ b/docs/rules/match-component-import-name.md
@@ -0,0 +1,49 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/match-component-import-name
+description: require the registered component name to match the imported component name
+since: v8.7.0
+---
+
+# vue/match-component-import-name
+
+> require the registered component name to match the imported component name
+
+## :book: Rule Details
+
+By default, this rule will validate that the imported name matches the name of the components object property identifer. Note that "matches" means that the imported name matches either the PascalCase or kebab-case version of the components object property identifer. If you would like to enforce that it must match only one of PascalCase or kebab-case, use this rule in conjunction with the rule [vue/component-definition-name-casing](./component-definition-name-casing.md).
+
+
+
+```vue
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/component-definition-name-casing](./component-definition-name-casing.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.7.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/match-component-import-name.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/match-component-import-name.js)
diff --git a/docs/rules/max-attributes-per-line.md b/docs/rules/max-attributes-per-line.md
index cecbfd563..141b136e0 100644
--- a/docs/rules/max-attributes-per-line.md
+++ b/docs/rules/max-attributes-per-line.md
@@ -5,12 +5,13 @@ title: vue/max-attributes-per-line
description: enforce the maximum number of attributes per line
since: v3.12.0
---
+
# vue/max-attributes-per-line
> enforce the maximum number of attributes per line
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
Limits the maximum number of attributes/properties per line to improve readability.
@@ -114,7 +115,7 @@ There is a configurable number of attributes that are acceptable in one-line cas
## :books: Further Reading
-- [Style guide - Multi attribute elements](https://v3.vuejs.org/style-guide/#multi-attribute-elements-strongly-recommended)
+- [Style guide - Multi attribute elements](https://vuejs.org/style-guide/rules-strongly-recommended.html#multi-attribute-elements)
## :rocket: Version
diff --git a/docs/rules/max-len.md b/docs/rules/max-len.md
index 8d7b32eb7..0816000ee 100644
--- a/docs/rules/max-len.md
+++ b/docs/rules/max-len.md
@@ -2,12 +2,13 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/max-len
-description: enforce a maximum line length
+description: enforce a maximum line length in `.vue` files
since: v6.1.0
---
+
# vue/max-len
-> enforce a maximum line length
+> enforce a maximum line length in `.vue` files
## :book: Rule Details
@@ -112,7 +113,6 @@ var foo = ['line', 'length', 'is', '50', '......']
-
### `"template": 120`
diff --git a/docs/rules/max-lines-per-block.md b/docs/rules/max-lines-per-block.md
new file mode 100644
index 000000000..a65be3bb9
--- /dev/null
+++ b/docs/rules/max-lines-per-block.md
@@ -0,0 +1,63 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/max-lines-per-block
+description: enforce maximum number of lines in Vue SFC blocks
+since: v9.15.0
+---
+
+# vue/max-lines-per-block
+
+> enforce maximum number of lines in Vue SFC blocks
+
+## :book: Rule Details
+
+This rule enforces a maximum number of lines per block, in order to aid in maintainability and reduce complexity.
+
+## :wrench: Options
+
+This rule takes an object, where you can specify the maximum number of lines in each type of SFC block and customize the line counting behavior.
+The following properties can be specified for the object.
+
+- `script` ... Specify the maximum number of lines in `
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.15.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/max-lines-per-block.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/max-lines-per-block.js)
diff --git a/docs/rules/max-props.md b/docs/rules/max-props.md
new file mode 100644
index 000000000..918c67294
--- /dev/null
+++ b/docs/rules/max-props.md
@@ -0,0 +1,65 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/max-props
+description: enforce maximum number of props in Vue component
+since: v9.28.0
+---
+
+# vue/max-props
+
+> enforce maximum number of props in Vue component
+
+## :book: Rule Details
+
+This rule enforces a maximum number of props in a Vue SFC, in order to aid in maintainability and reduce complexity.
+
+## :wrench: Options
+
+This rule takes an object, where you can specify the maximum number of props allowed in a Vue SFC.
+There is one property that can be specified for the object.
+
+- `maxProps` ... Specify the maximum number of props in the `script` block.
+
+### `{ maxProps: 1 }`
+
+
+
+```vue
+
+
+
+
+
+```
+
+
+
+### `{ maxProps: 5 }`
+
+
+
+```vue
+
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.28.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/max-props.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/max-props.js)
diff --git a/docs/rules/max-template-depth.md b/docs/rules/max-template-depth.md
new file mode 100644
index 000000000..42ee4da88
--- /dev/null
+++ b/docs/rules/max-template-depth.md
@@ -0,0 +1,70 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/max-template-depth
+description: enforce maximum depth of template
+since: v9.28.0
+---
+
+# vue/max-template-depth
+
+> enforce maximum depth of template
+
+## :book: Rule Details
+
+This rule enforces a maximum depth of the template in a Vue SFC, in order to aid in maintainability and reduce complexity.
+
+## :wrench: Options
+
+This rule takes an object, where you can specify the maximum depth allowed in a Vue SFC template block.
+There is one property that can be specified for the object.
+
+- `maxDepth` ... Specify the maximum template depth `template` block.
+
+### `{ maxDepth: 3 }`
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.28.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/max-template-depth.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/max-template-depth.js)
diff --git a/docs/rules/multi-word-component-names.md b/docs/rules/multi-word-component-names.md
index e45bd7b61..08539dddb 100644
--- a/docs/rules/multi-word-component-names.md
+++ b/docs/rules/multi-word-component-names.md
@@ -5,18 +5,19 @@ title: vue/multi-word-component-names
description: require component names to be always multi-word
since: v7.20.0
---
+
# vue/multi-word-component-names
> require component names to be always multi-word
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
This rule require component names to be always multi-word, except for root `App`
components, and built-in components provided by Vue, such as `` or
``. This prevents conflicts with existing and future HTML elements,
-since all HTML elements are a single word.
+since all HTML elements are single words.
@@ -76,6 +77,47 @@ export default {
+
+
+```vue
+
+
+
+```
+
+
+
+
+
+```vue
+
+
+
+```
+
+
+
+
+
+```vue
+
+
+
+
+```
+
+
+
## :wrench: Options
```json
@@ -116,9 +158,25 @@ export default {
+
+
+```vue
+
+
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/no-reserved-component-names](./no-reserved-component-names.md)
+
## :books: Further Reading
-- [Style guide - Multi-word component names](https://v3.vuejs.org/style-guide/#multi-word-component-names-essential)
+- [Style guide - Multi-word component names](https://vuejs.org/style-guide/rules-essential.html#use-multi-word-component-names)
## :rocket: Version
diff --git a/docs/rules/multiline-html-element-content-newline.md b/docs/rules/multiline-html-element-content-newline.md
index 9813113ee..bf654aafe 100644
--- a/docs/rules/multiline-html-element-content-newline.md
+++ b/docs/rules/multiline-html-element-content-newline.md
@@ -5,12 +5,13 @@ title: vue/multiline-html-element-content-newline
description: require a line break before and after the contents of a multiline element
since: v5.0.0
---
+
# vue/multiline-html-element-content-newline
> require a line break before and after the contents of a multiline element
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
diff --git a/docs/rules/multiline-ternary.md b/docs/rules/multiline-ternary.md
new file mode 100644
index 000000000..1fe4d84fb
--- /dev/null
+++ b/docs/rules/multiline-ternary.md
@@ -0,0 +1,71 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/multiline-ternary
+description: Enforce newlines between operands of ternary expressions in ``
+since: v9.7.0
+---
+
+# vue/multiline-ternary
+
+> Enforce newlines between operands of ternary expressions in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/multiline-ternary] rule but it applies to the expressions in `` and `
+```
+
+
+
+## :books: Further Reading
+
+- [@stylistic/multiline-ternary]
+- [multiline-ternary]
+
+[@stylistic/multiline-ternary]: https://eslint.style/rules/default/multiline-ternary
+[multiline-ternary]: https://eslint.org/docs/rules/multiline-ternary
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.7.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/multiline-ternary.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/multiline-ternary.js)
+
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/js/multiline-ternary)
diff --git a/docs/rules/mustache-interpolation-spacing.md b/docs/rules/mustache-interpolation-spacing.md
index f331d968b..1e66fc942 100644
--- a/docs/rules/mustache-interpolation-spacing.md
+++ b/docs/rules/mustache-interpolation-spacing.md
@@ -5,12 +5,13 @@ title: vue/mustache-interpolation-spacing
description: enforce unified spacing in mustache interpolations
since: v3.13.0
---
+
# vue/mustache-interpolation-spacing
> enforce unified spacing in mustache interpolations
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
diff --git a/docs/rules/name-property-casing.md b/docs/rules/name-property-casing.md
index 7a912d56b..e1840987b 100644
--- a/docs/rules/name-property-casing.md
+++ b/docs/rules/name-property-casing.md
@@ -5,12 +5,12 @@ title: vue/name-property-casing
description: enforce specific casing for the name property in Vue components
since: v3.8.0
---
+
# vue/name-property-casing
> enforce specific casing for the name property in Vue components
-- :warning: This rule was **deprecated** and replaced by [vue/component-definition-name-casing](component-definition-name-casing.md) rule.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :no_entry: This rule was **removed** in eslint-plugin-vue v9.0.0 and replaced by [vue/component-definition-name-casing](component-definition-name-casing.md) rule.
## :book: Rule Details
@@ -20,10 +20,10 @@ This rule aims at enforcing the style for the `name` property casing for consist
```vue
```
@@ -33,10 +33,10 @@ This rule aims at enforcing the style for the `name` property casing for consist
```vue
```
@@ -59,10 +59,10 @@ This rule aims at enforcing the style for the `name` property casing for consist
```vue
```
@@ -72,10 +72,10 @@ This rule aims at enforcing the style for the `name` property casing for consist
```vue
```
@@ -83,7 +83,7 @@ This rule aims at enforcing the style for the `name` property casing for consist
## :books: Further Reading
-- [Style guide - Component name casing in JS/JSX](https://v3.vuejs.org/style-guide/#component-name-casing-in-js-jsx-strongly-recommended)
+- [Style guide - Component name casing in JS/JSX](https://vuejs.org/style-guide/rules-strongly-recommended.html#component-name-casing-in-js-jsx)
## :rocket: Version
diff --git a/docs/rules/new-line-between-multi-line-property.md b/docs/rules/new-line-between-multi-line-property.md
index 7af127bea..1191a1567 100644
--- a/docs/rules/new-line-between-multi-line-property.md
+++ b/docs/rules/new-line-between-multi-line-property.md
@@ -5,11 +5,12 @@ title: vue/new-line-between-multi-line-property
description: enforce new lines between multi-line properties in Vue components
since: v7.3.0
---
+
# vue/new-line-between-multi-line-property
> enforce new lines between multi-line properties in Vue components
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -75,7 +76,7 @@ export default {
-## :wrench: Option
+## :wrench: Options
```json
{
@@ -89,7 +90,7 @@ export default {
## :books: Further Reading
-- [Style guide - Empty lines in component/instance options](https://v3.vuejs.org/style-guide/#empty-lines-in-component-instance-options-recommended)
+- [Style guide - Empty lines in component/instance options](https://vuejs.org/style-guide/rules-recommended.html#empty-lines-in-component-instance-options)
## :rocket: Version
diff --git a/docs/rules/next-tick-style.md b/docs/rules/next-tick-style.md
index 2f17f2dd4..0e9d7812f 100644
--- a/docs/rules/next-tick-style.md
+++ b/docs/rules/next-tick-style.md
@@ -5,11 +5,12 @@ title: vue/next-tick-style
description: enforce Promise or callback style in `nextTick`
since: v7.5.0
---
+
# vue/next-tick-style
> enforce Promise or callback style in `nextTick`
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -46,6 +47,7 @@ export default {
## :wrench: Options
+
Default is set to `promise`.
```json
@@ -91,11 +93,11 @@ export default {
## :books: Further Reading
-- [`Vue.nextTick` API in Vue 2](https://vuejs.org/v2/api/#Vue-nextTick)
-- [`vm.$nextTick` API in Vue 2](https://vuejs.org/v2/api/#vm-nextTick)
-- [Global API Treeshaking](https://v3.vuejs.org/guide/migration/global-api-treeshaking.html)
-- [Global `nextTick` API in Vue 3](https://v3.vuejs.org/api/global-api.html#nexttick)
-- [Instance `$nextTick` API in Vue 3](https://v3.vuejs.org/api/instance-methods.html#nexttick)
+- [`Vue.nextTick` API in Vue 2](https://v2.vuejs.org/v2/api/#Vue-nextTick)
+- [`vm.$nextTick` API in Vue 2](https://v2.vuejs.org/v2/api/#vm-nextTick)
+- [Global API Treeshaking](https://v3-migration.vuejs.org/breaking-changes/global-api-treeshaking.html)
+- [Global `nextTick` API in Vue 3](https://vuejs.org/api/general.html#nexttick)
+- [Instance `$nextTick` API in Vue 3](https://vuejs.org/api/component-instance.html#nexttick)
## :rocket: Version
diff --git a/docs/rules/no-arrow-functions-in-watch.md b/docs/rules/no-arrow-functions-in-watch.md
index 2ad7bdf70..fb1f55215 100644
--- a/docs/rules/no-arrow-functions-in-watch.md
+++ b/docs/rules/no-arrow-functions-in-watch.md
@@ -5,15 +5,16 @@ title: vue/no-arrow-functions-in-watch
description: disallow using arrow functions to define watcher
since: v7.0.0
---
+
# vue/no-arrow-functions-in-watch
> disallow using arrow functions to define watcher
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
-This rules disallows using arrow functions to defined watcher.The reason is arrow functions bind the parent context, so `this` will not be the Vue instance as you expect.([see here for more details](https://v3.vuejs.org/api/options-data.html#watch))
+This rule disallows using arrow functions when defining a watcher. Arrow functions bind to their parent context, which means they will not have access to the Vue component instance via `this`. [See here for more details](https://vuejs.org/api/options-state.html#watch).
diff --git a/docs/rules/no-async-in-computed-properties.md b/docs/rules/no-async-in-computed-properties.md
index ac7ac50f2..814a53238 100644
--- a/docs/rules/no-async-in-computed-properties.md
+++ b/docs/rules/no-async-in-computed-properties.md
@@ -5,11 +5,12 @@ title: vue/no-async-in-computed-properties
description: disallow asynchronous actions in computed properties
since: v3.8.0
---
+
# vue/no-async-in-computed-properties
> disallow asynchronous actions in computed properties
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
Computed properties and functions should be synchronous. Asynchronous actions inside them may not work as expected and can lead to an unexpected behaviour, that's why you should avoid them.
If you need async computed properties you might want to consider using additional plugin [vue-async-computed]
@@ -25,7 +26,7 @@ This rule is aimed at preventing asynchronous methods from being called in compu
export default {
computed: {
/* ✓ GOOD */
- foo () {
+ foo() {
var bar = 0
try {
bar = bar / this.a
@@ -37,22 +38,22 @@ export default {
},
/* ✗ BAD */
- pro () {
+ pro() {
return Promise.all([new Promise((resolve, reject) => {})])
},
foo1: async function () {
return await someFunc()
},
- bar () {
- return fetch(url).then(response => {})
+ bar() {
+ return fetch(url).then((response) => {})
},
- tim () {
- setTimeout(() => { }, 0)
+ tim() {
+ setTimeout(() => {}, 0)
},
- inter () {
- setInterval(() => { }, 0)
+ inter() {
+ setInterval(() => {}, 0)
},
- anim () {
+ anim() {
requestAnimationFrame(() => {})
}
}
@@ -66,7 +67,7 @@ export default {
```vue
diff --git a/docs/rules/no-confusing-v-for-v-if.md b/docs/rules/no-confusing-v-for-v-if.md
index 8c5d799da..f463c883e 100644
--- a/docs/rules/no-confusing-v-for-v-if.md
+++ b/docs/rules/no-confusing-v-for-v-if.md
@@ -5,11 +5,12 @@ title: vue/no-confusing-v-for-v-if
description: disallow confusing `v-for` and `v-if` on the same element
since: v3.0.0
---
+
# vue/no-confusing-v-for-v-if
> disallow confusing `v-for` and `v-if` on the same element
-- :warning: This rule was **deprecated** and replaced by [vue/no-use-v-if-with-v-for](no-use-v-if-with-v-for.md) rule.
+- :no_entry: This rule was **removed** in eslint-plugin-vue v9.0.0 and replaced by [vue/no-use-v-if-with-v-for](no-use-v-if-with-v-for.md) rule.
## :book: Rule Details
@@ -50,7 +51,7 @@ In that case, the `v-if` should be written on the wrapper element.
::: warning Note
When they exist on the same node, `v-for` has a higher priority than `v-if`. That means the `v-if` will be run on each iteration of the loop separately.
-[https://v3.vuejs.org/guide/list.html#v-for-with-v-if](https://v3.vuejs.org/guide/list.html#v-for-with-v-if)
+[https://vuejs.org/guide/essentials/list.html#v-for-with-v-if](https://vuejs.org/guide/essentials/list.html#v-for-with-v-if)
:::
## :wrench: Options
@@ -59,9 +60,9 @@ Nothing.
## :books: Further Reading
-- [Style guide - Avoid v-if with v-for](https://v3.vuejs.org/style-guide/#avoid-v-if-with-v-for-essential)
-- [Guide - Conditional Rendering / v-if with v-for](https://v3.vuejs.org/guide/conditional.html#v-if-with-v-for)
-- [Guide - List Rendering / v-for with v-if](https://v3.vuejs.org/guide/list.html#v-for-with-v-if)
+- [Style guide - Avoid v-if with v-for](https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for)
+- [Guide - Conditional Rendering / v-if with v-for](https://vuejs.org/guide/essentials/conditional.html#v-if-with-v-for)
+- [Guide - List Rendering / v-for with v-if](https://vuejs.org/guide/essentials/list.html#v-for-with-v-if)
## :rocket: Version
diff --git a/docs/rules/no-console.md b/docs/rules/no-console.md
new file mode 100644
index 000000000..64ef0278e
--- /dev/null
+++ b/docs/rules/no-console.md
@@ -0,0 +1,34 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-console
+description: Disallow the use of `console` in ``
+since: v9.15.0
+---
+
+# vue/no-console
+
+> Disallow the use of `console` in ``
+
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+This rule is the same rule as core [no-console] rule but it applies to the expressions in ``.
+
+## :books: Further Reading
+
+- [no-console]
+
+[no-console]: https://eslint.org/docs/latest/rules/no-console
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.15.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-console.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-console.js)
+
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/no-console)
diff --git a/docs/rules/no-constant-condition.md b/docs/rules/no-constant-condition.md
index 54f6df9d0..36520e25b 100644
--- a/docs/rules/no-constant-condition.md
+++ b/docs/rules/no-constant-condition.md
@@ -2,12 +2,13 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-constant-condition
-description: disallow constant expressions in conditions
+description: Disallow constant expressions in conditions in ``
since: v7.5.0
---
+
# vue/no-constant-condition
-> disallow constant expressions in conditions
+> Disallow constant expressions in conditions in ``
This rule is the same rule as core [no-constant-condition] rule but it applies to the expressions in ``.
@@ -26,4 +27,4 @@ This rule was introduced in eslint-plugin-vue v7.5.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-constant-condition.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-constant-condition.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-constant-condition)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/no-constant-condition)
diff --git a/docs/rules/no-custom-modifiers-on-v-model.md b/docs/rules/no-custom-modifiers-on-v-model.md
index 092ee325c..f657f9cf7 100644
--- a/docs/rules/no-custom-modifiers-on-v-model.md
+++ b/docs/rules/no-custom-modifiers-on-v-model.md
@@ -5,13 +5,14 @@ title: vue/no-custom-modifiers-on-v-model
description: disallow custom modifiers on v-model used on the component
since: v7.0.0
---
+
# vue/no-custom-modifiers-on-v-model
> disallow custom modifiers on v-model used on the component
-- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
-This rule checks whether `v-model `used on the component do not have custom modifiers.
+This rule checks whether `v-model` used on the component do not have custom modifiers.
## :book: Rule Details
@@ -29,11 +30,9 @@ This rule reports `v-model` directives in the following cases:
-
-
```
diff --git a/docs/rules/no-deprecated-data-object-declaration.md b/docs/rules/no-deprecated-data-object-declaration.md
index faedcfab7..214aacc30 100644
--- a/docs/rules/no-deprecated-data-object-declaration.md
+++ b/docs/rules/no-deprecated-data-object-declaration.md
@@ -5,19 +5,20 @@ title: vue/no-deprecated-data-object-declaration
description: disallow using deprecated object declaration on data (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-data-object-declaration
> disallow using deprecated object declaration on data (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
This rule reports use of deprecated object declaration on `data` property (in Vue.js 3.0.0+).
The different from `vue/no-shared-component-data` is the root instance being also disallowed.
-See [Migration Guide - Data Option](https://v3.vuejs.org/guide/migration/data-option.html) for more details.
+See [Migration Guide - Data Option](https://v3-migration.vuejs.org/breaking-changes/data-option.html) for more details.
@@ -31,7 +32,7 @@ createApp({
createApp({
/* ✓ GOOD */
- data () {
+ data() {
return {
foo: null
}
@@ -62,7 +63,7 @@ export default {
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Migration Guide - Removed APIs](https://v3-migration.vuejs.org/breaking-changes/#removed-apis)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.29.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-delete-set.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-delete-set.js)
diff --git a/docs/rules/no-deprecated-destroyed-lifecycle.md b/docs/rules/no-deprecated-destroyed-lifecycle.md
index c99a7670a..9c681e0ea 100644
--- a/docs/rules/no-deprecated-destroyed-lifecycle.md
+++ b/docs/rules/no-deprecated-destroyed-lifecycle.md
@@ -5,12 +5,13 @@ title: vue/no-deprecated-destroyed-lifecycle
description: disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-destroyed-lifecycle
> disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -22,14 +23,14 @@ This rule reports use of deprecated `destroyed` and `beforeDestroy` lifecycle ho
```
@@ -40,6 +41,10 @@ export default {
Nothing.
+## :books: Further Reading
+
+- [Migration Guide - VNode Lifecycle Events](https://v3-migration.vuejs.org/breaking-changes/vnode-lifecycle-events.html#migration-strategy)
+
## :rocket: Version
This rule was introduced in eslint-plugin-vue v7.0.0
diff --git a/docs/rules/no-deprecated-dollar-listeners-api.md b/docs/rules/no-deprecated-dollar-listeners-api.md
index f0f1616cc..91854806b 100644
--- a/docs/rules/no-deprecated-dollar-listeners-api.md
+++ b/docs/rules/no-deprecated-dollar-listeners-api.md
@@ -5,11 +5,12 @@ title: vue/no-deprecated-dollar-listeners-api
description: disallow using deprecated `$listeners` (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-dollar-listeners-api
> disallow using deprecated `$listeners` (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
@@ -46,6 +47,7 @@ Nothing.
## :books: Further Reading
- [Vue RFCs - 0031-attr-fallthrough](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md)
+- [Migration Guide - `$listeners` removed](https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-dollar-scopedslots-api.md b/docs/rules/no-deprecated-dollar-scopedslots-api.md
index 3183ea472..45e67d480 100644
--- a/docs/rules/no-deprecated-dollar-scopedslots-api.md
+++ b/docs/rules/no-deprecated-dollar-scopedslots-api.md
@@ -5,18 +5,19 @@ title: vue/no-deprecated-dollar-scopedslots-api
description: disallow using deprecated `$scopedSlots` (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-dollar-scopedslots-api
> disallow using deprecated `$scopedSlots` (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
This rule reports use of deprecated `$scopedSlots`. (in Vue.js 3.0.0+).
-See [Migration Guide - Slots Unification](https://v3.vuejs.org/guide/migration/slots-unification.html) for more details.
+See [Migration Guide - Slots Unification](https://v3-migration.vuejs.org/breaking-changes/slots-unification.html) for more details.
@@ -43,7 +44,7 @@ Nothing.
## :books: Further Reading
-- [Migration Guide - Slots Unification](https://v3.vuejs.org/guide/migration/slots-unification.html)
+- [Migration Guide - Slots Unification](https://v3-migration.vuejs.org/breaking-changes/slots-unification.html)
- [Vue RFCs - 0006-slots-unification](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0006-slots-unification.md)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-events-api.md b/docs/rules/no-deprecated-events-api.md
index f78ec8c71..a3539d7cf 100644
--- a/docs/rules/no-deprecated-events-api.md
+++ b/docs/rules/no-deprecated-events-api.md
@@ -5,17 +5,18 @@ title: vue/no-deprecated-events-api
description: disallow using deprecated events api (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-events-api
> disallow using deprecated events api (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
This rule reports use of deprecated `$on`, `$off` `$once` api. (in Vue.js 3.0.0+).
-See [Migration Guide - Events API](https://v3.vuejs.org/guide/migration/events-api.html) for more details.
+See [Migration Guide - Events API](https://v3-migration.vuejs.org/breaking-changes/events-api.html) for more details.
@@ -23,8 +24,8 @@ See [Migration Guide - Events API](https://v3.vuejs.org/guide/migration/events-a
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/no-deprecated-model-definition": ["error", {
+ "allowVue3Compat": true
+ }]
+}
+```
+
+### `"allowVue3Compat": true`
+
+Allow `model` definitions with prop/event names that match the Vue.js 3.0.0+ `v-model` syntax, i.e. `modelValue`/`update:modelValue` or `model-value`/`update:model-value`.
+
+
+
+```vue
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/valid-model-definition](./valid-model-definition.md) (for Vue.js 2.x)
+- [vue/no-v-model-argument](./no-v-model-argument.md) (for Vue.js 2.x)
+
+## :books: Further Reading
+
+- [Migration Guide – `v-model`](https://v3-migration.vuejs.org/breaking-changes/v-model.html)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.16.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-model-definition.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-model-definition.js)
diff --git a/docs/rules/no-deprecated-props-default-this.md b/docs/rules/no-deprecated-props-default-this.md
index 4710fec02..af8136fa2 100644
--- a/docs/rules/no-deprecated-props-default-this.md
+++ b/docs/rules/no-deprecated-props-default-this.md
@@ -5,18 +5,19 @@ title: vue/no-deprecated-props-default-this
description: disallow deprecated `this` access in props default function (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-props-default-this
> disallow deprecated `this` access in props default function (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
This rule reports the use of `this` within the props default value factory functions.
In Vue.js 3.0.0+, props default value factory functions no longer have access to `this`.
-See [Migration Guide - Props Default Function `this` Access](https://v3.vuejs.org/guide/migration/props-default-this.html) for more details.
+See [Migration Guide - Props Default Function `this` Access](https://v3-migration.vuejs.org/breaking-changes/props-default-this.html) for more details.
@@ -26,7 +27,7 @@ export default {
props: {
a: String,
b: {
- default () {
+ default() {
/* ✗ BAD */
return this.a
}
@@ -46,7 +47,7 @@ export default {
props: {
a: String,
b: {
- default (props) {
+ default(props) {
/* ✓ GOOD */
return props.a
}
@@ -64,7 +65,7 @@ Nothing.
## :books: Further Reading
-- [Migration Guide - Props Default Function `this` Access](https://v3.vuejs.org/guide/migration/props-default-this.html)
+- [Migration Guide - Props Default Function `this` Access](https://v3-migration.vuejs.org/breaking-changes/props-default-this.html)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-router-link-tag-prop.md b/docs/rules/no-deprecated-router-link-tag-prop.md
index b53433b4c..1ec049fd0 100644
--- a/docs/rules/no-deprecated-router-link-tag-prop.md
+++ b/docs/rules/no-deprecated-router-link-tag-prop.md
@@ -5,11 +5,12 @@ title: vue/no-deprecated-router-link-tag-prop
description: disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)
since: v7.20.0
---
+
# vue/no-deprecated-router-link-tag-prop
> disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
diff --git a/docs/rules/no-deprecated-scope-attribute.md b/docs/rules/no-deprecated-scope-attribute.md
index cae02132a..d19b172a8 100644
--- a/docs/rules/no-deprecated-scope-attribute.md
+++ b/docs/rules/no-deprecated-scope-attribute.md
@@ -5,12 +5,13 @@ title: vue/no-deprecated-scope-attribute
description: disallow deprecated `scope` attribute (in Vue.js 2.5.0+)
since: v6.0.0
---
+
# vue/no-deprecated-scope-attribute
> disallow deprecated `scope` attribute (in Vue.js 2.5.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -42,7 +43,7 @@ This rule reports deprecated `scope` attribute in Vue.js v2.5.0+.
## :books: Further Reading
-- [API - scope](https://vuejs.org/v2/api/#scope-removed)
+- [API - scope](https://v2.vuejs.org/v2/api/#scope-removed)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-slot-attribute.md b/docs/rules/no-deprecated-slot-attribute.md
index d8a45cff5..64f2c5e00 100644
--- a/docs/rules/no-deprecated-slot-attribute.md
+++ b/docs/rules/no-deprecated-slot-attribute.md
@@ -5,12 +5,13 @@ title: vue/no-deprecated-slot-attribute
description: disallow deprecated `slot` attribute (in Vue.js 2.6.0+)
since: v6.1.0
---
+
# vue/no-deprecated-slot-attribute
> disallow deprecated `slot` attribute (in Vue.js 2.6.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -37,9 +38,52 @@ This rule reports deprecated `slot` attribute in Vue.js v2.6.0+.
+## :wrench: Options
+
+```json
+{
+ "vue/no-deprecated-slot-attribute": ["error", {
+ "ignore": ["my-component"]
+ }]
+}
+```
+
+- `"ignore"` (`string[]`) An array of tags that ignore this rules. This option will check both kebab-case and PascalCase versions of the given tag names. Default is empty.
+
+### `"ignore": ["my-component"]`
+
+
+
+```vue
+
+
+
+
+ {{ props.title }}
+
+
+
+
+
+
+ {{ props.title }}
+
+
+
+
+
+
+ {{ props.title }}
+
+
+
+```
+
+
+
## :books: Further Reading
-- [API - slot](https://vuejs.org/v2/api/#slot-deprecated)
+- [API - slot](https://v2.vuejs.org/v2/api/#slot-deprecated)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-slot-scope-attribute.md b/docs/rules/no-deprecated-slot-scope-attribute.md
index f1fe017e6..86212de0f 100644
--- a/docs/rules/no-deprecated-slot-scope-attribute.md
+++ b/docs/rules/no-deprecated-slot-scope-attribute.md
@@ -5,12 +5,13 @@ title: vue/no-deprecated-slot-scope-attribute
description: disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)
since: v6.1.0
---
+
# vue/no-deprecated-slot-scope-attribute
> disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -39,7 +40,7 @@ This rule reports deprecated `slot-scope` attribute in Vue.js v2.6.0+.
## :books: Further Reading
-- [API - slot-scope](https://vuejs.org/v2/api/#slot-scope-deprecated)
+- [API - slot-scope](https://v2.vuejs.org/v2/api/#slot-scope-deprecated)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-v-bind-sync.md b/docs/rules/no-deprecated-v-bind-sync.md
index dfc652890..77a79f16f 100644
--- a/docs/rules/no-deprecated-v-bind-sync.md
+++ b/docs/rules/no-deprecated-v-bind-sync.md
@@ -5,33 +5,33 @@ title: vue/no-deprecated-v-bind-sync
description: disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-v-bind-sync
> disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
This rule reports use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+).
-See [Migration Guide - `v-model`](https://v3.vuejs.org/guide/migration/v-model.html) for more details.
+See [Migration Guide - `v-model`](https://v3-migration.vuejs.org/breaking-changes/v-model.html) for more details.
```vue
-
-
-
+
+
-
-
-
-
+
+
+
+
```
@@ -49,7 +49,7 @@ Nothing.
## :books: Further Reading
-- [Migration Guide - `v-model`](https://v3.vuejs.org/guide/migration/v-model.html)
+- [Migration Guide - `v-model`](https://v3-migration.vuejs.org/breaking-changes/v-model.html)
- [Vue RFCs - 0005-replace-v-bind-sync-with-v-model-argument](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-v-is.md b/docs/rules/no-deprecated-v-is.md
index 3ac26dde6..9fd1590e4 100644
--- a/docs/rules/no-deprecated-v-is.md
+++ b/docs/rules/no-deprecated-v-is.md
@@ -5,27 +5,27 @@ title: vue/no-deprecated-v-is
description: disallow deprecated `v-is` directive (in Vue.js 3.1.0+)
since: v7.11.0
---
+
# vue/no-deprecated-v-is
> disallow deprecated `v-is` directive (in Vue.js 3.1.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
This rule reports deprecated `v-is` directive in Vue.js v3.1.0+.
-Use [`is` attribute with `vue:` prefix](https://v3.vuejs.org/api/special-attributes.html#is) instead.
+Use [`is` attribute with `vue:` prefix](https://vuejs.org/api/built-in-special-attributes.html#is) instead.
-
+
```vue
-
+
@@ -41,7 +41,8 @@ Use [`is` attribute with `vue:` prefix](https://v3.vuejs.org/api/special-attribu
## :books: Further Reading
-- [API - v-is](https://v3.vuejs.org/api/directives.html#v-is)
+- [Migration Guide - Custom Elements Interop](https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html#vue-prefix-for-in-dom-template-parsing-workarounds)
+- [API - v-is](https://vuejs.org/api/built-in-special-attributes.html#is)
- [API - v-is (Old)](https://github.com/vuejs/docs-next/blob/008613756c3d781128d96b64a2d27f7598f8f548/src/api/directives.md#v-is)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-v-on-native-modifier.md b/docs/rules/no-deprecated-v-on-native-modifier.md
index 394db36bc..8b8312235 100644
--- a/docs/rules/no-deprecated-v-on-native-modifier.md
+++ b/docs/rules/no-deprecated-v-on-native-modifier.md
@@ -5,11 +5,12 @@ title: vue/no-deprecated-v-on-native-modifier
description: disallow using deprecated `.native` modifiers (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-v-on-native-modifier
> disallow using deprecated `.native` modifiers (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
@@ -44,6 +45,7 @@ Nothing.
## :books: Further Reading
- [Vue RFCs - 0031-attr-fallthrough](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md)
+- [Migration Guide - `v-on.native` modifier removed](https://v3-migration.vuejs.org/breaking-changes/v-on-native-modifier-removed.html)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-v-on-number-modifiers.md b/docs/rules/no-deprecated-v-on-number-modifiers.md
index 74e711f9c..54ea7d431 100644
--- a/docs/rules/no-deprecated-v-on-number-modifiers.md
+++ b/docs/rules/no-deprecated-v-on-number-modifiers.md
@@ -5,18 +5,19 @@ title: vue/no-deprecated-v-on-number-modifiers
description: disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-v-on-number-modifiers
> disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
This rule reports use of deprecated `KeyboardEvent.keyCode` modifier on `v-on` directive (in Vue.js 3.0.0+).
-See [Migration Guide - KeyCode Modifiers](https://v3.vuejs.org/guide/migration/keycode-modifiers.html) for more details.
+See [Migration Guide - KeyCode Modifiers](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) for more details.
@@ -48,7 +49,7 @@ Nothing.
## :books: Further Reading
-- [Migration Guide - KeyCode Modifiers](https://v3.vuejs.org/guide/migration/keycode-modifiers.html)
+- [Migration Guide - KeyCode Modifiers](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html)
- [Vue RFCs - 0014-drop-keycode-support](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0014-drop-keycode-support.md)
## :rocket: Version
diff --git a/docs/rules/no-deprecated-vue-config-keycodes.md b/docs/rules/no-deprecated-vue-config-keycodes.md
index b271741e0..697604b8c 100644
--- a/docs/rules/no-deprecated-vue-config-keycodes.md
+++ b/docs/rules/no-deprecated-vue-config-keycodes.md
@@ -5,17 +5,18 @@ title: vue/no-deprecated-vue-config-keycodes
description: disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)
since: v7.0.0
---
+
# vue/no-deprecated-vue-config-keycodes
> disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
This rule reports use of deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+).
-See [Migration Guide - KeyCode Modifiers](https://v3.vuejs.org/guide/migration/keycode-modifiers.html) for more details.
+See [Migration Guide - KeyCode Modifiers](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) for more details.
@@ -44,9 +45,9 @@ Nothing.
- [Vue RFCs - 0014-drop-keycode-support]
- [API - Global Config - keyCodes]
-[Migration Guide - KeyCode Modifiers]: https://v3.vuejs.org/guide/migration/keycode-modifiers.html
+[Migration Guide - KeyCode Modifiers]: https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html
[Vue RFCs - 0014-drop-keycode-support]: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0014-drop-keycode-support.md
-[API - Global Config - keyCodes]: https://vuejs.org/v2/api/#keyCodes
+[API - Global Config - keyCodes]: https://v2.vuejs.org/v2/api/#keyCodes
## :rocket: Version
diff --git a/docs/rules/no-dupe-keys.md b/docs/rules/no-dupe-keys.md
index 41f4a962f..3372a8e26 100644
--- a/docs/rules/no-dupe-keys.md
+++ b/docs/rules/no-dupe-keys.md
@@ -5,17 +5,19 @@ title: vue/no-dupe-keys
description: disallow duplication of field names
since: v3.9.0
---
+
# vue/no-dupe-keys
> disallow duplication of field names
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
-This rule prevents to use duplicated names.
+This rule prevents using duplicate key names.
## :book: Rule Details
-This rule is aimed at preventing duplicated property names.
+This rule prevents duplicate `props`/`data`/`methods`/etc. key names defined on a component.
+Even if a key name does not conflict in the `
@@ -64,10 +66,10 @@ export default {
/* ✗ BAD */
export default {
computed: {
- foo () {}
+ foo() {}
},
firebase: {
- foo () {}
+ foo() {}
}
}
diff --git a/docs/rules/no-dupe-v-else-if.md b/docs/rules/no-dupe-v-else-if.md
index 4fbdffcbc..d5bf83797 100644
--- a/docs/rules/no-dupe-v-else-if.md
+++ b/docs/rules/no-dupe-v-else-if.md
@@ -5,11 +5,12 @@ title: vue/no-dupe-v-else-if
description: disallow duplicate conditions in `v-if` / `v-else-if` chains
since: v7.0.0
---
+
# 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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
diff --git a/docs/rules/no-duplicate-attr-inheritance.md b/docs/rules/no-duplicate-attr-inheritance.md
index 908b49b19..fe3cd37bf 100644
--- a/docs/rules/no-duplicate-attr-inheritance.md
+++ b/docs/rules/no-duplicate-attr-inheritance.md
@@ -5,16 +5,17 @@ title: vue/no-duplicate-attr-inheritance
description: enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"`
since: v7.0.0
---
+
# vue/no-duplicate-attr-inheritance
> enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"`
## :book: Rule Details
-This rule aims to prevent duplicated attribute inheritance.
-This rule to warn to apply `inheritAttrs: false` when it detects `v-bind="$attrs"` being used.
+This rule aims to prevent duplicate attribute inheritance.
+This rule suggests applying `inheritAttrs: false` when it detects `v-bind="$attrs"` being used.
-
+
```vue
@@ -25,11 +26,12 @@ export default {
/* ✓ GOOD */
inheritAttrs: false
}
+
```
-
+
```vue
@@ -40,17 +42,46 @@ export default {
/* ✗ BAD */
// inheritAttrs: true (default)
}
+
```
## :wrench: Options
-Nothing.
+```json
+{
+ "vue/no-duplicate-attr-inheritance": ["error", {
+ "checkMultiRootNodes": false,
+ }]
+}
+```
+
+- `"checkMultiRootNodes"`: If set to `true`, also suggest applying `inheritAttrs: false` to components with multiple root nodes (where `inheritAttrs: false` is the implicit default, see [attribute inheritance on multiple root nodes](https://vuejs.org/guide/components/attrs.html#attribute-inheritance-on-multiple-root-nodes)), whenever it detects `v-bind="$attrs"` being used. Default is `false`, which will ignore components with multiple root nodes.
+
+### `"checkMultiRootNodes": true`
+
+
+
+```vue
+
+
+
+
+
+```
+
+
## :books: Further Reading
-- [API - inheritAttrs](https://v3.vuejs.org/api/options-misc.html#inheritattrs)
+- [API - inheritAttrs](https://vuejs.org/api/options-misc.html#inheritattrs)
+- [Fallthrough Attributes](https://vuejs.org/guide/components/attrs.html#attribute-inheritance-on-multiple-root-nodes)
## :rocket: Version
diff --git a/docs/rules/no-duplicate-attributes.md b/docs/rules/no-duplicate-attributes.md
index 000b67414..9ee2d2be6 100644
--- a/docs/rules/no-duplicate-attributes.md
+++ b/docs/rules/no-duplicate-attributes.md
@@ -5,19 +5,19 @@ title: vue/no-duplicate-attributes
description: disallow duplication of attributes
since: v3.0.0
---
+
# vue/no-duplicate-attributes
> disallow duplication of attributes
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
-When duplicate arguments exist, only the last one is valid.
-It's possibly mistakes.
+When there are multiple attributes with the same name on a component, only the last one is used and the rest are ignored, so this is usually a mistake.
## :book: Rule Details
This rule reports duplicate attributes.
-`v-bind:foo` directives are handled as the attributes `foo`.
+`v-bind:foo` directives are handled as the attribute `foo`.
@@ -53,8 +53,8 @@ This rule reports duplicate attributes.
- `allowCoexistClass` (`boolean`) ... Enables [`v-bind:class`] directive can coexist with the plain `class` attribute. Default is `true`.
- `allowCoexistStyle` (`boolean`) ... Enables [`v-bind:style`] directive can coexist with the plain `style` attribute. Default is `true`.
-[`v-bind:class`]: https://v3.vuejs.org/guide/class-and-style.html
-[`v-bind:style`]: https://v3.vuejs.org/guide/class-and-style.html
+[`v-bind:class`]: https://vuejs.org/guide/essentials/class-and-style.html
+[`v-bind:style`]: https://vuejs.org/guide/essentials/class-and-style.html
### `"allowCoexistClass": false, "allowCoexistStyle": false`
diff --git a/docs/rules/no-empty-component-block.md b/docs/rules/no-empty-component-block.md
index 5a15b6d7d..344bd0cba 100644
--- a/docs/rules/no-empty-component-block.md
+++ b/docs/rules/no-empty-component-block.md
@@ -5,18 +5,21 @@ title: vue/no-empty-component-block
description: disallow the `` `
@@ -43,7 +46,6 @@ See: https://vue-loader.vuejs.org/spec.html#src-imports
-
diff --git a/docs/rules/no-empty-pattern.md b/docs/rules/no-empty-pattern.md
index 6b017a8b0..6434a7bff 100644
--- a/docs/rules/no-empty-pattern.md
+++ b/docs/rules/no-empty-pattern.md
@@ -2,12 +2,13 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-empty-pattern
-description: disallow empty destructuring patterns
+description: Disallow empty destructuring patterns in ``
since: v6.0.0
---
+
# vue/no-empty-pattern
-> disallow empty destructuring patterns
+> Disallow empty destructuring patterns in ``
This rule is the same rule as core [no-empty-pattern] rule but it applies to the expressions in ``.
@@ -26,4 +27,4 @@ This rule was introduced in eslint-plugin-vue v6.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-empty-pattern.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-empty-pattern.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-empty-pattern)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/no-empty-pattern)
diff --git a/docs/rules/no-export-in-script-setup.md b/docs/rules/no-export-in-script-setup.md
index b059305b9..08166596b 100644
--- a/docs/rules/no-export-in-script-setup.md
+++ b/docs/rules/no-export-in-script-setup.md
@@ -5,11 +5,12 @@ title: vue/no-export-in-script-setup
description: disallow `export` in `
+```
+
+
+
## :wrench: Options
Nothing.
diff --git a/docs/rules/no-extra-parens.md b/docs/rules/no-extra-parens.md
index 49359d948..442a965ba 100644
--- a/docs/rules/no-extra-parens.md
+++ b/docs/rules/no-extra-parens.md
@@ -2,21 +2,27 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-extra-parens
-description: disallow unnecessary parentheses
+description: Disallow unnecessary parentheses in ``
since: v7.0.0
---
+
# vue/no-extra-parens
-> disallow unnecessary parentheses
+> Disallow unnecessary parentheses in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/no-extra-parens] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [no-extra-parens] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :book: Rule Details
This rule restricts the use of parentheses to only where they are necessary.
-This rule extends the core [no-extra-parens] rule and applies it to the ``. This rule also checks some Vue.js syntax.
+This rule extends the [@stylistic/no-extra-parens] rule and applies it to the ``. This rule also checks some Vue.js syntax.
@@ -37,8 +43,10 @@ This rule extends the core [no-extra-parens] rule and applies it to the `Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-extra-parens)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/no-extra-parens)
diff --git a/docs/rules/no-implicit-coercion.md b/docs/rules/no-implicit-coercion.md
new file mode 100644
index 000000000..22698320c
--- /dev/null
+++ b/docs/rules/no-implicit-coercion.md
@@ -0,0 +1,32 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-implicit-coercion
+description: Disallow shorthand type conversions in ``
+since: v9.33.0
+---
+
+# vue/no-implicit-coercion
+
+> Disallow shorthand type conversions in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as core [no-implicit-coercion] rule but it applies to the expressions in ``.
+
+## :books: Further Reading
+
+- [no-implicit-coercion]
+
+[no-implicit-coercion]: https://eslint.org/docs/rules/no-implicit-coercion
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.33.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-implicit-coercion.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-implicit-coercion.js)
+
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/no-implicit-coercion)
diff --git a/docs/rules/no-import-compiler-macros.md b/docs/rules/no-import-compiler-macros.md
new file mode 100644
index 000000000..7ceadb336
--- /dev/null
+++ b/docs/rules/no-import-compiler-macros.md
@@ -0,0 +1,58 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-import-compiler-macros
+description: disallow importing Vue compiler macros
+since: v10.0.0
+---
+
+# vue/no-import-compiler-macros
+
+> disallow importing Vue compiler macros
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule disallow importing vue compiler macros.
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [defineProps() & defineEmits()]
+
+[defineProps() & defineEmits()]: https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v10.0.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-import-compiler-macros.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-import-compiler-macros.js)
diff --git a/docs/rules/no-invalid-model-keys.md b/docs/rules/no-invalid-model-keys.md
index 615b69855..e93c79db4 100644
--- a/docs/rules/no-invalid-model-keys.md
+++ b/docs/rules/no-invalid-model-keys.md
@@ -5,10 +5,13 @@ title: vue/no-invalid-model-keys
description: require valid keys in model option
since: v7.9.0
---
+
# vue/no-invalid-model-keys
> require valid keys in model option
+- :no_entry: This rule was **removed** in eslint-plugin-vue v10.0.0 and replaced by [vue/valid-model-definition](valid-model-definition.md) rule.
+
## :book: Rule Details
This rule is aimed at preventing invalid keys in model option.
@@ -108,7 +111,6 @@ export default {
-
## :rocket: Version
This rule was introduced in eslint-plugin-vue v7.9.0
diff --git a/docs/rules/no-irregular-whitespace.md b/docs/rules/no-irregular-whitespace.md
index 401b0feef..615f2294a 100644
--- a/docs/rules/no-irregular-whitespace.md
+++ b/docs/rules/no-irregular-whitespace.md
@@ -2,12 +2,13 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-irregular-whitespace
-description: disallow irregular whitespace
+description: disallow irregular whitespace in `.vue` files
since: v6.1.0
---
+
# vue/no-irregular-whitespace
-> disallow irregular whitespace
+> disallow irregular whitespace in `.vue` files
## :book: Rule Details
diff --git a/docs/rules/no-lifecycle-after-await.md b/docs/rules/no-lifecycle-after-await.md
index dbcef6b80..78d54c526 100644
--- a/docs/rules/no-lifecycle-after-await.md
+++ b/docs/rules/no-lifecycle-after-await.md
@@ -5,11 +5,12 @@ title: vue/no-lifecycle-after-await
description: disallow asynchronously registered lifecycle hooks
since: v7.0.0
---
+
# vue/no-lifecycle-after-await
> disallow asynchronously registered lifecycle hooks
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
@@ -43,7 +44,7 @@ Nothing.
## :books: Further Reading
-- [Guide - Composition API - Lifecycle Hooks](https://v3.vuejs.org/guide/composition-api-lifecycle-hooks.html)
+- [Guide - Composition API - Lifecycle Hooks](https://vuejs.org/api/composition-api-lifecycle.html)
- [Vue RFCs - 0013-composition-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0013-composition-api.md)
## :rocket: Version
diff --git a/docs/rules/no-lone-template.md b/docs/rules/no-lone-template.md
index 0c3904e3c..170b63ce3 100644
--- a/docs/rules/no-lone-template.md
+++ b/docs/rules/no-lone-template.md
@@ -5,11 +5,12 @@ title: vue/no-lone-template
description: disallow unnecessary ``
since: v7.0.0
---
+
# vue/no-lone-template
> disallow unnecessary ``
-- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
diff --git a/docs/rules/no-loss-of-precision.md b/docs/rules/no-loss-of-precision.md
index c558513cd..c9b88ce63 100644
--- a/docs/rules/no-loss-of-precision.md
+++ b/docs/rules/no-loss-of-precision.md
@@ -2,12 +2,13 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-loss-of-precision
-description: disallow literal numbers that lose precision
+description: Disallow literal numbers that lose precision in ``
since: v8.0.0
---
+
# vue/no-loss-of-precision
-> disallow literal numbers that lose precision
+> Disallow literal numbers that lose precision in ``
This rule is the same rule as core [no-loss-of-precision] rule but it applies to the expressions in ``.
@@ -30,4 +31,4 @@ This rule was introduced in eslint-plugin-vue v8.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-loss-of-precision.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-loss-of-precision.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-loss-of-precision)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/no-loss-of-precision)
diff --git a/docs/rules/no-multi-spaces.md b/docs/rules/no-multi-spaces.md
index 065b799b7..66c6a81d7 100644
--- a/docs/rules/no-multi-spaces.md
+++ b/docs/rules/no-multi-spaces.md
@@ -5,12 +5,13 @@ title: vue/no-multi-spaces
description: disallow multiple spaces
since: v3.12.0
---
+
# vue/no-multi-spaces
> disallow multiple spaces
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
diff --git a/docs/rules/no-multiple-objects-in-class.md b/docs/rules/no-multiple-objects-in-class.md
index 9e1508b8b..13f1f591b 100644
--- a/docs/rules/no-multiple-objects-in-class.md
+++ b/docs/rules/no-multiple-objects-in-class.md
@@ -2,16 +2,17 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-multiple-objects-in-class
-description: disallow to pass multiple objects into array to class
+description: disallow passing multiple objects in an array to class
since: v7.0.0
---
+
# vue/no-multiple-objects-in-class
-> disallow to pass multiple objects into array to class
+> disallow passing multiple objects in an array to class
## :book: Rule Details
-This rule disallows to pass multiple objects into array to class.
+This rule disallows to pass multiple objects into array to class.
@@ -19,10 +20,10 @@ This rule disallows to pass multiple objects into array to class.
-
+
-
+
```
diff --git a/docs/rules/no-multiple-slot-args.md b/docs/rules/no-multiple-slot-args.md
index c73be9007..74d6c2931 100644
--- a/docs/rules/no-multiple-slot-args.md
+++ b/docs/rules/no-multiple-slot-args.md
@@ -2,14 +2,15 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-multiple-slot-args
-description: disallow to pass multiple arguments to scoped slots
+description: disallow passing multiple arguments to scoped slots
since: v7.0.0
---
+
# vue/no-multiple-slot-args
-> disallow to pass multiple arguments to scoped slots
+> disallow passing multiple arguments to scoped slots
-- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
diff --git a/docs/rules/no-multiple-template-root.md b/docs/rules/no-multiple-template-root.md
index e5130faab..8a85c9201 100644
--- a/docs/rules/no-multiple-template-root.md
+++ b/docs/rules/no-multiple-template-root.md
@@ -5,11 +5,12 @@ title: vue/no-multiple-template-root
description: disallow adding multiple root nodes to the template
since: v7.0.0
---
+
# vue/no-multiple-template-root
> disallow adding multiple root nodes to the template
-- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -41,7 +42,7 @@ This rule checks whether template contains single root element valid for Vue 2.
```vue
-
+
```
@@ -60,7 +61,32 @@ This rule checks whether template contains single root element valid for Vue 2.
## :wrench: Options
-Nothing.
+```json
+{
+ "vue/no-multiple-template-root": ["error", {
+ "disallowComments": false
+ }]
+}
+```
+
+- "disallowComments" (`boolean`) Enables there should not be any comments in the template root. Default is `false`.
+
+### "disallowComments": true
+
+
+
+```vue
+/* ✗ BAD */
+
+
+
+ vue eslint plugin
+
+
+
+```
+
+
## :rocket: Version
diff --git a/docs/rules/no-mutating-props.md b/docs/rules/no-mutating-props.md
index 1b58da7d4..3ddee4cc2 100644
--- a/docs/rules/no-mutating-props.md
+++ b/docs/rules/no-mutating-props.md
@@ -5,11 +5,12 @@ title: vue/no-mutating-props
description: disallow mutation of component props
since: v7.0.0
---
+
# vue/no-mutating-props
> disallow mutation of component props
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -22,22 +23,38 @@ This rule reports mutation of component props.
+
+
```
@@ -50,22 +67,38 @@ This rule reports mutation of component props.
+
+
```
@@ -75,12 +108,12 @@ This rule reports mutation of component props.
```vue
```
@@ -88,12 +121,50 @@ This rule reports mutation of component props.
## :wrench: Options
-Nothing.
+```json
+{
+ "vue/no-mutating-props": ["error", {
+ "shallowOnly": false
+ }]
+}
+```
+
+- "shallowOnly" (`boolean`) Enables mutating the value of a prop but leaving the reference the same. Default is `false`.
+
+### "shallowOnly": true
+
+
+
+```vue
+
+
+
+
+
+
+
+```
+
+
## :books: Further Reading
-- [Style guide - Implicit parent-child communication](https://v3.vuejs.org/style-guide/#implicit-parent-child-communication-use-with-caution)
-- [Vue - Prop Mutation - deprecated](https://vuejs.org/v2/guide/migration.html#Prop-Mutation-deprecated)
+- [Style guide - Implicit parent-child communication](https://vuejs.org/style-guide/rules-use-with-caution.html#implicit-parent-child-communication)
+- [Vue - Prop Mutation - deprecated](https://v2.vuejs.org/v2/guide/migration.html#Prop-Mutation-deprecated)
## :rocket: Version
diff --git a/docs/rules/no-parsing-error.md b/docs/rules/no-parsing-error.md
index 811fd06af..a6fcf2908 100644
--- a/docs/rules/no-parsing-error.md
+++ b/docs/rules/no-parsing-error.md
@@ -5,21 +5,22 @@ title: vue/no-parsing-error
description: disallow parsing errors in ``
since: v3.0.0
---
+
# vue/no-parsing-error
> disallow parsing errors in ``
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule reports syntax errors in ``. For example:
- Syntax errors of scripts in directives.
- Syntax errors of scripts in mustaches.
- Syntax errors of HTML.
- - Invalid end tags.
- - Attributes in end tags.
- - ...
- - See also: [WHATWG HTML spec](https://html.spec.whatwg.org/multipage/parsing.html#parse-errors)
+ - Invalid end tags.
+ - Attributes in end tags.
+ - ...
+ - See also: [WHATWG HTML spec](https://html.spec.whatwg.org/multipage/parsing.html#parse-errors)
## :book: Rule Details
diff --git a/docs/rules/no-potential-component-option-typo.md b/docs/rules/no-potential-component-option-typo.md
index bcd2d4ed3..17e21c818 100644
--- a/docs/rules/no-potential-component-option-typo.md
+++ b/docs/rules/no-potential-component-option-typo.md
@@ -5,6 +5,7 @@ title: vue/no-potential-component-option-typo
description: disallow a potential typo in your component property
since: v7.0.0
---
+
# vue/no-potential-component-option-typo
> disallow a potential typo in your component property
@@ -15,7 +16,7 @@ since: v7.0.0
This rule disallow a potential typo in your component options
-**Here is the config**
+### Here is the config
```json
{
@@ -59,7 +60,7 @@ export default {
> we use edit distance to compare two string similarity, threshold is an option to control upper bound of edit distance to report
-**Here is the another example about config option `threshold`**
+### Here is the another example about config option `threshold`
```json
{
diff --git a/docs/rules/no-ref-as-operand.md b/docs/rules/no-ref-as-operand.md
index 2610d4624..af10e0b7d 100644
--- a/docs/rules/no-ref-as-operand.md
+++ b/docs/rules/no-ref-as-operand.md
@@ -5,12 +5,13 @@ title: vue/no-ref-as-operand
description: disallow use of value wrapped by `ref()` (Composition API) as an operand
since: v7.0.0
---
+
# vue/no-ref-as-operand
> disallow use of value wrapped by `ref()` (Composition API) as an operand
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -24,7 +25,7 @@ You must use `.value` to access the `Ref` value.
import { ref } from 'vue'
export default {
- setup () {
+ setup(_props, { emit }) {
const count = ref(0)
const ok = ref(true)
@@ -33,12 +34,14 @@ export default {
count.value + 1
1 + count.value
var msg = ok.value ? 'yes' : 'no'
+ emit('increment', count.value)
/* ✗ BAD */
count++
count + 1
1 + count
var msg = ok ? 'yes' : 'no'
+ emit('increment', count)
return {
count
diff --git a/docs/rules/no-ref-object-destructure.md b/docs/rules/no-ref-object-destructure.md
new file mode 100644
index 000000000..8ea5247a1
--- /dev/null
+++ b/docs/rules/no-ref-object-destructure.md
@@ -0,0 +1,61 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-ref-object-destructure
+description: disallow usages of ref objects that can lead to loss of reactivity
+since: v9.5.0
+---
+
+# vue/no-ref-object-destructure
+
+> disallow usages of ref objects that can lead to loss of reactivity
+
+- :no_entry: This rule was **removed** in eslint-plugin-vue v10.0.0 and replaced by [vue/no-ref-object-reactivity-loss](no-ref-object-reactivity-loss.md) rule.
+
+## :book: Rule Details
+
+This rule reports the destructuring of ref objects causing the value to lose reactivity.
+
+
+
+```js
+import { ref } from 'vue'
+const count = ref(0)
+const v1 = count.value /* ✗ BAD */
+const { value: v2 } = count /* ✗ BAD */
+const v3 = computed(() => count.value /* ✓ GOOD */)
+const v4 = fn(count.value) /* ✗ BAD */
+const v5 = fn(count) /* ✓ GOOD */
+const v6 = computed(() => fn(count.value) /* ✓ GOOD */)
+```
+
+
+
+This rule also supports Reactivity Transform, but Reactivity Transform is an experimental feature and may have false positives due to future Vue changes.
+See the [RFC](https://github.com/vuejs/rfcs/pull/420) for more information on Reactivity Transform.
+
+
+
+```js
+const count = $ref(0)
+const v1 = count /* ✗ BAD */
+const v2 = $computed(() => count /* ✓ GOOD */)
+const v3 = fn(count) /* ✗ BAD */
+const v4 = fn($$(count)) /* ✓ GOOD */
+const v5 = $computed(() => fn(count) /* ✓ GOOD */)
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-ref-object-destructure.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-ref-object-destructure.js)
diff --git a/docs/rules/no-ref-object-reactivity-loss.md b/docs/rules/no-ref-object-reactivity-loss.md
new file mode 100644
index 000000000..5df8ed52b
--- /dev/null
+++ b/docs/rules/no-ref-object-reactivity-loss.md
@@ -0,0 +1,59 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-ref-object-reactivity-loss
+description: disallow usages of ref objects that can lead to loss of reactivity
+since: v9.17.0
+---
+
+# vue/no-ref-object-reactivity-loss
+
+> disallow usages of ref objects that can lead to loss of reactivity
+
+## :book: Rule Details
+
+This rule reports the usages of ref objects causing the value to lose reactivity.
+
+
+
+```js
+import { ref } from 'vue'
+const count = ref(0)
+const v1 = count.value /* ✗ BAD */
+const { value: v2 } = count /* ✗ BAD */
+const v3 = computed(() => count.value /* ✓ GOOD */)
+const v4 = fn(count.value) /* ✗ BAD */
+const v5 = fn(count) /* ✓ GOOD */
+const v6 = computed(() => fn(count.value) /* ✓ GOOD */)
+```
+
+
+
+This rule also supports Reactivity Transform, but Reactivity Transform is an experimental feature and may have false positives due to future Vue changes.
+See the [RFC](https://github.com/vuejs/rfcs/pull/420) for more information on Reactivity Transform.
+
+
+
+```js
+const count = $ref(0)
+const v1 = count /* ✗ BAD */
+const v2 = $computed(() => count /* ✓ GOOD */)
+const v3 = fn(count) /* ✗ BAD */
+const v4 = fn($$(count)) /* ✓ GOOD */
+const v5 = $computed(() => fn(count) /* ✓ GOOD */)
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.17.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-ref-object-reactivity-loss.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-ref-object-reactivity-loss.js)
diff --git a/docs/rules/no-required-prop-with-default.md b/docs/rules/no-required-prop-with-default.md
new file mode 100644
index 000000000..8308e4d8c
--- /dev/null
+++ b/docs/rules/no-required-prop-with-default.md
@@ -0,0 +1,102 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-required-prop-with-default
+description: enforce props with default values to be optional
+since: v9.6.0
+---
+
+# vue/no-required-prop-with-default
+
+> enforce props with default values to be optional
+
+- :gear: This rule is included in all of `"plugin:vue/vue2-recommended"`, `*.configs["flat/vue2-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+If a prop is declared with a default value, whether it is required or not, we can always skip it in actual use. In that situation, the default value would be applied.
+So, a required prop with a default value is essentially the same as an optional prop.
+This rule enforces all props with default values to be optional.
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/no-required-prop-with-default": ["error", {
+ "autofix": false,
+ }]
+}
+```
+
+- `"autofix"` ... If `true`, enable autofix. (Default: `false`)
+
+## :couple: Related Rules
+
+- [vue/require-default-prop](./require-default-prop.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.6.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-required-prop-with-default.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-required-prop-with-default.js)
diff --git a/docs/rules/no-reserved-component-names.md b/docs/rules/no-reserved-component-names.md
index d424fb8f1..c99191872 100644
--- a/docs/rules/no-reserved-component-names.md
+++ b/docs/rules/no-reserved-component-names.md
@@ -5,10 +5,13 @@ title: vue/no-reserved-component-names
description: disallow the use of reserved names in component definitions
since: v6.1.0
---
+
# vue/no-reserved-component-names
> disallow the use of reserved names in component definitions
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+
## :book: Rule Details
This rule prevents name collisions between Vue components and standard HTML elements and built-in components.
@@ -32,13 +35,15 @@ export default {
{
"vue/no-reserved-component-names": ["error", {
"disallowVueBuiltInComponents": false,
- "disallowVue3BuiltInComponents": false
+ "disallowVue3BuiltInComponents": false,
+ "htmlElementCaseSensitive": false,
}]
}
```
- `disallowVueBuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 2.x built-in component names. Default is `false`.
- `disallowVue3BuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 3.x built-in component names. Default is `false`.
+- `htmlElementCaseSensitive` (`boolean`) ... If `true`, component names must exactly match the case of an HTML element to be considered conflicting. Default is `false` (i.e. case-insensitve comparison).
### `"disallowVueBuiltInComponents": true`
@@ -70,14 +75,46 @@ export default {
+### `"htmlElementCaseSensitive": true`
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/multi-word-component-names](./multi-word-component-names.md)
+
## :books: Further Reading
- [List of html elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element)
- [List of SVG elements](https://developer.mozilla.org/en-US/docs/Web/SVG/Element)
- [Kebab case elements](https://stackoverflow.com/questions/22545621/do-custom-elements-require-a-dash-in-their-name/22545622#22545622)
-- [Valid custom element name](https://w3c.github.io/webcomponents/spec/custom/#valid-custom-element-name)
-- [API - Built-In Components](https://v3.vuejs.org/api/built-in-components.html)
-- [API (for v2) - Built-In Components](https://vuejs.org/v2/api/index.html#Built-In-Components)
+- [Valid custom element name](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name)
+- [API - Built-In Components](https://vuejs.org/api/built-in-components.html)
+- [API (for v2) - Built-In Components](https://v2.vuejs.org/v2/api/index.html#Built-In-Components)
## :rocket: Version
diff --git a/docs/rules/no-reserved-keys.md b/docs/rules/no-reserved-keys.md
index 36535c5b5..9244ee0b7 100644
--- a/docs/rules/no-reserved-keys.md
+++ b/docs/rules/no-reserved-keys.md
@@ -5,11 +5,12 @@ title: vue/no-reserved-keys
description: disallow overwriting reserved keys
since: v3.9.0
---
+
# vue/no-reserved-keys
> disallow overwriting reserved keys
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -26,14 +27,14 @@ export default {
},
computed: {
$on: {
- get () {}
+ get() {}
}
},
data: {
_foo: null
},
methods: {
- $nextTick () {}
+ $nextTick() {}
}
}
@@ -64,10 +65,10 @@ export default {
/* ✗ BAD */
export default {
computed: {
- foo () {}
+ foo() {}
},
firebase: {
- foo2 () {}
+ foo2() {}
}
}
diff --git a/docs/rules/no-reserved-props.md b/docs/rules/no-reserved-props.md
index 3c9ac1044..982ddb947 100644
--- a/docs/rules/no-reserved-props.md
+++ b/docs/rules/no-reserved-props.md
@@ -5,11 +5,12 @@ title: vue/no-reserved-props
description: disallow reserved names in props
since: v8.0.0
---
+
# vue/no-reserved-props
> disallow reserved names in props
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
diff --git a/docs/rules/no-restricted-block.md b/docs/rules/no-restricted-block.md
index cb6ab788b..0a96a64a4 100644
--- a/docs/rules/no-restricted-block.md
+++ b/docs/rules/no-restricted-block.md
@@ -5,6 +5,7 @@ title: vue/no-restricted-block
description: disallow specific block
since: v7.4.0
---
+
# vue/no-restricted-block
> disallow specific block
diff --git a/docs/rules/no-restricted-call-after-await.md b/docs/rules/no-restricted-call-after-await.md
index f880f8f12..0082bb881 100644
--- a/docs/rules/no-restricted-call-after-await.md
+++ b/docs/rules/no-restricted-call-after-await.md
@@ -5,6 +5,7 @@ title: vue/no-restricted-call-after-await
description: disallow asynchronously called restricted methods
since: v7.4.0
---
+
# vue/no-restricted-call-after-await
> disallow asynchronously called restricted methods
diff --git a/docs/rules/no-restricted-class.md b/docs/rules/no-restricted-class.md
index c4919ebd6..a69f28d55 100644
--- a/docs/rules/no-restricted-class.md
+++ b/docs/rules/no-restricted-class.md
@@ -5,6 +5,7 @@ title: vue/no-restricted-class
description: disallow specific classes in Vue components
since: v7.19.0
---
+
# vue/no-restricted-class
> disallow specific classes in Vue components
@@ -20,7 +21,7 @@ in the rule configuration.
```json
{
- "vue/no-restricted-class": ["error", "forbidden", "forbidden-two", "forbidden-three"]
+ "vue/no-restricted-class": ["error", "forbidden", "forbidden-two", "forbidden-three", "/^for(bidden|gotten)/"]
}
```
@@ -30,12 +31,12 @@ in the rule configuration.
-
+
-
+
@@ -64,12 +65,13 @@ variables, like below, will not be detected by this rule.
export default {
data() {
return {
- classes: "forbidden"
+ classes: 'forbidden'
}
}
}
```
+
:::
## :rocket: Version
diff --git a/docs/rules/no-restricted-component-names.md b/docs/rules/no-restricted-component-names.md
new file mode 100644
index 000000000..689c73aa1
--- /dev/null
+++ b/docs/rules/no-restricted-component-names.md
@@ -0,0 +1,98 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-restricted-component-names
+description: disallow specific component names
+since: v9.15.0
+---
+
+# vue/no-restricted-component-names
+
+> disallow specific component names
+
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+This rule allows you to specify component names that you don't want to use in your application.
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+## :wrench: Options
+
+This rule takes a list of strings, where each string is a component name or pattern to be restricted:
+
+```json
+{
+ "vue/no-restricted-component-names": ["error", "foo", "/^Disallow/"]
+}
+```
+
+Alternatively, you can specify an object with a `name` property and an optional `message` and `suggest` property:
+
+```json
+{
+ "vue/no-restricted-component-names": [
+ "error",
+ {
+ "name": "Disallow",
+ "message": "Please do not use `Disallow` as a component name",
+ "suggest": "allow"
+ },
+ {
+ "name": "/^custom/",
+ "message": "Please do not use component names starting with 'custom'"
+ }
+ ]
+}
+```
+
+
+
+```vue
+
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/restricted-component-names](./restricted-component-names.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.15.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-restricted-component-names.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-restricted-component-names.js)
diff --git a/docs/rules/no-restricted-component-options.md b/docs/rules/no-restricted-component-options.md
index a11570945..57d736ae3 100644
--- a/docs/rules/no-restricted-component-options.md
+++ b/docs/rules/no-restricted-component-options.md
@@ -5,6 +5,7 @@ title: vue/no-restricted-component-options
description: disallow specific component option
since: v7.0.0
---
+
# vue/no-restricted-component-options
> disallow specific component option
diff --git a/docs/rules/no-restricted-custom-event.md b/docs/rules/no-restricted-custom-event.md
index f18edc3f3..88a3eb424 100644
--- a/docs/rules/no-restricted-custom-event.md
+++ b/docs/rules/no-restricted-custom-event.md
@@ -5,6 +5,7 @@ title: vue/no-restricted-custom-event
description: disallow specific custom event
since: v7.3.0
---
+
# vue/no-restricted-custom-event
> disallow specific custom event
@@ -31,7 +32,7 @@ This rule takes a list of strings, where each string is a custom event name or p
-
+
+```
+
+
+
+Destructuring the `props` passed to `setup` will cause the value to lose reactivity.
+
+
+
+```vue
+
+```
+
+
+
+Also, destructuring in root scope of `setup()` should error, but ok inside nested callbacks or returned render functions:
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Guide - Composition API - Setup](https://vuejs.org/api/composition-api-setup.html)
+- [Vue RFCs - 0013-composition-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0013-composition-api.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.17.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-setup-props-reactivity-loss.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-setup-props-reactivity-loss.js)
diff --git a/docs/rules/no-shared-component-data.md b/docs/rules/no-shared-component-data.md
index 258828a47..1a96cf89f 100644
--- a/docs/rules/no-shared-component-data.md
+++ b/docs/rules/no-shared-component-data.md
@@ -5,12 +5,13 @@ title: vue/no-shared-component-data
description: enforce component's data property to be a function
since: v3.8.0
---
+
# vue/no-shared-component-data
> enforce component's data property to be a function
-- :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"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
When using the data property on a component (i.e. anywhere except on `new Vue`), the value must be a function that returns an object.
@@ -32,7 +33,7 @@ Vue.component('some-comp', {
})
export default {
- data () {
+ data() {
return {
foo: 'bar'
}
@@ -70,7 +71,7 @@ Nothing.
## :books: Further Reading
-- [Style guide (for v2) - Component data](https://vuejs.org/v2/style-guide/#Component-data-essential)
+- [Style guide (for v2) - Component data](https://v2.vuejs.org/v2/style-guide/#Component-data-essential)
- [API - data](https://v3.vuejs.org/api/options-data.html#data-2)
- [API (for v2) - data](https://v3.vuejs.org/api/options-data.html#data-2)
diff --git a/docs/rules/no-side-effects-in-computed-properties.md b/docs/rules/no-side-effects-in-computed-properties.md
index b2d0490b7..970299537 100644
--- a/docs/rules/no-side-effects-in-computed-properties.md
+++ b/docs/rules/no-side-effects-in-computed-properties.md
@@ -5,11 +5,12 @@ title: vue/no-side-effects-in-computed-properties
description: disallow side effects in computed properties
since: v3.6.0
---
+
# vue/no-side-effects-in-computed-properties
> disallow side effects in computed properties
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -24,10 +25,10 @@ It is considered a very bad practice to introduce side effects inside computed p
/* ✓ GOOD */
export default {
computed: {
- fullName () {
+ fullName() {
return `${this.firstName} ${this.lastName}`
},
- reversedArray () {
+ reversedArray() {
return this.array.slice(0).reverse() // .slice makes a copy of the array, instead of mutating the orginal
}
}
@@ -44,11 +45,11 @@ export default {
/* ✗ BAD */
export default {
computed: {
- fullName () {
+ fullName() {
this.firstName = 'lorem' // <- side effect
return `${this.firstName} ${this.lastName}`
},
- reversedArray () {
+ reversedArray() {
return this.array.reverse() // <- side effect - orginal array is being mutated
}
}
@@ -62,7 +63,7 @@ export default {
```vue
```
@@ -50,7 +51,37 @@ This rule aims to eliminate shadowed variable declarations of v-for directives o
## :wrench: Options
-Nothing.
+This rule takes one optional object option, with the property `"allow"`.
+
+```json
+{
+ "vue/no-template-shadow": ["error", { "allow": [] }]
+}
+```
+
+- `"allow"` (`[string]`) Array of identifier names for which shadowing is allowed.
+
+Examples of correct code for the `{ "allow": ["i"] }` option:
+
+
+
+```vue
+
+
+
+
+
+```
+
+
## :rocket: Version
diff --git a/docs/rules/no-template-target-blank.md b/docs/rules/no-template-target-blank.md
index f53289e3e..d2a8fc006 100644
--- a/docs/rules/no-template-target-blank.md
+++ b/docs/rules/no-template-target-blank.md
@@ -5,9 +5,12 @@ title: vue/no-template-target-blank
description: disallow target="_blank" attribute without rel="noopener noreferrer"
since: v7.0.0
---
+
# vue/no-template-target-blank
-> disallow target="_blank" attribute without rel="noopener noreferrer"
+> disallow target="\_blank" attribute without rel="noopener noreferrer"
+
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
## :book: Rule Details
diff --git a/docs/rules/no-textarea-mustache.md b/docs/rules/no-textarea-mustache.md
index 4c26b6bf0..0db4aff4f 100644
--- a/docs/rules/no-textarea-mustache.md
+++ b/docs/rules/no-textarea-mustache.md
@@ -5,11 +5,12 @@ title: vue/no-textarea-mustache
description: disallow mustaches in `
+
::: warning Note
Interpolation on textareas (`
{{text}}
`) won't work. Use `v-model` instead.
-[https://v3.vuejs.org/guide/forms.html#multiline-text](https://v3.vuejs.org/guide/forms.html#multiline-text)
+[https://vuejs.org/guide/essentials/forms.html#multiline-text](https://vuejs.org/guide/essentials/forms.html#multiline-text)
:::
@@ -44,7 +46,7 @@ Nothing.
## :books: Further Reading
-- [Guide - Form Input Bindings / Multiline text](https://v3.vuejs.org/guide/forms.html#multiline-text)
+- [Guide - Form Input Bindings / Multiline text](https://vuejs.org/guide/essentials/forms.html#multiline-text)
## :rocket: Version
diff --git a/docs/rules/no-this-in-before-route-enter.md b/docs/rules/no-this-in-before-route-enter.md
index 2871a617f..d84531ddb 100644
--- a/docs/rules/no-this-in-before-route-enter.md
+++ b/docs/rules/no-this-in-before-route-enter.md
@@ -5,13 +5,14 @@ title: vue/no-this-in-before-route-enter
description: disallow `this` usage in a `beforeRouteEnter` method
since: v7.11.0
---
+
# vue/no-this-in-before-route-enter
> disallow `this` usage in a `beforeRouteEnter` method
## :book: Rule Details
-Because lack of `this` in the `beforeRouteEnter` [(docs)](https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards). This behavior isn't obvious, so it's pretty easy to make a `TypeError`. Especially during some refactor.
+Because lack of `this` in the `beforeRouteEnter` [(docs)](https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards). This behavior isn't obvious, so it's pretty easy to make a `TypeError`. Especially during some refactor.
@@ -25,7 +26,7 @@ export default {
if (this.value === 42) {
}
this.attribute = this.method();
- }
+ }
}
```
@@ -40,7 +41,7 @@ export default {
beforeRouteEnter() {
/* ✓ GOOD */
// anything without this
- }
+ }
}
```
diff --git a/docs/rules/no-undef-components.md b/docs/rules/no-undef-components.md
new file mode 100644
index 000000000..0e28192e3
--- /dev/null
+++ b/docs/rules/no-undef-components.md
@@ -0,0 +1,176 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-undef-components
+description: disallow use of undefined components in ``
+since: v8.4.0
+---
+
+# vue/no-undef-components
+
+> disallow use of undefined components in ``
+
+This rule reports components that are used in the ``, but that are not defined in the `
+
+
+
+
+
+
+
+
+```
+
+
+
+
+
+```vue
+
+
+
```
diff --git a/docs/rules/no-unregistered-components.md b/docs/rules/no-unregistered-components.md
index 20e2867bf..dd8977bc6 100644
--- a/docs/rules/no-unregistered-components.md
+++ b/docs/rules/no-unregistered-components.md
@@ -5,17 +5,20 @@ title: vue/no-unregistered-components
description: disallow using components that are not registered inside templates
since: v7.0.0
---
+
# vue/no-unregistered-components
> disallow using components that are not registered inside templates
+- :no_entry: This rule was **removed** in eslint-plugin-vue v9.0.0 and replaced by [vue/no-undef-components](no-undef-components.md) rule.
+
## :book: Rule Details
This rule reports components that haven't been registered and are being used in the template.
::: warning Note
-This rule cannot check globally registered components and components registered in mixins
-unless you add them as part of the ignored patterns. `component`, `suspense` and `teleport`
+This rule cannot check globally registered components and components registered in mixins
+unless you add them as part of the ignored patterns. `component`, `suspense` and `teleport`
are ignored by default.
:::
@@ -35,19 +38,19 @@ are ignored by default.
```
@@ -65,11 +68,9 @@ are ignored by default.
```
@@ -101,11 +102,9 @@ are ignored by default.
```
@@ -123,11 +122,9 @@ are ignored by default.
```
diff --git a/docs/rules/no-unsupported-features.md b/docs/rules/no-unsupported-features.md
index a70fa2b4e..d77fdbdaa 100644
--- a/docs/rules/no-unsupported-features.md
+++ b/docs/rules/no-unsupported-features.md
@@ -5,11 +5,12 @@ title: vue/no-unsupported-features
description: disallow unsupported Vue.js syntax on the specified version
since: v6.1.0
---
+
# vue/no-unsupported-features
> disallow unsupported Vue.js syntax on the specified version
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -28,24 +29,31 @@ This rule reports unsupported Vue.js syntax on the specified version.
- `version` ... The `version` option accepts [the valid version range of `node-semver`](https://github.com/npm/node-semver#range-grammar). Set the version of Vue.js you are using. This option is required.
- `ignores` ... You can use this `ignores` option to ignore the given features.
-The `"ignores"` option accepts an array of the following strings.
+ The `"ignores"` option accepts an array of the following strings.
+ - Vue.js 3.4.0+
+ - `"define-model"` ... `defineModel()` macro.
+ - `"v-bind-same-name-shorthand"` ... `v-bind` same-name shorthand.
+ - Vue.js 3.3.0+
+ - `"define-slots"` ... `defineSlots()` macro.
+ - `"define-options"` ... `defineOptions()` macro.
- Vue.js 3.2.0+
- - `"v-memo"` ... [v-memo](https://v3.vuejs.org/api/directives.html#v-memo) directive.
+ - `"v-memo"` ... [v-memo](https://vuejs.org/api/built-in-directives.html#v-memo) directive.
- `"v-bind-prop-modifier-shorthand"` ... `v-bind` with `.prop` modifier shorthand.
- `"v-bind-attr-modifier"` ... `.attr` modifier on `v-bind` directive.
- Vue.js 3.1.0+
- - `"is-attribute-with-vue-prefix"` ... [`is` attribute with `vue:` prefix](https://v3.vuejs.org/api/special-attributes.html#is)
+ - `"is-attribute-with-vue-prefix"` ... [`is` attribute with `vue:` prefix](https://vuejs.org/api/built-in-special-attributes.html#is)
- Vue.js 3.0.0+
- - `"style-css-vars-injection"` ... [SFC CSS variable injection][Vue RFCs - 0043-sfc-style-variables]
- - `"script-setup"` ... [`
```
@@ -61,15 +62,15 @@ This rule reports components that haven't been used in the template.
```
@@ -108,17 +109,17 @@ Components registered under `PascalCase` or `camelCase` names have may be called
```
@@ -136,21 +137,20 @@ Components registered under `PascalCase` or `camelCase` names have may be called
```
diff --git a/docs/rules/no-unused-emit-declarations.md b/docs/rules/no-unused-emit-declarations.md
new file mode 100644
index 000000000..54abc81d1
--- /dev/null
+++ b/docs/rules/no-unused-emit-declarations.md
@@ -0,0 +1,63 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-unused-emit-declarations
+description: disallow unused emit declarations
+since: v9.19.0
+---
+
+# vue/no-unused-emit-declarations
+
+> disallow unused emit declarations
+
+## :book: Rule Details
+
+This rule is aimed at eliminating unused emit declarations.
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related Rules
+
+- [vue/require-explicit-emits](./require-explicit-emits.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.19.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-unused-emit-declarations.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-unused-emit-declarations.js)
diff --git a/docs/rules/no-unused-properties.md b/docs/rules/no-unused-properties.md
index da8fa7d87..e0e14416a 100644
--- a/docs/rules/no-unused-properties.md
+++ b/docs/rules/no-unused-properties.md
@@ -5,6 +5,7 @@ title: vue/no-unused-properties
description: disallow unused properties
since: v7.0.0
---
+
# vue/no-unused-properties
> disallow unused properties
@@ -14,7 +15,7 @@ since: v7.0.0
This rule is aimed at eliminating unused properties.
::: warning Note
-This rule cannot be checked for use in other components (e.g. `mixins`, Property access via `$refs`) and use in places where the scope cannot be determined.
+This rule cannot check for use of properties by other components (e.g. `mixins`, property access via `$refs`) and use in places where the scope cannot be determined. Some access to properties might be implied, for example accessing data or computed via a variable such as `this[varName]`. In this case, the default is to assume all properties, methods, etc. are 'used'. See the `unreferencedOptions` for a more strict interpretation of 'use' in these cases.
:::
@@ -25,9 +26,9 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
{{ count }}
```
@@ -41,9 +42,9 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
{{ cnt }}
```
@@ -56,7 +57,8 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
"vue/no-unused-properties": ["error", {
"groups": ["props"],
"deepData": false,
- "ignorePublicMembers": false
+ "ignorePublicMembers": false,
+ "unreferencedOptions": []
}]
}
```
@@ -69,6 +71,7 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
- `"setup"`
- `deepData` (`boolean`) If `true`, the object of the property defined in `data` will be searched deeply. Default is `false`. Include `"data"` in `groups` to use this option.
- `ignorePublicMembers` (`boolean`) If `true`, members marked with a [JSDoc `/** @public */` tag](https://jsdoc.app/tags-public.html) will be ignored. Default is `false`.
+- `unreferencedOptions` (`string[]`) Array of access methods that should be interpreted as leaving properties unreferenced. Currently, two such methods are available: `unknownMemberAsUnreferenced`, and `returnAsUnreferenced`. See examples below.
### `"groups": ["props", "data"]`
@@ -77,16 +80,16 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
```vue
```
@@ -97,16 +100,16 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
```vue
```
@@ -148,18 +151,18 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
{{ reversedMessage }}
```
@@ -173,18 +176,18 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
{{ message }}
```
@@ -200,19 +203,80 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
+```
+
+
+
+### `{ "groups": ["computed"], "unreferencedOptions": ["unknownMemberAsUnreferenced"] }`
+
+
+
+```vue
+
+
+```
+
+
+
+### `{ "groups": ["computed"], "unreferencedOptions": ["returnAsUnreferenced"] }`
+
+
+
+```vue
+
+
```
diff --git a/docs/rules/no-unused-refs.md b/docs/rules/no-unused-refs.md
index 7abf43e28..06383d93e 100644
--- a/docs/rules/no-unused-refs.md
+++ b/docs/rules/no-unused-refs.md
@@ -5,6 +5,7 @@ title: vue/no-unused-refs
description: disallow unused refs
since: v7.9.0
---
+
# vue/no-unused-refs
> disallow unused refs
diff --git a/docs/rules/no-unused-vars.md b/docs/rules/no-unused-vars.md
index be0b1fcc1..eedfc7250 100644
--- a/docs/rules/no-unused-vars.md
+++ b/docs/rules/no-unused-vars.md
@@ -5,11 +5,12 @@ title: vue/no-unused-vars
description: disallow unused variable definitions of v-for directives or scope attributes
since: v3.14.0
---
+
# vue/no-unused-vars
> disallow unused variable definitions of v-for directives or scope attributes
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
## :book: Rule Details
diff --git a/docs/rules/no-use-computed-property-like-method.md b/docs/rules/no-use-computed-property-like-method.md
index 33e49d653..3dc4dbc54 100644
--- a/docs/rules/no-use-computed-property-like-method.md
+++ b/docs/rules/no-use-computed-property-like-method.md
@@ -5,10 +5,13 @@ title: vue/no-use-computed-property-like-method
description: disallow use computed property like method
since: v7.15.0
---
+
# vue/no-use-computed-property-like-method
> disallow use computed property like method
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+
## :book: Rule Details
This rule disallows to use computed property like method.
@@ -320,7 +323,7 @@ export default {
this.computedReturnPropsFunction
this.computedReturnPropsFunction()
/* ✗ BAD */
- /* Nope. everything is GOOD!! */
+ /* Nope. everything is good!! */
}
}
}
diff --git a/docs/rules/no-use-v-else-with-v-for.md b/docs/rules/no-use-v-else-with-v-for.md
new file mode 100644
index 000000000..203775ad8
--- /dev/null
+++ b/docs/rules/no-use-v-else-with-v-for.md
@@ -0,0 +1,58 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-use-v-else-with-v-for
+description: disallow using `v-else-if`/`v-else` on the same element as `v-for`
+since: v9.16.0
+---
+
+# vue/no-use-v-else-with-v-for
+
+> disallow using `v-else-if`/`v-else` on the same element as `v-for`
+
+## :book: Rule Details
+
+This rule reports elements that have both `v-else-if`/`v-else` and `v-for` directives. That is valid in Vue (`v-else-if`/`v-else` will take precedence), but is confusing to read.
+
+
+
+```vue
+
+
+
foo
+
+
{{ x }}
+
+
+
{{ x }}
+
+
+
+
foo
+
{{ x }}
+
{{ x }}
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :mute: When Not To Use It
+
+If you don't find using `v-else-if`/`v-else` together with `v-for` confusing to read, you can safely disable this rule.
+
+## :couple: Related Rules
+
+- [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.16.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-use-v-else-with-v-for.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-use-v-else-with-v-for.js)
diff --git a/docs/rules/no-use-v-if-with-v-for.md b/docs/rules/no-use-v-if-with-v-for.md
index 78b11aa4c..faf1c62b2 100644
--- a/docs/rules/no-use-v-if-with-v-for.md
+++ b/docs/rules/no-use-v-if-with-v-for.md
@@ -2,22 +2,24 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-use-v-if-with-v-for
-description: disallow use v-if on the same element as v-for
+description: disallow using `v-if` on the same element as `v-for`
since: v4.6.0
---
+
# vue/no-use-v-if-with-v-for
-> disallow use v-if on the same element as v-for
+> disallow using `v-if` on the same element as `v-for`
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
This rule is aimed at preventing the use of `v-for` directives together with `v-if` directives on the same element.
There are two common cases where this can be tempting:
- * To filter items in a list (e.g. `v-for="user in users" v-if="user.isActive"`). In these cases, replace `users` with a new computed property that returns your filtered list (e.g. `activeUsers`).
- * To avoid rendering a list if it should be hidden (e.g. `v-for="user in users" v-if="shouldShowUsers"`). In these cases, move the `v-if` to a container element (e.g. `ul`, `ol`).
+
+- To filter items in a list (e.g. `v-for="user in users" v-if="user.isActive"`). In these cases, replace `users` with a new computed property that returns your filtered list (e.g. `activeUsers`).
+- To avoid rendering a list if it should be hidden (e.g. `v-for="user in users" v-if="shouldShowUsers"`). In these cases, move the `v-if` to a container element (e.g. `ul`, `ol`).
@@ -87,11 +89,15 @@ There are two common cases where this can be tempting:
+## :couple: Related Rules
+
+- [vue/no-use-v-else-with-v-for](./no-use-v-else-with-v-for.md)
+
## :books: Further Reading
-- [Style guide - Avoid v-if with v-for](https://v3.vuejs.org/style-guide/#avoid-v-if-with-v-for-essential)
-- [Guide - Conditional Rendering / v-if with v-for](https://v3.vuejs.org/guide/conditional.html#v-if-with-v-for)
-- [Guide - List Rendering / v-for with v-if](https://v3.vuejs.org/guide/list.html#v-for-with-v-if)
+- [Style guide - Avoid v-if with v-for](https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for)
+- [Guide - Conditional Rendering / v-if with v-for](https://vuejs.org/guide/essentials/conditional.html#v-if-with-v-for)
+- [Guide - List Rendering / v-for with v-if](https://vuejs.org/guide/essentials/list.html#v-for-with-v-if)
## :rocket: Version
diff --git a/docs/rules/no-useless-concat.md b/docs/rules/no-useless-concat.md
index 0deda262e..bd74f475d 100644
--- a/docs/rules/no-useless-concat.md
+++ b/docs/rules/no-useless-concat.md
@@ -2,12 +2,13 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-useless-concat
-description: disallow unnecessary concatenation of literals or template literals
+description: Disallow unnecessary concatenation of literals or template literals in ``
since: v7.0.0
---
+
# vue/no-useless-concat
-> disallow unnecessary concatenation of literals or template literals
+> Disallow unnecessary concatenation of literals or template literals in ``
This rule is the same rule as core [no-useless-concat] rule but it applies to the expressions in ``.
@@ -26,4 +27,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-useless-concat.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-useless-concat.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-useless-concat)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/no-useless-concat)
diff --git a/docs/rules/no-useless-mustaches.md b/docs/rules/no-useless-mustaches.md
index 205371a86..aed50cb1c 100644
--- a/docs/rules/no-useless-mustaches.md
+++ b/docs/rules/no-useless-mustaches.md
@@ -5,11 +5,12 @@ title: vue/no-useless-mustaches
description: disallow unnecessary mustache interpolations
since: v7.0.0
---
+
# vue/no-useless-mustaches
> disallow unnecessary mustache interpolations
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
diff --git a/docs/rules/no-useless-template-attributes.md b/docs/rules/no-useless-template-attributes.md
index b309897f7..d8eb20a98 100644
--- a/docs/rules/no-useless-template-attributes.md
+++ b/docs/rules/no-useless-template-attributes.md
@@ -5,11 +5,12 @@ title: vue/no-useless-template-attributes
description: disallow useless attribute on ``
since: v7.19.0
---
+
# vue/no-useless-template-attributes
> disallow useless attribute on ``
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
diff --git a/docs/rules/no-useless-v-bind.md b/docs/rules/no-useless-v-bind.md
index 303f15871..4c5b11ba5 100644
--- a/docs/rules/no-useless-v-bind.md
+++ b/docs/rules/no-useless-v-bind.md
@@ -5,11 +5,12 @@ title: vue/no-useless-v-bind
description: disallow unnecessary `v-bind` directives
since: v7.0.0
---
+
# vue/no-useless-v-bind
> disallow unnecessary `v-bind` directives
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -21,12 +22,12 @@ The `v-bind` with a string literal value can be changed to a static attribute de
```vue
-
-
+
+
-
-
+
+
```
@@ -53,10 +54,10 @@ The `v-bind` with a string literal value can be changed to a static attribute de
```vue
-
+
-
+
```
@@ -69,7 +70,7 @@ The `v-bind` with a string literal value can be changed to a static attribute de
```vue
-
+
```
diff --git a/docs/rules/no-v-for-template-key-on-child.md b/docs/rules/no-v-for-template-key-on-child.md
index f43aa307f..4e6ddd0c3 100644
--- a/docs/rules/no-v-for-template-key-on-child.md
+++ b/docs/rules/no-v-for-template-key-on-child.md
@@ -5,23 +5,24 @@ title: vue/no-v-for-template-key-on-child
description: disallow key of `` placed on child elements
since: v7.0.0
---
+
# vue/no-v-for-template-key-on-child
> disallow key of `` placed on child elements
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
This rule reports the key of the `` placed on the child elements.
-In Vue.js 3.x, with the support for fragments, the `` key can be placed on the `` tag.
+In Vue.js 3.x, with the support for fragments, the `` key can be placed on the `` tag.
-See [Migration Guide - `key` attribute > With ``](https://v3.vuejs.org/guide/migration/key-attribute.html#with-template-v-for) for more details.
+See [Migration Guide - `key` attribute > With ``](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for) for more details.
::: warning Note
-Do not use with the [vue/no-v-for-template-key] rule for Vue.js 2.x.
-This rule conflicts with the [vue/no-v-for-template-key] rule.
+This rule is targeted at Vue.js 3.x.
+If you are using Vue.js 2.x, enable the [vue/no-v-for-template-key] rule instead. Don't enable both rules together; they are conflicting.
:::
@@ -54,7 +55,7 @@ Nothing.
## :books: Further Reading
-- [Migration Guide - `key` attribute > With ``](https://v3.vuejs.org/guide/migration/key-attribute.html#with-template-v-for)
+- [Migration Guide - `key` attribute > With ``](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for)
## :rocket: Version
diff --git a/docs/rules/no-v-for-template-key.md b/docs/rules/no-v-for-template-key.md
index 128a5c07d..5dbe15471 100644
--- a/docs/rules/no-v-for-template-key.md
+++ b/docs/rules/no-v-for-template-key.md
@@ -5,11 +5,13 @@ title: vue/no-v-for-template-key
description: disallow `key` attribute on ``
since: v7.0.0
---
+
# vue/no-v-for-template-key
> disallow `key` attribute on ``
-- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+- :no_entry_sign: This rule was **deprecated**.
+- :gear: This rule is included in all of `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -18,8 +20,8 @@ This rule reports the `` elements which have `key` attribute.
In Vue.js 2.x, disallows `key` attribute on `` elements.
::: warning Note
-Do not use with the [vue/no-v-for-template-key-on-child] rule for Vue.js 3.x.
-This rule conflicts with the [vue/no-v-for-template-key-on-child] rule.
+This rule is targeted at Vue.js 2.x.
+If you are using Vue.js 3.x, enable the [vue/no-v-for-template-key-on-child] rule instead. Don't enable both rules together; they are conflicting.
:::
@@ -58,8 +60,8 @@ Nothing.
## :books: Further Reading
-- [API - Special Attributes - key](https://v3.vuejs.org/api/special-attributes.html#key)
-- [API (for v2) - Special Attributes - key](https://vuejs.org/v2/api/#key)
+- [API - Special Attributes - key](https://vuejs.org/api/built-in-special-attributes.html#key)
+- [API (for v2) - Special Attributes - key](https://v2.vuejs.org/v2/api/#key)
## :rocket: Version
diff --git a/docs/rules/no-v-html.md b/docs/rules/no-v-html.md
index cb390eca1..3098683e0 100644
--- a/docs/rules/no-v-html.md
+++ b/docs/rules/no-v-html.md
@@ -5,11 +5,12 @@ title: vue/no-v-html
description: disallow use of v-html to prevent XSS attack
since: v4.7.0
---
+
# vue/no-v-html
> disallow use of v-html to prevent XSS attack
-- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -37,10 +38,6 @@ Nothing.
If you are certain the content passed to `v-html` is sanitized HTML you can disable this rule.
-## :books: Further Reading
-
-- [XSS in Vue.js](https://blog.sqreen.io/xss-in-vue-js/)
-
## :rocket: Version
This rule was introduced in eslint-plugin-vue v4.7.0
diff --git a/docs/rules/no-v-model-argument.md b/docs/rules/no-v-model-argument.md
index 4571893c6..6856c5e6c 100644
--- a/docs/rules/no-v-model-argument.md
+++ b/docs/rules/no-v-model-argument.md
@@ -5,11 +5,13 @@ title: vue/no-v-model-argument
description: disallow adding an argument to `v-model` used in custom component
since: v7.0.0
---
+
# vue/no-v-model-argument
> disallow adding an argument to `v-model` used in custom component
-- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+- :no_entry_sign: This rule was **deprecated**.
+- :gear: This rule is included in all of `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether `v-model` used on custom component do not have an argument.
@@ -26,7 +28,6 @@ This rule reports `v-model` directives in the following cases:
-
@@ -34,7 +35,6 @@ This rule reports `v-model` directives in the following cases:
-
## :wrench: Options
Nothing.
diff --git a/docs/rules/no-v-text-v-html-on-component.md b/docs/rules/no-v-text-v-html-on-component.md
new file mode 100644
index 000000000..9bbbcf7ab
--- /dev/null
+++ b/docs/rules/no-v-text-v-html-on-component.md
@@ -0,0 +1,94 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-v-text-v-html-on-component
+description: disallow v-text / v-html on component
+since: v8.4.0
+---
+
+# vue/no-v-text-v-html-on-component
+
+> disallow v-text / v-html on component
+
+- :gear: This rule is included in all of `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-recommended"`, `*.configs["flat/vue2-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+
+## :book: Rule Details
+
+This rule disallows the use of v-text / v-html on component.
+
+If you use v-text / v-html on a component, it will overwrite the component's content and may break the component.
+
+
+
+```vue
+
+
+
+
+
+
+ {{ content }}
+
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/no-v-text-v-html-on-component": ["error", {
+ "allow": ["router-link", "nuxt-link"],
+ "ignoreElementNamespaces": false
+ }]
+}
+```
+
+- `allow` (`string[]`) ... Specify a list of custom components for which the rule should not apply.
+- `ignoreElementNamespaces` (`boolean`) ... If `true`, always treat SVG and MathML tag names as HTML elements, even if they are not used inside a SVG/MathML root element. Default is `false`.
+
+### `{ "allow": ["router-link", "nuxt-link"] }`
+
+
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+
+
+### `{ "ignoreElementNamespaces": true }`
+
+
+
+```vue
+
+
+
+
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.4.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-v-text-v-html-on-component.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-v-text-v-html-on-component.js)
diff --git a/docs/rules/no-v-text.md b/docs/rules/no-v-text.md
index 84cece3a4..89bedbfbb 100644
--- a/docs/rules/no-v-text.md
+++ b/docs/rules/no-v-text.md
@@ -5,6 +5,7 @@ title: vue/no-v-text
description: disallow use of v-text
since: v7.17.0
---
+
# vue/no-v-text
> disallow use of v-text
diff --git a/docs/rules/no-watch-after-await.md b/docs/rules/no-watch-after-await.md
index a5c2a5e9f..32b5dab0c 100644
--- a/docs/rules/no-watch-after-await.md
+++ b/docs/rules/no-watch-after-await.md
@@ -5,11 +5,12 @@ title: vue/no-watch-after-await
description: disallow asynchronously registered `watch`
since: v7.0.0
---
+
# vue/no-watch-after-await
> disallow asynchronously registered `watch`
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
## :book: Rule Details
diff --git a/docs/rules/object-curly-newline.md b/docs/rules/object-curly-newline.md
index 52e3d6ae4..8f636a1da 100644
--- a/docs/rules/object-curly-newline.md
+++ b/docs/rules/object-curly-newline.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/object-curly-newline
-description: enforce consistent line breaks after opening and before closing braces
+description: Enforce consistent line breaks after opening and before closing braces in ``
since: v7.0.0
---
+
# vue/object-curly-newline
-> enforce consistent line breaks after opening and before closing braces
+> Enforce consistent line breaks after opening and before closing braces in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/object-curly-newline] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [object-curly-newline] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/object-curly-newline]
- [object-curly-newline]
+[@stylistic/object-curly-newline]: https://eslint.style/rules/default/object-curly-newline
[object-curly-newline]: https://eslint.org/docs/rules/object-curly-newline
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/object-curly-newline.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/object-curly-newline.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/object-curly-newline)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/object-curly-newline)
diff --git a/docs/rules/object-curly-spacing.md b/docs/rules/object-curly-spacing.md
index f63716c24..e7a3fa0a6 100644
--- a/docs/rules/object-curly-spacing.md
+++ b/docs/rules/object-curly-spacing.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/object-curly-spacing
-description: enforce consistent spacing inside braces
+description: Enforce consistent spacing inside braces in ``
since: v5.2.0
---
+
# vue/object-curly-spacing
-> enforce consistent spacing inside braces
+> Enforce consistent spacing inside braces in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/object-curly-spacing] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [object-curly-spacing] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/object-curly-spacing]
- [object-curly-spacing]
+[@stylistic/object-curly-spacing]: https://eslint.style/rules/default/object-curly-spacing
[object-curly-spacing]: https://eslint.org/docs/rules/object-curly-spacing
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v5.2.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/object-curly-spacing.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/object-curly-spacing.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/object-curly-spacing)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/object-curly-spacing)
diff --git a/docs/rules/object-property-newline.md b/docs/rules/object-property-newline.md
index 62ec559f6..6b6434ced 100644
--- a/docs/rules/object-property-newline.md
+++ b/docs/rules/object-property-newline.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/object-property-newline
-description: enforce placing object properties on separate lines
+description: Enforce placing object properties on separate lines in ``
since: v7.0.0
---
+
# vue/object-property-newline
-> enforce placing object properties on separate lines
+> Enforce placing object properties on separate lines in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/object-property-newline] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [object-property-newline] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/object-property-newline]
- [object-property-newline]
+[@stylistic/object-property-newline]: https://eslint.style/rules/default/object-property-newline
[object-property-newline]: https://eslint.org/docs/rules/object-property-newline
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/object-property-newline.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/object-property-newline.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/object-property-newline)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/object-property-newline)
diff --git a/docs/rules/object-shorthand.md b/docs/rules/object-shorthand.md
new file mode 100644
index 000000000..8b4365b19
--- /dev/null
+++ b/docs/rules/object-shorthand.md
@@ -0,0 +1,32 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/object-shorthand
+description: Require or disallow method and property shorthand syntax for object literals in ``
+since: v8.4.0
+---
+
+# vue/object-shorthand
+
+> Require or disallow method and property shorthand syntax for object literals in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as core [object-shorthand] rule but it applies to the expressions in ``.
+
+## :books: Further Reading
+
+- [object-shorthand]
+
+[object-shorthand]: https://eslint.org/docs/rules/object-shorthand
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.4.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/object-shorthand.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/object-shorthand.js)
+
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/object-shorthand)
diff --git a/docs/rules/one-component-per-file.md b/docs/rules/one-component-per-file.md
index 006a13b74..8d31a0875 100644
--- a/docs/rules/one-component-per-file.md
+++ b/docs/rules/one-component-per-file.md
@@ -5,11 +5,12 @@ title: vue/one-component-per-file
description: enforce that each component should be in its own file
since: v7.0.0
---
+
# vue/one-component-per-file
> enforce that each component should be in its own file
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -48,9 +49,13 @@ export default {
Nothing.
+## :couple: Related Rules
+
+- [vue/require-default-export](./require-default-export.md)
+
## :books: Further Reading
-- [Style guide - Component files](https://v3.vuejs.org/style-guide/#component-files-strongly-recommended)
+- [Style guide - Component files](https://vuejs.org/style-guide/rules-strongly-recommended.html#component-files)
## :rocket: Version
diff --git a/docs/rules/operator-linebreak.md b/docs/rules/operator-linebreak.md
index a982b999a..b994e7622 100644
--- a/docs/rules/operator-linebreak.md
+++ b/docs/rules/operator-linebreak.md
@@ -2,21 +2,29 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/operator-linebreak
-description: enforce consistent linebreak style for operators
+description: Enforce consistent linebreak style for operators in ``
since: v7.0.0
---
+
# vue/operator-linebreak
-> enforce consistent linebreak style for operators
+> Enforce consistent linebreak style for operators in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/operator-linebreak] rule but it applies to the expressions in ``.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
-This rule is the same rule as core [operator-linebreak] rule but it applies to the expressions in ``.
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
## :books: Further Reading
+- [@stylistic/operator-linebreak]
- [operator-linebreak]
+[@stylistic/operator-linebreak]: https://eslint.style/rules/default/operator-linebreak
[operator-linebreak]: https://eslint.org/docs/rules/operator-linebreak
## :rocket: Version
@@ -28,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/operator-linebreak.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/operator-linebreak.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/operator-linebreak)
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/js/operator-linebreak)
diff --git a/docs/rules/order-in-components.md b/docs/rules/order-in-components.md
index bcbbe77d0..29d3eda1c 100644
--- a/docs/rules/order-in-components.md
+++ b/docs/rules/order-in-components.md
@@ -5,17 +5,19 @@ title: vue/order-in-components
description: enforce order of properties in components
since: v3.2.0
---
+
# vue/order-in-components
> enforce order of properties in components
-- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
## :book: Rule Details
This rule makes sure you keep declared order of properties in components.
-Recommended order of properties can be [found here](https://v3.vuejs.org/style-guide/#component-instance-options-order-recommended).
+Recommended order of properties can be [found here](https://vuejs.org/style-guide/rules-recommended.html#component-instance-options-order).
@@ -27,7 +29,7 @@ export default {
props: {
propA: Number
},
- data () {
+ data() {
return {
msg: 'Welcome to Your Vue.js App'
}
@@ -45,7 +47,7 @@ export default {
/* ✗ BAD */
export default {
name: 'app',
- data () {
+ data() {
return {
msg: 'Welcome to Your Vue.js App'
}
@@ -86,6 +88,8 @@ export default {
"model",
["props", "propsData"],
"emits",
+ "slots",
+ "expose",
"setup",
"asyncData",
"data",
@@ -105,16 +109,15 @@ export default {
- `order` (`(string | string[])[]`) ... The order of properties. Elements are the property names or one of the following groups:
- - `LIFECYCLE_HOOKS`: [Vue Lifecycle Events](https://v3.vuejs.org/guide/instance.html#lifecycle-diagram), in the order they are called
+ - `LIFECYCLE_HOOKS`: [Vue Lifecycle Events](https://vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram), in the order they are called
- `ROUTER_GUARDS`: [Vue Router Navigation Guards](https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards), in the order they are called
If an element is an array of strings, it means any of those can be placed there unordered. Default is above.
-
## :books: Further Reading
-- [Style guide - Component/instance options order](https://v3.vuejs.org/style-guide/#component-instance-options-order-recommended)
-- [Style guide (for v2) - Component/instance options order](https://vuejs.org/v2/style-guide/#Component-instance-options-order-recommended)
+- [Style guide - Component/instance options order](https://vuejs.org/style-guide/rules-recommended.html#component-instance-options-order)
+- [Style guide (for v2) - Component/instance options order](https://v2.vuejs.org/v2/style-guide/#Component-instance-options-order-recommended)
## :rocket: Version
diff --git a/docs/rules/padding-line-between-blocks.md b/docs/rules/padding-line-between-blocks.md
index 7e1e058e1..4f3ee0290 100644
--- a/docs/rules/padding-line-between-blocks.md
+++ b/docs/rules/padding-line-between-blocks.md
@@ -5,11 +5,12 @@ title: vue/padding-line-between-blocks
description: require or disallow padding lines between blocks
since: v6.2.0
---
+
# vue/padding-line-between-blocks
> require or disallow padding lines between blocks
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -134,7 +135,6 @@ export default {}
[padding-line-between-statements]: https://eslint.org/docs/rules/padding-line-between-statements
[lines-between-class-members]: https://eslint.org/docs/rules/lines-between-class-members
-
## :rocket: Version
This rule was introduced in eslint-plugin-vue v6.2.0
diff --git a/docs/rules/padding-line-between-tags.md b/docs/rules/padding-line-between-tags.md
new file mode 100644
index 000000000..e5b5c4b41
--- /dev/null
+++ b/docs/rules/padding-line-between-tags.md
@@ -0,0 +1,170 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/padding-line-between-tags
+description: require or disallow newlines between sibling tags in template
+since: v9.5.0
+---
+
+# vue/padding-line-between-tags
+
+> require or disallow newlines between sibling tags in template
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule requires or disallows newlines between sibling HTML tags.
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/padding-line-between-tags": ["error", [
+ { "blankLine": "always", "prev": "*", "next": "*" }
+ ]]
+}
+```
+
+This rule requires blank lines between each sibling HTML tag by default.
+
+A configuration is an object which has 3 properties; `blankLine`, `prev` and `next`. For example, `{ blankLine: "always", prev: "br", next: "div" }` means “one or more blank lines are required between a `br` tag and a `div` tag.” You can supply any number of configurations. If a tag pair matches multiple configurations, the last matched configuration will be used.
+
+- `blankLine` is one of the following:
+ - `always` requires one or more blank lines.
+ - `never` disallows blank lines.
+ - `consistent` requires or disallows a blank line based on the first sibling element.
+- `prev` any tag name without brackets.
+- `next` any tag name without brackets.
+
+### Disallow blank lines between all tags
+
+`{ blankLine: 'never', prev: '*', next: '*' }`
+
+
+
+```vue
+
+
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/padding-line-between-tags.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/padding-line-between-tags.js)
diff --git a/docs/rules/padding-lines-in-component-definition.md b/docs/rules/padding-lines-in-component-definition.md
new file mode 100644
index 000000000..eef694e14
--- /dev/null
+++ b/docs/rules/padding-lines-in-component-definition.md
@@ -0,0 +1,168 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/padding-lines-in-component-definition
+description: require or disallow padding lines in component definition
+since: v9.9.0
+---
+
+# vue/padding-lines-in-component-definition
+
+> require or disallow padding lines in component definition
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule requires or disallows blank lines in the component definition. Properly blank lines help developers improve code readability and code style flexibility.
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/padding-lines-in-component-definition": ["error", {
+ "betweenOptions": "always" | "never",
+
+ "withinOption": {
+ "props": {
+ "betweenItems": "always" | "never" | "ignore",
+ "withinEach": "always" | "never" | "ignore",
+ } | "always" | "never" | "ignore", // shortcut to set both
+
+ "data": {
+ "betweenItems": "always" | "never" | "ignore",
+ "withinEach": "always" | "never" | "ignore",
+ } | "always" | "never" | "ignore" // shortcut to set both
+
+ // ... all options
+ } | "always" | "never" | "ignore",
+
+ "groupSingleLineProperties": true | false
+ }]
+}
+```
+
+- `betweenOptions` ... Setting padding lines between options. default `always`
+- `withinOption` ... Setting padding lines within option
+ - `emits` ... Setting padding between lines between `emits` and `defineEmits`. default `always`
+ - `props` ... Setting padding between lines between `props` and `defineProps`. default `always`
+ - ...
+- `groupSingleLineProperties` ... Setting groupings of multiple consecutive single-line properties (e.g. `name`, `inheritAttrs`), default `true`
+
+### Group single-line properties
+
+
+
+```vue
+
+```
+
+
+
+### With custom options
+
+
+
+```vue
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.9.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/padding-lines-in-component-definition.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/padding-lines-in-component-definition.js)
diff --git a/docs/rules/prefer-define-options.md b/docs/rules/prefer-define-options.md
new file mode 100644
index 000000000..110c1426a
--- /dev/null
+++ b/docs/rules/prefer-define-options.md
@@ -0,0 +1,61 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/prefer-define-options
+description: enforce use of `defineOptions` instead of default export
+since: v9.13.0
+---
+
+# vue/prefer-define-options
+
+> enforce use of `defineOptions` instead of default export
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule aims to enforce use of `defineOptions` instead of default export in `
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [API - defineOptions()](https://vuejs.org/api/sfc-script-setup.html#defineoptions)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.13.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-define-options.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-define-options.js)
diff --git a/docs/rules/prefer-import-from-vue.md b/docs/rules/prefer-import-from-vue.md
new file mode 100644
index 000000000..2da6700f9
--- /dev/null
+++ b/docs/rules/prefer-import-from-vue.md
@@ -0,0 +1,58 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/prefer-import-from-vue
+description: enforce import from 'vue' instead of import from '@vue/*'
+since: v8.5.0
+---
+
+# vue/prefer-import-from-vue
+
+> enforce import from 'vue' instead of import from '@vue/\*'
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule aims to use imports from `'vue'` instead of imports from `'@vue/*'`.
+
+Imports from the following modules are almost always wrong. You should import from `vue` instead.
+
+- `@vue/runtime-dom`
+- `@vue/runtime-core`
+- `@vue/reactivity`
+- `@vue/shared`
+
+
+
+```js
+/* ✓ GOOD */
+import { createApp, ref, Component } from 'vue'
+```
+
+
+
+
+
+```js
+/* ✗ BAD */
+import { createApp } from '@vue/runtime-dom'
+import { Component } from '@vue/runtime-core'
+import { ref } from '@vue/reactivity'
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-import-from-vue.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-import-from-vue.js)
diff --git a/docs/rules/prefer-prop-type-boolean-first.md b/docs/rules/prefer-prop-type-boolean-first.md
new file mode 100644
index 000000000..8282e32ba
--- /dev/null
+++ b/docs/rules/prefer-prop-type-boolean-first.md
@@ -0,0 +1,65 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/prefer-prop-type-boolean-first
+description: enforce `Boolean` comes first in component prop types
+since: v8.6.0
+---
+
+# vue/prefer-prop-type-boolean-first
+
+> enforce `Boolean` comes first in component prop types
+
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+When declaring types of a property in component, we can use array style to accept multiple types.
+
+When using components in template,
+we can use shorthand-style property if its value is `true`.
+
+However, if a property allows `Boolean` or `String` and we use it with shorthand form in somewhere else,
+different types order can introduce different behaviors:
+If `Boolean` comes first, it will be `true`; if `String` comes first, it will be `""` (empty string).
+
+See [this demo](https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCBNeUNvbXBvbmVudCBmcm9tICcuL015Q29tcG9uZW50LnZ1ZSdcbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIFNob3J0aGFuZCBmb3JtOlxuICA8TXlDb21wb25lbnQgYm9vbCBib29sLW9yLXN0cmluZyBzdHJpbmctb3ItYm9vbCAvPlxuICBcbiAgTG9uZ2hhbmQgZm9ybTpcbiAgPE15Q29tcG9uZW50IDpib29sPVwidHJ1ZVwiIDpib29sLW9yLXN0cmluZz1cInRydWVcIiA6c3RyaW5nLW9yLWJvb2w9XCJ0cnVlXCIgLz5cbjwvdGVtcGxhdGU+IiwiaW1wb3J0LW1hcC5qc29uIjoie1xuICBcImltcG9ydHNcIjoge1xuICAgIFwidnVlXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3Z1ZS5ydW50aW1lLmVzbS1icm93c2VyLmpzXCJcbiAgfVxufSIsIk15Q29tcG9uZW50LnZ1ZSI6IjxzY3JpcHQ+XG5leHBvcnQgZGVmYXVsdCB7XG4gIHByb3BzOiB7XG4gICAgYm9vbDogQm9vbGVhbixcbiAgICBib29sT3JTdHJpbmc6IFtCb29sZWFuLCBTdHJpbmddLFxuICAgIHN0cmluZ09yQm9vbDogW1N0cmluZywgQm9vbGVhbl0sXG4gIH1cbn1cbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIDxwcmU+XG5ib29sOiB7e2Jvb2x9fSAoe3sgdHlwZW9mIGJvb2wgfX0pXG5ib29sT3JTdHJpbmc6IHt7Ym9vbE9yU3RyaW5nfX0gKHt7IHR5cGVvZiBib29sT3JTdHJpbmcgfX0pXG5zdHJpbmdPckJvb2w6IHt7c3RyaW5nT3JCb29sfX0gKHt7IHR5cGVvZiBzdHJpbmdPckJvb2wgfX0pXG4gIDwvcHJlPlxuPC90ZW1wbGF0ZT4ifQ==).
+
+
+
+```vue
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.6.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-prop-type-boolean-first.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-prop-type-boolean-first.js)
diff --git a/docs/rules/prefer-separate-static-class.md b/docs/rules/prefer-separate-static-class.md
new file mode 100644
index 000000000..610e3f2b8
--- /dev/null
+++ b/docs/rules/prefer-separate-static-class.md
@@ -0,0 +1,48 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/prefer-separate-static-class
+description: require static class names in template to be in a separate `class` attribute
+since: v8.2.0
+---
+
+# vue/prefer-separate-static-class
+
+> require static class names in template to be in a separate `class` attribute
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule reports static class names in dynamic class attributes.
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.2.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-separate-static-class.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-separate-static-class.js)
diff --git a/docs/rules/prefer-template.md b/docs/rules/prefer-template.md
index 09a41769e..ae4d3fc29 100644
--- a/docs/rules/prefer-template.md
+++ b/docs/rules/prefer-template.md
@@ -2,14 +2,15 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/prefer-template
-description: require template literals instead of string concatenation
+description: Require template literals instead of string concatenation in ``
since: v7.0.0
---
+
# vue/prefer-template
-> require template literals instead of string concatenation
+> Require template literals instead of string concatenation in ``
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
This rule is the same rule as core [prefer-template] rule but it applies to the expressions in ``.
@@ -28,4 +29,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-template.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-template.js)
-Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/prefer-template)
+Taken with ❤️ [from ESLint core](https://eslint.org/docs/latest/rules/prefer-template)
diff --git a/docs/rules/prefer-true-attribute-shorthand.md b/docs/rules/prefer-true-attribute-shorthand.md
new file mode 100644
index 000000000..700921ce1
--- /dev/null
+++ b/docs/rules/prefer-true-attribute-shorthand.md
@@ -0,0 +1,146 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/prefer-true-attribute-shorthand
+description: require shorthand form attribute when `v-bind` value is `true`
+since: v8.5.0
+---
+
+# vue/prefer-true-attribute-shorthand
+
+> require shorthand form attribute when `v-bind` value is `true`
+
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+`v-bind` attribute with `true` value usually can be written in shorthand form. This can reduce verbosity.
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+```
+
+
+
+::: warning Warning
+The shorthand form is not always equivalent! If a prop accepts multiple types, but Boolean is not the first one, a shorthand prop won't pass `true`.
+:::
+
+```vue
+
+```
+
+**Shorthand form:**
+
+```vue
+
+```
+
+```txt
+bool: true (boolean)
+boolOrString: true (boolean)
+stringOrBool: "" (string)
+```
+
+**Longhand form:**
+
+```vue
+
+```
+
+```txt
+bool: true (boolean)
+boolOrString: true (boolean)
+stringOrBool: true (boolean)
+```
+
+Those two calls will introduce different render result. See [this demo](https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCBNeUNvbXBvbmVudCBmcm9tICcuL015Q29tcG9uZW50LnZ1ZSdcbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIFNob3J0aGFuZCBmb3JtOlxuICA8TXlDb21wb25lbnQgYm9vbCBib29sLW9yLXN0cmluZyBzdHJpbmctb3ItYm9vbCAvPlxuICBcbiAgTG9uZ2hhbmQgZm9ybTpcbiAgPE15Q29tcG9uZW50IDpib29sPVwidHJ1ZVwiIDpib29sLW9yLXN0cmluZz1cInRydWVcIiA6c3RyaW5nLW9yLWJvb2w9XCJ0cnVlXCIgLz5cbjwvdGVtcGxhdGU+IiwiaW1wb3J0LW1hcC5qc29uIjoie1xuICBcImltcG9ydHNcIjoge1xuICAgIFwidnVlXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3Z1ZS5ydW50aW1lLmVzbS1icm93c2VyLmpzXCJcbiAgfVxufSIsIk15Q29tcG9uZW50LnZ1ZSI6IjxzY3JpcHQ+XG5leHBvcnQgZGVmYXVsdCB7XG4gIHByb3BzOiB7XG4gICAgYm9vbDogQm9vbGVhbixcbiAgICBib29sT3JTdHJpbmc6IFtCb29sZWFuLCBTdHJpbmddLFxuICAgIHN0cmluZ09yQm9vbDogW1N0cmluZywgQm9vbGVhbl0sXG4gIH1cbn1cbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIDxwcmU+XG5ib29sOiB7e2Jvb2x9fSAoe3sgdHlwZW9mIGJvb2wgfX0pXG5ib29sT3JTdHJpbmc6IHt7Ym9vbE9yU3RyaW5nfX0gKHt7IHR5cGVvZiBib29sT3JTdHJpbmcgfX0pXG5zdHJpbmdPckJvb2w6IHt7c3RyaW5nT3JCb29sfX0gKHt7IHR5cGVvZiBzdHJpbmdPckJvb2wgfX0pXG4gIDwvcHJlPlxuPC90ZW1wbGF0ZT4ifQ==).
+
+## :wrench: Options
+
+Default options is `"always"`.
+
+```json
+{
+ "vue/prefer-true-attribute-shorthand": ["error",
+ "always" | "never",
+ {
+ except: []
+ }
+ ]
+}
+```
+
+- `"always"` (default) ... requires shorthand form.
+- `"never"` ... requires long form.
+- `except` (`string[]`) ... specifies a list of attribute names that should be treated differently.
+
+### `"never"`
+
+
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+
+
+### `"never", { 'except': ['value', '/^foo-/'] }`
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/no-boolean-default](./no-boolean-default.md)
+- [vue/prefer-prop-type-boolean-first](./prefer-prop-type-boolean-first.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-true-attribute-shorthand.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-true-attribute-shorthand.js)
diff --git a/docs/rules/prefer-use-template-ref.md b/docs/rules/prefer-use-template-ref.md
new file mode 100644
index 000000000..1b1b40385
--- /dev/null
+++ b/docs/rules/prefer-use-template-ref.md
@@ -0,0 +1,78 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/prefer-use-template-ref
+description: require using `useTemplateRef` instead of `ref`/`shallowRef` for template refs
+since: v9.31.0
+---
+
+# vue/prefer-use-template-ref
+
+> require using `useTemplateRef` instead of `ref`/`shallowRef` for template refs
+
+## :book: Rule Details
+
+Vue 3.5 introduced a new way of obtaining template refs via
+the [`useTemplateRef()`](https://vuejs.org/guide/essentials/template-refs.html#accessing-the-refs) API.
+
+This rule enforces using the new `useTemplateRef` function instead of `ref`/`shallowRef` for template refs.
+
+
+
+```vue
+
+
+
+
+
+
+
+```
+
+
+
+This rule skips `ref` template function refs as these should be used to allow custom implementation of storing `ref`. If you prefer
+`useTemplateRef`, you have to change the value of the template `ref` to a string.
+
+
+
+```vue
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.31.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-use-template-ref.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-use-template-ref.js)
diff --git a/docs/rules/prop-name-casing.md b/docs/rules/prop-name-casing.md
index 81f19518f..32681643c 100644
--- a/docs/rules/prop-name-casing.md
+++ b/docs/rules/prop-name-casing.md
@@ -5,11 +5,12 @@ title: vue/prop-name-casing
description: enforce specific casing for the Prop name in Vue components
since: v4.3.0
---
+
# vue/prop-name-casing
> enforce specific casing for the Prop name in Vue components
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -38,12 +39,18 @@ export default {
```json
{
- "vue/prop-name-casing": ["error", "camelCase" | "snake_case"]
+ "vue/prop-name-casing": ["error",
+ "camelCase" | "snake_case",
+ {
+ "ignoreProps": []
+ }
+ ]
}
```
- `"camelCase"` (default) ... Enforce property names in `props` to camel case.
- `"snake_case"` ... Enforce property names in `props` to snake case.
+- `ignoreProps` (`string[]`) ... An array of prop names (or patterns) that don't need to follow the specified casing.
### `"snake_case"`
@@ -66,15 +73,40 @@ export default {
-## :books: Further Reading
+### `"ignoreProps": ["foo-bar", "/^_[a-z]+/u"]`
+
+
+
+```vue
+
+```
-- [Style guide - Prop name casing](https://v3.vuejs.org/style-guide/#prop-name-casing-strongly-recommended)
+
## :couple: Related Rules
- [vue/attribute-hyphenation](./attribute-hyphenation.md)
- [vue/custom-event-name-casing](./custom-event-name-casing.md)
+## :books: Further Reading
+
+- [Style guide - Prop name casing](https://vuejs.org/style-guide/rules-strongly-recommended.html#prop-name-casing)
+
## :rocket: Version
This rule was introduced in eslint-plugin-vue v4.3.0
diff --git a/docs/rules/quote-props.md b/docs/rules/quote-props.md
new file mode 100644
index 000000000..f5fe59248
--- /dev/null
+++ b/docs/rules/quote-props.md
@@ -0,0 +1,39 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/quote-props
+description: Require quotes around object literal, type literal, interfaces and enums property names in ``
+since: v8.4.0
+---
+
+# vue/quote-props
+
+> Require quotes around object literal, type literal, interfaces and enums property names in ``
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
+
+This rule is the same rule as [@stylistic/quote-props] rule but it applies to the expressions in ``.
+
+This rule extends the rule that [@stylistic/eslint-plugin] has, but if [@stylistic/eslint-plugin] is not installed, this rule extracts and extends the same rule from ESLint core.
+However, if neither is found, the rule cannot be used.
+
+[@stylistic/eslint-plugin]: https://eslint.style/packages/default
+
+## :books: Further Reading
+
+- [@stylistic/quote-props]
+- [quote-props]
+
+[@stylistic/quote-props]: https://eslint.style/rules/default/quote-props
+[quote-props]: https://eslint.org/docs/rules/quote-props
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v8.4.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/quote-props.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/quote-props.js)
+
+Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/quote-props)
diff --git a/docs/rules/require-component-is.md b/docs/rules/require-component-is.md
index 767e9a930..40280a975 100644
--- a/docs/rules/require-component-is.md
+++ b/docs/rules/require-component-is.md
@@ -5,28 +5,28 @@ title: vue/require-component-is
description: require `v-bind:is` of `` elements
since: v3.0.0
---
+
# vue/require-component-is
> require `v-bind:is` of `` elements
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
This rule reports the `` elements which do not have `v-bind:is` attributes.
-
```vue
-
-
+
+
-
-
+
+
```
@@ -36,14 +36,13 @@ This rule reports the `` elements which do not have `v-bind:is` attri
You can use the same mount point and dynamically switch between multiple components using the reserved `` element and dynamically bind to its `is` attribute.
:::
-
## :wrench: Options
Nothing.
## :books: Further Reading
-- [Guide - Components Basics / Dynamic Components](https://v3.vuejs.org/guide/component-basics.html#dynamic-components)
+- [Guide - Components Basics / Dynamic Components](https://vuejs.org/guide/essentials/component-basics.html#dynamic-components)
## :rocket: Version
diff --git a/docs/rules/require-default-export.md b/docs/rules/require-default-export.md
new file mode 100644
index 000000000..9266eee89
--- /dev/null
+++ b/docs/rules/require-default-export.md
@@ -0,0 +1,60 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/require-default-export
+description: require components to be the default export
+since: v9.28.0
+---
+
+# vue/require-default-export
+
+> require components to be the default export
+
+## :book: Rule Details
+
+This rule reports when a Vue component does not have a default export, if the component is not defined as `
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related Rules
+
+- [vue/one-component-per-file](./one-component-per-file.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.28.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-default-export.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-default-export.js)
diff --git a/docs/rules/require-default-prop.md b/docs/rules/require-default-prop.md
index 2f43c88a1..c96edc24e 100644
--- a/docs/rules/require-default-prop.md
+++ b/docs/rules/require-default-prop.md
@@ -5,11 +5,12 @@ title: vue/require-default-prop
description: require default value for props
since: v3.13.0
---
+
# vue/require-default-prop
> require default value for props
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -61,9 +62,13 @@ export default {
Nothing.
+## :couple: Related Rules
+
+- [vue/no-boolean-default](./no-boolean-default.md)
+
## :books: Further Reading
-- [Style guide - Prop definitions](https://v3.vuejs.org/style-guide/#prop-definitions-essential)
+- [Style guide - Prop definitions](https://vuejs.org/style-guide/rules-essential.html#use-detailed-prop-definitions)
## :rocket: Version
diff --git a/docs/rules/require-direct-export.md b/docs/rules/require-direct-export.md
index 52661b7c1..4f2e37650 100644
--- a/docs/rules/require-direct-export.md
+++ b/docs/rules/require-direct-export.md
@@ -5,6 +5,7 @@ title: vue/require-direct-export
description: require the component to be directly exported
since: v5.2.0
---
+
# vue/require-direct-export
> require the component to be directly exported
@@ -70,7 +71,7 @@ export default ComponentA
```vue
```
@@ -83,7 +84,7 @@ export default props => h('div', props.msg)
```vue
```
diff --git a/docs/rules/require-emit-validator.md b/docs/rules/require-emit-validator.md
index 97a975185..34b674bf5 100644
--- a/docs/rules/require-emit-validator.md
+++ b/docs/rules/require-emit-validator.md
@@ -5,6 +5,7 @@ title: vue/require-emit-validator
description: require type definitions in emits
since: v7.10.0
---
+
# vue/require-emit-validator
> require type definitions in emits
@@ -53,7 +54,7 @@ Nothing.
## :books: Further Reading
-- [API Reference](https://v3.vuejs.org/api/options-data.html#emits)
+- [API Reference](https://vuejs.org/api/options-state.html#emits)
## :rocket: Version
diff --git a/docs/rules/require-explicit-emits.md b/docs/rules/require-explicit-emits.md
index 6d681c551..5c5d3f8a9 100644
--- a/docs/rules/require-explicit-emits.md
+++ b/docs/rules/require-explicit-emits.md
@@ -5,11 +5,12 @@ title: vue/require-explicit-emits
description: require `emits` option with name triggered by `$emit()`
since: v7.0.0
---
+
# vue/require-explicit-emits
> require `emits` option with name triggered by `$emit()`
-- :gear: This rule is included in `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
## :book: Rule Details
@@ -17,16 +18,16 @@ since: v7.0.0
This rule reports event triggers not declared with the `emits` option. (The `emits` option is a new in Vue.js 3.0.0+)
Explicit `emits` declaration serves as self-documenting code. This can be useful for other developers to instantly understand what events the component is supposed to emit.
-Also, with attribute fallthrough changes in Vue.js 3.0.0+, `v-on` listeners on components will fallthrough as native listeners by default. Declare it as a component-only event in `emits` to avoid unnecessary registration of native listeners.
+Also, with attribute fallthrough changes in Vue.js 3.0.0+, `v-on` listeners on components will fallthrough as native listeners by default. Declare it as a component-only event in `emits` to avoid unnecessary registration of native listeners.
```vue
-
+
-
+
+```
+
+
+
+
+
+```vue
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.21.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-explicit-slots.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-explicit-slots.js)
diff --git a/docs/rules/require-expose.md b/docs/rules/require-expose.md
index d6b27c7fc..98762a8c9 100644
--- a/docs/rules/require-expose.md
+++ b/docs/rules/require-expose.md
@@ -5,6 +5,7 @@ title: vue/require-expose
description: require declare public properties using `expose`
since: v7.14.0
---
+
# vue/require-expose
> require declare public properties using `expose`
diff --git a/docs/rules/require-macro-variable-name.md b/docs/rules/require-macro-variable-name.md
new file mode 100644
index 000000000..af6db1733
--- /dev/null
+++ b/docs/rules/require-macro-variable-name.md
@@ -0,0 +1,89 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/require-macro-variable-name
+description: require a certain macro variable name
+since: v9.15.0
+---
+
+# vue/require-macro-variable-name
+
+> require a certain macro variable name
+
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+This rule reports macro variables not corresponding to the specified name.
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/require-macro-variable-name": ["error", {
+ "defineProps": "props",
+ "defineEmits": "emit",
+ "defineSlots": "slots",
+ "useSlots": "slots",
+ "useAttrs": "attrs"
+ }]
+}
+```
+
+- `defineProps` - The name of the macro variable for `defineProps`. default: `props`
+- `defineEmits` - The name of the macro variable for `defineEmits`. default: `emit`
+- `defineSlots` - The name of the macro variable for `defineSlots`. default: `slots`
+- `useSlots` - The name of the macro variable for `useSlots`. default: `slots`
+- `useAttrs` - The name of the macro variable for `useAttrs`. default: `attrs`
+
+### With custom macro variable names
+
+
+
+```vue
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.15.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-macro-variable-name.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-macro-variable-name.js)
diff --git a/docs/rules/require-name-property.md b/docs/rules/require-name-property.md
index d741e07c3..6d9974fc4 100644
--- a/docs/rules/require-name-property.md
+++ b/docs/rules/require-name-property.md
@@ -5,10 +5,13 @@ title: vue/require-name-property
description: require a name property in Vue components
since: v6.1.0
---
+
# vue/require-name-property
> require a name property in Vue components
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
## :book: Rule Details
This rule requires a `name` property to be set on components.
@@ -31,8 +34,7 @@ export default {
```vue
```
diff --git a/docs/rules/require-prop-comment.md b/docs/rules/require-prop-comment.md
new file mode 100644
index 000000000..25b78c346
--- /dev/null
+++ b/docs/rules/require-prop-comment.md
@@ -0,0 +1,148 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/require-prop-comment
+description: require props to have a comment
+since: v9.8.0
+---
+
+# vue/require-prop-comment
+
+> require props to have a comment
+
+## :book: Rule Details
+
+This rule enforces that every prop has a comment that documents it.
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/require-prop-comment": ["error", {
+ "type": "JSDoc"
+ }]
+}
+```
+
+- `type` ... Type of comment. Default is `"JSDoc"`
+ - `"JSDoc"` ... Only JSDoc comment are allowed.
+ - `"line"` ... Only line comment are allowed.
+ - `"block"` ... Only block comment are allowed.
+ - `"any"` ... All comment types are allowed.
+
+### `"type": "block"`
+
+
+
+```vue
+
+```
+
+
+
+### `"type": "line"`
+
+
+
+```vue
+
+```
+
+
+
+### `"type": "any"`
+
+
+
+```vue
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.8.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-prop-comment.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-prop-comment.js)
diff --git a/docs/rules/require-prop-type-constructor.md b/docs/rules/require-prop-type-constructor.md
index 97bce84f3..e872fc5d4 100644
--- a/docs/rules/require-prop-type-constructor.md
+++ b/docs/rules/require-prop-type-constructor.md
@@ -5,12 +5,13 @@ title: vue/require-prop-type-constructor
description: require prop type to be a constructor
since: v5.0.0
---
+
# vue/require-prop-type-constructor
> require prop type to be a constructor
-- :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"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
## :book: Rule Details
@@ -38,7 +39,7 @@ export default {
anotherProp: [Number, String],
myFieldWithBadType: {
type: Object,
- default: function() {
+ default: function () {
return {}
},
},
@@ -47,16 +48,16 @@ export default {
default: 1,
},
/* ✗ BAD */
- myProp: "Number",
- anotherProp: ["Number", "String"],
+ myProp: 'Number',
+ anotherProp: ['Number', 'String'],
myFieldWithBadType: {
- type: "Object",
- default: function() {
+ type: 'Object',
+ default: function () {
return {}
},
},
myOtherFieldWithBadType: {
- type: "Number",
+ type: 'Number',
default: 1,
},
}
@@ -72,7 +73,7 @@ Nothing.
## :books: Further Reading
-- [Guide - Prop Validation](https://v3.vuejs.org/guide/component-props.html#prop-validation)
+- [Guide - Prop Validation](https://vuejs.org/guide/components/props.html#prop-validation)
## :rocket: Version
diff --git a/docs/rules/require-prop-types.md b/docs/rules/require-prop-types.md
index ae3ffe47c..20e46453f 100644
--- a/docs/rules/require-prop-types.md
+++ b/docs/rules/require-prop-types.md
@@ -5,11 +5,12 @@ title: vue/require-prop-types
description: require type definitions in props
since: v3.9.0
---
+
# vue/require-prop-types
> require type definitions in props
-- :gear: This rule is included in all of `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -65,7 +66,7 @@ Nothing.
## :books: Further Reading
-- [Style guide - Prop definitions](https://v3.vuejs.org/style-guide/#prop-definitions-essential)
+- [Style guide - Prop definitions](https://vuejs.org/style-guide/rules-essential.html#use-detailed-prop-definitions)
## :rocket: Version
diff --git a/docs/rules/require-render-return.md b/docs/rules/require-render-return.md
index 0e5c89434..88cbf1efb 100644
--- a/docs/rules/require-render-return.md
+++ b/docs/rules/require-render-return.md
@@ -5,11 +5,12 @@ title: vue/require-render-return
description: enforce render function to always return value
since: v3.10.0
---
+
# vue/require-render-return
> enforce render function to always return value
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -21,7 +22,7 @@ This rule aims to enforce render function to always return value
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :mute: When Not To Use It
+
+When you're not using TypeScript in the project.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.16.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-typed-object-prop.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-typed-object-prop.js)
diff --git a/docs/rules/require-typed-ref.md b/docs/rules/require-typed-ref.md
new file mode 100644
index 000000000..85b58b213
--- /dev/null
+++ b/docs/rules/require-typed-ref.md
@@ -0,0 +1,51 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/require-typed-ref
+description: require `ref` and `shallowRef` functions to be strongly typed
+since: v9.15.0
+---
+
+# vue/require-typed-ref
+
+> require `ref` and `shallowRef` functions to be strongly typed
+
+## :book: Rule Details
+
+This rule disallows calling `ref()` or `shallowRef()` functions without generic type parameter or an argument when using TypeScript.
+
+With TypeScript it is easy to prevent usage of `any` by using [`noImplicitAny`](https://www.typescriptlang.org/tsconfig#noImplicitAny). Unfortunately this rule is easily bypassed with Vue `ref()` function. Calling `ref()` function without a generic parameter or an initial value leads to ref having `Ref` type.
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.15.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-typed-ref.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-typed-ref.js)
diff --git a/docs/rules/require-v-for-key.md b/docs/rules/require-v-for-key.md
index 1e2b8a5f0..6811b9419 100644
--- a/docs/rules/require-v-for-key.md
+++ b/docs/rules/require-v-for-key.md
@@ -5,11 +5,12 @@ title: vue/require-v-for-key
description: require `v-bind:key` with `v-for` directives
since: v3.0.0
---
+
# vue/require-v-for-key
> require `v-bind:key` with `v-for` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -20,12 +21,9 @@ This rule reports the elements which have `v-for` and do not have `v-bind:key` w
```vue
-
+
-
+
```
@@ -43,13 +41,15 @@ Nothing.
## :couple: Related Rules
- [vue/valid-v-for]
+- [vue/v-if-else-key]
[vue/valid-v-for]: ./valid-v-for.md
+[vue/v-if-else-key]: ./v-if-else-key.md
## :books: Further Reading
-- [Style guide - Keyed v-for](https://v3.vuejs.org/style-guide/#keyed-v-for-essential)
-- [Guide (for v2) - v-for with a Component](https://vuejs.org/v2/guide/list.html#v-for-with-a-Component)
+- [Style guide - Keyed v-for](https://vuejs.org/style-guide/rules-essential.html#use-keyed-v-for)
+- [Guide (for v2) - v-for with a Component](https://v2.vuejs.org/v2/guide/list.html#v-for-with-a-Component)
## :rocket: Version
diff --git a/docs/rules/require-valid-default-prop.md b/docs/rules/require-valid-default-prop.md
index c53af81e1..bea185798 100644
--- a/docs/rules/require-valid-default-prop.md
+++ b/docs/rules/require-valid-default-prop.md
@@ -5,11 +5,12 @@ title: vue/require-valid-default-prop
description: enforce props default values to be valid
since: v3.13.0
---
+
# vue/require-valid-default-prop
> enforce props default values to be valid
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -78,7 +79,7 @@ Nothing.
## :books: Further Reading
-- [Guide - Prop Validation](https://v3.vuejs.org/guide/component-props.html#prop-validation)
+- [Guide - Prop Validation](https://vuejs.org/guide/components/props.html#prop-validation)
## :rocket: Version
diff --git a/docs/rules/restricted-component-names.md b/docs/rules/restricted-component-names.md
new file mode 100644
index 000000000..1d707baf3
--- /dev/null
+++ b/docs/rules/restricted-component-names.md
@@ -0,0 +1,69 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/restricted-component-names
+description: enforce using only specific component names
+since: v9.32.0
+---
+
+# vue/restricted-component-names
+
+> enforce using only specific component names
+
+## :book: Rule Details
+
+This rule enforces consistency in component names.
+
+
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+
+
+## :wrench: Options
+
+```json
+{
+ "vue/restricted-component-names": ["error", {
+ "allow": []
+ }]
+}
+```
+
+### `"allow: ['/^custom-/']"`
+
+
+
+```vue
+
+
+
+
+
+
+
+```
+
+
+
+## :couple: Related Rules
+
+- [vue/no-restricted-component-names](./no-restricted-component-names.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.32.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/restricted-component-names.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/restricted-component-names.js)
diff --git a/docs/rules/return-in-computed-property.md b/docs/rules/return-in-computed-property.md
index d18a1b274..5a1906060 100644
--- a/docs/rules/return-in-computed-property.md
+++ b/docs/rules/return-in-computed-property.md
@@ -5,11 +5,12 @@ title: vue/return-in-computed-property
description: enforce that a return statement is present in computed property
since: v3.7.0
---
+
# vue/return-in-computed-property
> enforce that a return statement is present in computed property
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
## :book: Rule Details
@@ -22,7 +23,7 @@ This rule enforces that a `return` statement is present in `computed` properties
export default {
computed: {
/* ✓ GOOD */
- foo () {
+ foo() {
if (this.bar) {
return this.baz
} else {
@@ -33,7 +34,7 @@ export default {
return false
},
/* ✗ BAD */
- baz () {
+ baz() {
if (this.baf) {
return this.baf
}
@@ -50,7 +51,7 @@ export default {
```vue
@@ -47,7 +54,11 @@ After turning on, `Foo` is being marked as used and `no-unused-vars` rule doesn'
## :mute: When Not To Use It
-If you are not using `
```
@@ -38,29 +39,33 @@ This rule reports `defineEmits` compiler macros in the following cases:
```vue
```
+
+
```vue
```
+
+
```vue
```
@@ -70,28 +75,32 @@ This rule reports `defineEmits` compiler macros in the following cases:
```vue
```
+
+
```vue
```
+
+
```vue
```
@@ -101,13 +110,13 @@ This rule reports `defineEmits` compiler macros in the following cases:
```vue
```
@@ -117,8 +126,8 @@ This rule reports `defineEmits` compiler macros in the following cases:
```vue
```
@@ -128,6 +137,12 @@ This rule reports `defineEmits` compiler macros in the following cases:
Nothing.
+## :couple: Related Rules
+
+- [vue/define-emits-declaration](./define-emits-declaration.md)
+- [vue/valid-define-options](./valid-define-options.md)
+- [vue/valid-define-props](./valid-define-props.md)
+
## :rocket: Version
This rule was introduced in eslint-plugin-vue v7.13.0
diff --git a/docs/rules/valid-define-options.md b/docs/rules/valid-define-options.md
new file mode 100644
index 000000000..a81d5ad96
--- /dev/null
+++ b/docs/rules/valid-define-options.md
@@ -0,0 +1,125 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/valid-define-options
+description: enforce valid `defineOptions` compiler macro
+since: v9.13.0
+---
+
+# vue/valid-define-options
+
+> enforce valid `defineOptions` compiler macro
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
+
+This rule checks whether `defineOptions` compiler macro is valid.
+
+## :book: Rule Details
+
+This rule reports `defineOptions` compiler macros in the following cases:
+
+- `defineOptions` is referencing locally declared variables.
+- `defineOptions` has been called multiple times.
+- Options are not defined in `defineOptions`.
+- `defineOptions` has type arguments.
+- `defineOptions` has `props`, `emits`, `expose` or `slots` options.
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related Rules
+
+- [vue/valid-define-emits](./valid-define-emits.md)
+- [vue/valid-define-props](./valid-define-props.md)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.13.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-define-options.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/valid-define-options.js)
diff --git a/docs/rules/valid-define-props.md b/docs/rules/valid-define-props.md
index b2c47cb54..1af111c9a 100644
--- a/docs/rules/valid-define-props.md
+++ b/docs/rules/valid-define-props.md
@@ -5,11 +5,12 @@ title: vue/valid-define-props
description: enforce valid `defineProps` compiler macro
since: v7.13.0
---
+
# vue/valid-define-props
> enforce valid `defineProps` compiler macro
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether `defineProps` compiler macro is valid.
@@ -17,7 +18,7 @@ This rule checks whether `defineProps` compiler macro is valid.
This rule reports `defineProps` compiler macros in the following cases:
-- `defineProps` are referencing locally declared variables.
+- `defineProps` is referencing locally declared variables.
- `defineProps` has both a literal type and an argument. e.g. `defineProps<{/*props*/}>({/*props*/})`
- `defineProps` has been called multiple times.
- Props are defined in both `defineProps` and `export default {}`.
@@ -27,8 +28,8 @@ This rule reports `defineProps` compiler macros in the following cases:
```vue
```
@@ -38,29 +39,33 @@ This rule reports `defineProps` compiler macros in the following cases:
```vue
```
+
+
```vue
```
+
+
```vue
```
@@ -70,28 +75,32 @@ This rule reports `defineProps` compiler macros in the following cases:
```vue
```
+
+
```vue
```
+
+
```vue
```
@@ -101,13 +110,13 @@ This rule reports `defineProps` compiler macros in the following cases:
```vue
```
@@ -117,8 +126,8 @@ This rule reports `defineProps` compiler macros in the following cases:
```vue
```
@@ -128,6 +137,12 @@ This rule reports `defineProps` compiler macros in the following cases:
Nothing.
+## :couple: Related Rules
+
+- [vue/define-props-declaration](./define-props-declaration.md)
+- [vue/valid-define-emits](./valid-define-emits.md)
+- [vue/valid-define-options](./valid-define-options.md)
+
## :rocket: Version
This rule was introduced in eslint-plugin-vue v7.13.0
diff --git a/docs/rules/valid-model-definition.md b/docs/rules/valid-model-definition.md
new file mode 100644
index 000000000..201fc1201
--- /dev/null
+++ b/docs/rules/valid-model-definition.md
@@ -0,0 +1,122 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/valid-model-definition
+description: require valid keys in model option
+since: v9.0.0
+---
+
+# vue/valid-model-definition
+
+> require valid keys in model option
+
+- :no_entry_sign: This rule was **deprecated**.
+- :gear: This rule is included in all of `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+
+## :book: Rule Details
+
+This rule is aimed at preventing invalid keys in model option.
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.0.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-model-definition.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/valid-model-definition.js)
diff --git a/docs/rules/valid-next-tick.md b/docs/rules/valid-next-tick.md
index 6c838553d..b2c95980b 100644
--- a/docs/rules/valid-next-tick.md
+++ b/docs/rules/valid-next-tick.md
@@ -5,12 +5,13 @@ title: vue/valid-next-tick
description: enforce valid `nextTick` function calls
since: v7.5.0
---
+
# vue/valid-next-tick
> enforce valid `nextTick` function calls
-- :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"`.
-- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule.
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
## :book: Rule Details
@@ -78,11 +79,11 @@ Nothing.
## :books: Further Reading
-- [`Vue.nextTick` API in Vue 2](https://vuejs.org/v2/api/#Vue-nextTick)
-- [`vm.$nextTick` API in Vue 2](https://vuejs.org/v2/api/#vm-nextTick)
-- [Global API Treeshaking](https://v3.vuejs.org/guide/migration/global-api-treeshaking.html)
-- [Global `nextTick` API in Vue 3](https://v3.vuejs.org/api/global-api.html#nexttick)
-- [Instance `$nextTick` API in Vue 3](https://v3.vuejs.org/api/instance-methods.html#nexttick)
+- [`Vue.nextTick` API in Vue 2](https://v2.vuejs.org/v2/api/#Vue-nextTick)
+- [`vm.$nextTick` API in Vue 2](https://v2.vuejs.org/v2/api/#vm-nextTick)
+- [Global API Treeshaking](https://v3-migration.vuejs.org/breaking-changes/global-api-treeshaking.html)
+- [Global `nextTick` API in Vue 3](https://vuejs.org/api/general.html#nexttick)
+- [Instance `$nextTick` API in Vue 3](https://vuejs.org/api/component-instance.html#nexttick)
## :rocket: Version
diff --git a/docs/rules/valid-template-root.md b/docs/rules/valid-template-root.md
index 14a56edc0..9dd89a287 100644
--- a/docs/rules/valid-template-root.md
+++ b/docs/rules/valid-template-root.md
@@ -5,11 +5,12 @@ title: vue/valid-template-root
description: enforce valid template root
since: v3.11.0
---
+
# vue/valid-template-root
> enforce valid template root
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every template root is valid.
diff --git a/docs/rules/valid-v-bind-sync.md b/docs/rules/valid-v-bind-sync.md
index 8b847659c..19129fb3d 100644
--- a/docs/rules/valid-v-bind-sync.md
+++ b/docs/rules/valid-v-bind-sync.md
@@ -5,11 +5,13 @@ title: vue/valid-v-bind-sync
description: enforce valid `.sync` modifier on `v-bind` directives
since: v7.0.0
---
+
# vue/valid-v-bind-sync
> enforce valid `.sync` modifier on `v-bind` directives
-- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+- :no_entry_sign: This rule was **deprecated**.
+- :gear: This rule is included in all of `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `.sync` modifier on `v-bind` directives is valid.
@@ -27,12 +29,12 @@ This rule reports `.sync` modifier on `v-bind` directives in the following cases
```vue
-
-
+
+
-
-
+
+
@@ -42,8 +44,8 @@ This rule reports `.sync` modifier on `v-bind` directives in the following cases
-
-
+
+
@@ -70,7 +72,7 @@ Nothing.
## :books: Further Reading
-- [Guide (for v2) - `.sync` Modifier](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier)
+- [Guide (for v2) - `.sync` Modifier](https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier)
## :rocket: Version
diff --git a/docs/rules/valid-v-bind.md b/docs/rules/valid-v-bind.md
index 32a07aa60..64badbb6a 100644
--- a/docs/rules/valid-v-bind.md
+++ b/docs/rules/valid-v-bind.md
@@ -5,11 +5,12 @@ title: vue/valid-v-bind
description: enforce valid `v-bind` directives
since: v3.11.0
---
+
# vue/valid-v-bind
> enforce valid `v-bind` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-bind` directive is valid.
@@ -27,15 +28,14 @@ This rule does not report `v-bind` directives which do not have their argument (
```vue
-
-
-
-
+
+
+
+
-
-
-
+
+
```
diff --git a/docs/rules/valid-v-cloak.md b/docs/rules/valid-v-cloak.md
index c992edd6e..57c60edcf 100644
--- a/docs/rules/valid-v-cloak.md
+++ b/docs/rules/valid-v-cloak.md
@@ -5,11 +5,12 @@ title: vue/valid-v-cloak
description: enforce valid `v-cloak` directives
since: v3.11.0
---
+
# vue/valid-v-cloak
> enforce valid `v-cloak` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-cloak` directive is valid.
@@ -26,12 +27,12 @@ This rule reports `v-cloak` directives in the following cases:
```vue
-
+
-
-
-
+
+
+
```
diff --git a/docs/rules/valid-v-else-if.md b/docs/rules/valid-v-else-if.md
index 5b7b9f63c..13959825b 100644
--- a/docs/rules/valid-v-else-if.md
+++ b/docs/rules/valid-v-else-if.md
@@ -5,11 +5,12 @@ title: vue/valid-v-else-if
description: enforce valid `v-else-if` directives
since: v3.11.0
---
+
# vue/valid-v-else-if
> enforce valid `v-else-if` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-else-if` directive is valid.
@@ -28,14 +29,14 @@ This rule reports `v-else-if` directives in the following cases:
```vue
-
-
+
+
-
-
-
-
+
+
+
+
```
diff --git a/docs/rules/valid-v-else.md b/docs/rules/valid-v-else.md
index b3424ad28..23df5787b 100644
--- a/docs/rules/valid-v-else.md
+++ b/docs/rules/valid-v-else.md
@@ -5,11 +5,12 @@ title: vue/valid-v-else
description: enforce valid `v-else` directives
since: v3.11.0
---
+
# vue/valid-v-else
> enforce valid `v-else` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-else` directive is valid.
@@ -28,14 +29,14 @@ This rule reports `v-else` directives in the following cases:
```vue
-
-
+
+
-
-
-
-
+
+
+
+
```
diff --git a/docs/rules/valid-v-for.md b/docs/rules/valid-v-for.md
index 843682626..f0e49c5e1 100644
--- a/docs/rules/valid-v-for.md
+++ b/docs/rules/valid-v-for.md
@@ -5,11 +5,12 @@ title: vue/valid-v-for
description: enforce valid `v-for` directives
since: v3.11.0
---
+
# vue/valid-v-for
> enforce valid `v-for` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-for` directive is valid.
diff --git a/docs/rules/valid-v-html.md b/docs/rules/valid-v-html.md
index 8c1932290..75e3c233d 100644
--- a/docs/rules/valid-v-html.md
+++ b/docs/rules/valid-v-html.md
@@ -5,11 +5,12 @@ title: vue/valid-v-html
description: enforce valid `v-html` directives
since: v3.11.0
---
+
# vue/valid-v-html
> enforce valid `v-html` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-html` directive is valid.
@@ -26,12 +27,12 @@ This rule reports `v-html` directives in the following cases:
```vue
-
+
-
-
-
+
+
+
```
diff --git a/docs/rules/valid-v-if.md b/docs/rules/valid-v-if.md
index f059aab30..8e45793f8 100644
--- a/docs/rules/valid-v-if.md
+++ b/docs/rules/valid-v-if.md
@@ -5,11 +5,12 @@ title: vue/valid-v-if
description: enforce valid `v-if` directives
since: v3.11.0
---
+
# vue/valid-v-if
> enforce valid `v-if` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-if` directive is valid.
@@ -27,14 +28,14 @@ This rule reports `v-if` directives in the following cases:
```vue
-
-
-
+
+
+
-
-
-
+
+
+
enforce valid `v-is` directives
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
This rule checks whether every `v-is` directive is valid.
@@ -59,7 +60,7 @@ Nothing.
## :books: Further Reading
-- [API - v-is](https://v3.vuejs.org/api/directives.html#v-is)
+- [API - v-is (Recent)](https://github.com/vuejs/docs/blob/8b4f11a4e94d01c7f1c91a60ceaa5b89d6b6de9f/src/api/built-in-directives.md#v-is-)
- [API - v-is (Old)](https://github.com/vuejs/docs-next/blob/008613756c3d781128d96b64a2d27f7598f8f548/src/api/directives.md#v-is)
## :rocket: Version
diff --git a/docs/rules/valid-v-memo.md b/docs/rules/valid-v-memo.md
index 620313698..c276eb8b4 100644
--- a/docs/rules/valid-v-memo.md
+++ b/docs/rules/valid-v-memo.md
@@ -5,11 +5,12 @@ title: vue/valid-v-memo
description: enforce valid `v-memo` directives
since: v7.16.0
---
+
# vue/valid-v-memo
> enforce valid `v-memo` directives
-- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/recommended"]`.
This rule checks whether every `v-memo` directive is valid.
@@ -28,13 +29,13 @@ This rule reports `v-memo` directives in the following cases:
```vue
-
+
-
-
-
-
+
+
+
+
@@ -59,7 +60,7 @@ Nothing.
## :books: Further Reading
-- [API - v-memo](https://v3.vuejs.org/api/directives.html#v-memo)
+- [API - v-memo](https://vuejs.org/api/built-in-directives.html#v-memo)
## :rocket: Version
diff --git a/docs/rules/valid-v-model.md b/docs/rules/valid-v-model.md
index 25c9ddb1f..0bc38d1b0 100644
--- a/docs/rules/valid-v-model.md
+++ b/docs/rules/valid-v-model.md
@@ -5,11 +5,12 @@ title: vue/valid-v-model
description: enforce valid `v-model` directives
since: v3.11.0
---
+
# vue/valid-v-model
> enforce valid `v-model` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-model` directive is valid.
@@ -31,27 +32,27 @@ This rule reports `v-model` directives in the following cases:
```vue
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
```
diff --git a/docs/rules/valid-v-on.md b/docs/rules/valid-v-on.md
index b3a157c2d..acfb630b7 100644
--- a/docs/rules/valid-v-on.md
+++ b/docs/rules/valid-v-on.md
@@ -5,11 +5,12 @@ title: vue/valid-v-on
description: enforce valid `v-on` directives
since: v3.11.0
---
+
# vue/valid-v-on
> enforce valid `v-on` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-on` directive is valid.
@@ -26,18 +27,18 @@ This rule reports `v-on` directives in the following cases:
```vue
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
```
@@ -67,8 +68,8 @@ This rule has an object option:
```vue
-
-
+
+
```
diff --git a/docs/rules/valid-v-once.md b/docs/rules/valid-v-once.md
index 4ab4ed8da..daa711b8d 100644
--- a/docs/rules/valid-v-once.md
+++ b/docs/rules/valid-v-once.md
@@ -5,11 +5,12 @@ title: vue/valid-v-once
description: enforce valid `v-once` directives
since: v3.11.0
---
+
# vue/valid-v-once
> enforce valid `v-once` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-once` directive is valid.
@@ -26,12 +27,12 @@ This rule reports `v-once` directives in the following cases:
```vue
-
+
-
-
-
+
+
+
```
diff --git a/docs/rules/valid-v-pre.md b/docs/rules/valid-v-pre.md
index 63095aab7..0464ed3d7 100644
--- a/docs/rules/valid-v-pre.md
+++ b/docs/rules/valid-v-pre.md
@@ -5,11 +5,12 @@ title: vue/valid-v-pre
description: enforce valid `v-pre` directives
since: v3.11.0
---
+
# vue/valid-v-pre
> enforce valid `v-pre` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-pre` directive is valid.
@@ -26,12 +27,12 @@ This rule reports `v-pre` directives in the following cases:
```vue
-
+
-
-
-
+
+
+
```
diff --git a/docs/rules/valid-v-show.md b/docs/rules/valid-v-show.md
index 710489946..5be8a50a5 100644
--- a/docs/rules/valid-v-show.md
+++ b/docs/rules/valid-v-show.md
@@ -5,11 +5,12 @@ title: vue/valid-v-show
description: enforce valid `v-show` directives
since: v3.11.0
---
+
# vue/valid-v-show
> enforce valid `v-show` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-show` directive is valid.
@@ -27,12 +28,12 @@ This rule reports `v-show` directives in the following cases:
```vue
-
+
-
-
-
+
+
+
```
diff --git a/docs/rules/valid-v-slot.md b/docs/rules/valid-v-slot.md
index 9c7c6621d..0c0c9e564 100644
--- a/docs/rules/valid-v-slot.md
+++ b/docs/rules/valid-v-slot.md
@@ -5,11 +5,12 @@ title: vue/valid-v-slot
description: enforce valid `v-slot` directives
since: v7.0.0
---
+
# vue/valid-v-slot
> enforce valid `v-slot` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-slot` directive is valid.
@@ -32,7 +33,7 @@ This rule reports `v-slot` directives in the following cases:
- {{data}}
+ {{ data }}
@@ -48,7 +49,7 @@ This rule reports `v-slot` directives in the following cases:
- {{data}}
+ {{ data }}
@@ -57,10 +58,10 @@ This rule reports `v-slot` directives in the following cases:
- {{data}}
+ {{ data }}
- {{data}}
+ {{ data }}
one
@@ -85,7 +86,7 @@ This rule reports `v-slot` directives in the following cases:
- {{data}}
+ {{ data }}
@@ -127,7 +128,7 @@ This rule does not check syntax errors in directives because it's checked by [vu
-
+
{{ data }}
diff --git a/docs/rules/valid-v-text.md b/docs/rules/valid-v-text.md
index c9d2a569e..093939d01 100644
--- a/docs/rules/valid-v-text.md
+++ b/docs/rules/valid-v-text.md
@@ -5,11 +5,12 @@ title: vue/valid-v-text
description: enforce valid `v-text` directives
since: v3.11.0
---
+
# vue/valid-v-text
> enforce valid `v-text` directives
-- :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"`.
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `*.configs["flat/essential"]`, `"plugin:vue/vue2-essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/vue2-strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/vue2-recommended"` and `*.configs["flat/vue2-recommended"]`.
This rule checks whether every `v-text` directive is valid.
@@ -26,12 +27,12 @@ This rule reports `v-text` directives in the following cases:
```vue
-
+
-
-
-
+
+
+
```
diff --git a/docs/user-guide/README.md b/docs/user-guide/index.md
similarity index 53%
rename from docs/user-guide/README.md
rename to docs/user-guide/index.md
index b083a6bd3..103471f61 100644
--- a/docs/user-guide/README.md
+++ b/docs/user-guide/index.md
@@ -2,12 +2,6 @@
## :cd: Installation
-Via `vue-cli` (**Recommended**):
-
-```bash
-vue add @vue/cli-plugin-eslint
-```
-
Via [npm](https://www.npmjs.com/):
```bash
@@ -17,21 +11,153 @@ npm install --save-dev eslint eslint-plugin-vue
Via [yarn](https://yarnpkg.com/):
```bash
-yarn add -D eslint eslint-plugin-vue
+yarn add -D eslint eslint-plugin-vue vue-eslint-parser globals
```
::: tip Requirements
-- ESLint v6.2.0 and above
-- Node.js v12.22.x, v14.17.x, v16.x and above
+- ESLint: `^8.57.0 || ^9.0.0`
+- Node.js: `^18.18.0 || ^20.9.0 || >=21.1.0`
:::
## :book: Usage
-### Configuration
+### Configuration (`eslint.config.js`)
+
+Use `eslint.config.js` file to configure rules. This is the default in ESLint v9, but can be used starting from ESLint v8.57.0. See also: .
+
+Example **eslint.config.js**:
+
+```js
+import pluginVue from 'eslint-plugin-vue'
+import globals from 'globals'
+
+export default [
+ // add more generic rulesets here, such as:
+ // js.configs.recommended,
+ ...pluginVue.configs['flat/recommended'],
+ // ...pluginVue.configs['flat/vue2-recommended'], // Use this if you are using Vue.js 2.x.
+ {
+ rules: {
+ // override/add rules settings here, such as:
+ // 'vue/no-unused-vars': 'error'
+ },
+ languageOptions: {
+ sourceType: 'module',
+ globals: {
+ ...globals.browser
+ }
+ }
+ }
+]
+```
+
+See [the rule list](../rules/index.md) to get the `configs` & `rules` that this plugin provides.
+
+#### Bundle Configurations (`eslint.config.js`)
+
+This plugin provides some predefined configs.
+You can use the following configs by adding them to `eslint.config.js`.
+(All flat configs in this plugin are provided as arrays, so spread syntax is required when combining them with other configs.)
+
+- `*.configs["flat/base"]` ... Settings and rules to enable correct ESLint parsing.
+- Configurations for using Vue.js 3.x:
+ - `*.configs["flat/essential"]` ... `base`, plus rules to prevent errors or unintended behavior.
+ - `*.configs["flat/strongly-recommended"]` ... Above, plus rules to considerably improve code readability and/or dev experience.
+ - `*.configs["flat/recommended"]` ... Above, plus rules to enforce subjective community defaults to ensure consistency.
+- Configurations for using Vue.js 2.x:
+ - `*.configs["flat/vue2-essential"]` ... `base`, plus rules to prevent errors or unintended behavior.
+ - `*.configs["flat/vue2-strongly-recommended"]` ... Above, plus rules to considerably improve code readability and/or dev experience.
+ - `*.configs["flat/vue2-recommended"]` ... Above, plus rules to enforce subjective community defaults to ensure consistency
+
+:::warning Reporting rules
+By default, all rules from **base** and **essential** categories report ESLint errors. Other rules - because they're not covering potential bugs in the application - report warnings. What does it mean? By default - nothing, but if you want - you can set up a threshold and break the build after a certain amount of warnings, instead of any. More information [here](https://eslint.org/docs/user-guide/command-line-interface#handling-warnings).
+:::
+
+#### Specifying Globals (`eslint.config.js`)
+
+Specify global objects depending on how you use Vue.js. More information on how to set globals can be found [here](https://eslint.org/docs/latest/use/configure/language-options#predefined-global-variables).
+
+If you're writing an app that will only render on the browser, use `globals.browser`.
+
+```js
+// ...
+import globals from 'globals'
+
+export default [
+ // ...
+ {
+ languageOptions: {
+ globals: {
+ ...globals.browser
+ }
+ }
+ }
+ // ...
+]
+```
+
+If you're writing an app that is rendered both server-side and on the browser, use `globals.shared-node-browser`.
+
+```js
+// ...
+import globals from 'globals'
+
+export default [
+ // ...
+ {
+ languageOptions: {
+ globals: {
+ ...globals['shared-node-browser']
+ }
+ }
+ }
+ // ...
+]
+```
+
+#### Example configuration with [typescript-eslint](https://typescript-eslint.io/) and [Prettier](https://prettier.io/)
+
+```bash
+npm install --save-dev eslint eslint-config-prettier eslint-plugin-vue globals typescript-eslint
+```
+
+```ts
+import eslint from '@eslint/js';
+import eslintConfigPrettier from 'eslint-config-prettier';
+import eslintPluginVue from 'eslint-plugin-vue';
+import globals from 'globals';
+import typescriptEslint from 'typescript-eslint';
+
+export default typescriptEslint.config(
+ { ignores: ['*.d.ts', '**/coverage', '**/dist'] },
+ {
+ extends: [
+ eslint.configs.recommended,
+ ...typescriptEslint.configs.recommended,
+ ...eslintPluginVue.configs['flat/recommended'],
+ ],
+ files: ['**/*.{ts,vue}'],
+ languageOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ globals: globals.browser,
+ parserOptions: {
+ parser: typescriptEslint.parser,
+ },
+ },
+ rules: {
+ // your rules
+ },
+ },
+ eslintConfigPrettier
+);
+```
+
+### Configuration (`.eslintrc`)
-Use `.eslintrc.*` file to configure rules. See also: [https://eslint.org/docs/user-guide/configuring](https://eslint.org/docs/user-guide/configuring).
+Use `.eslintrc.*` file to configure rules in ESLint < v9. See also: .
Example **.eslintrc.js**:
@@ -40,8 +166,8 @@ module.exports = {
extends: [
// add more generic rulesets here, such as:
// 'eslint:recommended',
- 'plugin:vue/vue3-recommended',
- // 'plugin:vue/recommended' // Use this if you are using Vue.js 2.x.
+ 'plugin:vue/recommended',
+ // 'plugin:vue/vue2-recommended' // Use this if you are using Vue.js 2.x.
],
rules: {
// override/add rules settings here, such as:
@@ -50,25 +176,25 @@ module.exports = {
}
```
-See [the rule list](../rules/README.md) to get the `extends` & `rules` that this plugin provides.
+See [the rule list](../rules/index.md) to get the `extends` & `rules` that this plugin provides.
-#### Bundle Configurations
+#### Bundle Configurations (`.eslintrc`)
This plugin provides some predefined configs.
You can use the following configs by adding them to `extends`.
- `"plugin:vue/base"` ... Settings and rules to enable correct ESLint parsing.
-- Configurations for using Vue.js 3.x.
- - `"plugin:vue/vue3-essential"` ... `base`, plus rules to prevent errors or unintended behavior.
- - `"plugin:vue/vue3-strongly-recommended"` ... Above, plus rules to considerably improve code readability and/or dev experience.
- - `"plugin:vue/vue3-recommended"` ... Above, plus rules to enforce subjective community defaults to ensure consistency.
-- Configurations for using Vue.js 2.x.
+- Configurations for using Vue.js 3.x:
- `"plugin:vue/essential"` ... `base`, plus rules to prevent errors or unintended behavior.
- `"plugin:vue/strongly-recommended"` ... Above, plus rules to considerably improve code readability and/or dev experience.
- - `"plugin:vue/recommended"` ... Above, plus rules to enforce subjective community defaults to ensure consistency
+ - `"plugin:vue/recommended"` ... Above, plus rules to enforce subjective community defaults to ensure consistency.
+- Configurations for using Vue.js 2.x:
+ - `"plugin:vue/vue2-essential"` ... `base`, plus rules to prevent errors or unintended behavior.
+ - `"plugin:vue/vue2-strongly-recommended"` ... Above, plus rules to considerably improve code readability and/or dev experience.
+ - `"plugin:vue/vue2-recommended"` ... Above, plus rules to enforce subjective community defaults to ensure consistency.
:::warning Reporting rules
-By default all rules from **base** and **essential** categories report ESLint errors. Other rules - because they're not covering potential bugs in the application - report warnings. What does it mean? By default - nothing, but if you want - you can set up a threshold and break the build after a certain amount of warnings, instead of any. More information [here](https://eslint.org/docs/user-guide/command-line-interface#handling-warnings).
+By default, all rules from **base** and **essential** categories report ESLint errors. Other rules - because they're not covering potential bugs in the application - report warnings. What does it mean? By default - nothing, but if you want - you can set up a threshold and break the build after a certain amount of warnings, instead of any. More information [here](https://eslint.org/docs/user-guide/command-line-interface#handling-warnings).
:::
:::warning Status of Vue.js 3.x supports
@@ -76,15 +202,38 @@ This plugin supports the basic syntax of Vue.js 3.2, `\n`
+ ),
+ fixer.removeRange(removeRange)
+ ]
+ }
+ }
+}
diff --git a/lib/rules/syntaxes/define-slots.js b/lib/rules/syntaxes/define-slots.js
new file mode 100644
index 000000000..450ec3e89
--- /dev/null
+++ b/lib/rules/syntaxes/define-slots.js
@@ -0,0 +1,22 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../../utils/index')
+
+module.exports = {
+ supported: '>=3.3.0',
+ /** @param {RuleContext} context @returns {RuleListener} */
+ createScriptVisitor(context) {
+ return utils.defineScriptSetupVisitor(context, {
+ onDefineSlotsEnter(node) {
+ context.report({
+ node,
+ messageId: 'forbiddenDefineSlots'
+ })
+ }
+ })
+ }
+}
diff --git a/lib/rules/syntaxes/script-setup.js b/lib/rules/syntaxes/script-setup.js
index ef395d5ed..7c0538b3d 100644
--- a/lib/rules/syntaxes/script-setup.js
+++ b/lib/rules/syntaxes/script-setup.js
@@ -7,7 +7,7 @@
const utils = require('../../utils')
module.exports = {
- supported: '>=3.0.0',
+ supported: '>=2.7.0',
/** @param {RuleContext} context @returns {TemplateListener} */
createScriptVisitor(context) {
const scriptSetup = utils.getScriptSetupElement(context)
diff --git a/lib/rules/syntaxes/slot-attribute.js b/lib/rules/syntaxes/slot-attribute.js
index 4cfe2099c..27087cb37 100644
--- a/lib/rules/syntaxes/slot-attribute.js
+++ b/lib/rules/syntaxes/slot-attribute.js
@@ -5,16 +5,21 @@
'use strict'
const canConvertToVSlot = require('./utils/can-convert-to-v-slot')
+const casing = require('../../utils/casing')
module.exports = {
deprecated: '2.6.0',
supported: '<3.0.0',
/** @param {RuleContext} context @returns {TemplateListener} */
createTemplateBodyVisitor(context) {
+ const options = context.options[0] || {}
+ /** @type {Set} */
+ const ignore = new Set(options.ignore)
+
const sourceCode = context.getSourceCode()
const tokenStore =
- context.parserServices.getTemplateBodyTokenStore &&
- context.parserServices.getTemplateBodyTokenStore()
+ sourceCode.parserServices.getTemplateBodyTokenStore &&
+ sourceCode.parserServices.getTemplateBodyTokenStore()
/**
* Checks whether the given node can convert to the `v-slot`.
@@ -51,10 +56,8 @@ module.exports = {
// parse error or empty expression
return false
}
- const slotName = sourceCode.getText(slotAttr.value.expression).trim()
- // If non-Latin characters are included it can not be converted.
- // It does not check the space only because `a>b?c:d` should be rejected.
- return !/[^a-z]/i.test(slotName)
+
+ return slotAttr.value.expression.type === 'Identifier'
}
/**
@@ -66,28 +69,50 @@ module.exports = {
* @returns {IterableIterator} fix data
*/
function* fixSlotToVSlot(fixer, slotAttr, slotName, vBind) {
- const element = slotAttr.parent
- const scopeAttr = element.attributes.find(
+ const startTag = slotAttr.parent
+ const scopeAttr = startTag.attributes.find(
(attr) =>
attr.directive === true &&
attr.key.name &&
(attr.key.name.name === 'slot-scope' ||
attr.key.name.name === 'scope')
)
- const nameArgument = slotName
- ? vBind
- ? `:[${slotName}]`
- : `:${slotName}`
- : ''
+ let nameArgument = ''
+ if (slotName) {
+ nameArgument = vBind ? `:[${slotName}]` : `:${slotName}`
+ }
const scopeValue =
scopeAttr && scopeAttr.value
? `=${sourceCode.getText(scopeAttr.value)}`
: ''
const replaceText = `v-slot${nameArgument}${scopeValue}`
- yield fixer.replaceText(slotAttr || scopeAttr, replaceText)
- if (slotAttr && scopeAttr) {
- yield fixer.remove(scopeAttr)
+
+ const element = startTag.parent
+ if (element.name === 'template') {
+ yield fixer.replaceText(slotAttr || scopeAttr, replaceText)
+ if (slotAttr && scopeAttr) {
+ yield fixer.remove(scopeAttr)
+ }
+ } else {
+ yield fixer.remove(slotAttr || scopeAttr)
+ if (slotAttr && scopeAttr) {
+ yield fixer.remove(scopeAttr)
+ }
+
+ const vFor = startTag.attributes.find(
+ (attr) => attr.directive && attr.key.name.name === 'for'
+ )
+ const vForText = vFor ? `${sourceCode.getText(vFor)} ` : ''
+ if (vFor) {
+ yield fixer.remove(vFor)
+ }
+
+ yield fixer.insertTextBefore(
+ element,
+ `\n`
+ )
+ yield fixer.insertTextAfter(element, `\n`)
}
}
/**
@@ -96,6 +121,15 @@ module.exports = {
* @returns {void}
*/
function reportSlot(slotAttr) {
+ const componentName = slotAttr.parent.parent.rawName
+ if (
+ ignore.has(componentName) ||
+ ignore.has(casing.pascalCase(componentName)) ||
+ ignore.has(casing.kebabCase(componentName))
+ ) {
+ return
+ }
+
context.report({
node: slotAttr.key,
messageId: 'forbiddenSlotAttribute',
diff --git a/lib/rules/syntaxes/slot-scope-attribute.js b/lib/rules/syntaxes/slot-scope-attribute.js
index 53a54d6de..5f8070498 100644
--- a/lib/rules/syntaxes/slot-scope-attribute.js
+++ b/lib/rules/syntaxes/slot-scope-attribute.js
@@ -18,8 +18,8 @@ module.exports = {
createTemplateBodyVisitor(context, { fixToUpgrade } = {}) {
const sourceCode = context.getSourceCode()
const tokenStore =
- context.parserServices.getTemplateBodyTokenStore &&
- context.parserServices.getTemplateBodyTokenStore()
+ sourceCode.parserServices.getTemplateBodyTokenStore &&
+ sourceCode.parserServices.getTemplateBodyTokenStore()
/**
* Checks whether the given node can convert to the `v-slot`.
@@ -62,16 +62,26 @@ module.exports = {
* Convert to `v-slot`.
* @param {RuleFixer} fixer fixer
* @param {VDirective} scopeAttr node of `slot-scope`
- * @returns {Fix} fix data
+ * @returns {Fix[]} fix data
*/
function fixSlotScopeToVSlot(fixer, scopeAttr) {
+ const element = scopeAttr.parent.parent
const scopeValue =
scopeAttr && scopeAttr.value
? `=${sourceCode.getText(scopeAttr.value)}`
: ''
const replaceText = `v-slot${scopeValue}`
- return fixer.replaceText(scopeAttr, replaceText)
+ if (element.name === 'template') {
+ return [fixer.replaceText(scopeAttr, replaceText)]
+ } else {
+ const tokenBefore = tokenStore.getTokenBefore(scopeAttr)
+ return [
+ fixer.removeRange([tokenBefore.range[1], scopeAttr.range[1]]),
+ fixer.insertTextBefore(element, `\n`),
+ fixer.insertTextAfter(element, `\n`)
+ ]
+ }
}
/**
* Reports `slot-scope` node
diff --git a/lib/rules/syntaxes/style-css-vars-injection.js b/lib/rules/syntaxes/style-css-vars-injection.js
index 1337cd0df..03608b5e1 100644
--- a/lib/rules/syntaxes/style-css-vars-injection.js
+++ b/lib/rules/syntaxes/style-css-vars-injection.js
@@ -7,7 +7,7 @@
const { getStyleVariablesContext } = require('../../utils/style-variables')
module.exports = {
- supported: '>=3.0.3',
+ supported: '>=3.0.3 || >=2.7.0 <3.0.0',
/** @param {RuleContext} context @returns {TemplateListener} */
createScriptVisitor(context) {
const styleVars = getStyleVariablesContext(context)
diff --git a/lib/rules/syntaxes/utils/can-convert-to-v-slot.js b/lib/rules/syntaxes/utils/can-convert-to-v-slot.js
index 3bdb7e1bc..e0e51053d 100644
--- a/lib/rules/syntaxes/utils/can-convert-to-v-slot.js
+++ b/lib/rules/syntaxes/utils/can-convert-to-v-slot.js
@@ -25,13 +25,11 @@ const utils = require('../../../utils')
* @param {ParserServices.TokenStore} tokenStore
*/
module.exports = function canConvertToVSlot(element, sourceCode, tokenStore) {
- if (element.name !== 'template') {
- return false
- }
const ownerElement = element.parent
if (
ownerElement.type === 'VDocumentFragment' ||
- !utils.isCustomComponent(ownerElement)
+ !utils.isCustomComponent(ownerElement) ||
+ ownerElement.name === 'component'
) {
return false
}
@@ -80,7 +78,7 @@ function getSlotVForVariableIfUsingIterationVars(slot, vFor) {
vFor && vFor.value && /** @type {VForExpression} */ (vFor.value.expression)
const variables =
expr && getUsingIterationVars(slot.value, slot.parent.parent)
- return expr && variables && variables.length ? { expr, variables } : null
+ return expr && variables && variables.length > 0 ? { expr, variables } : null
}
/**
@@ -204,14 +202,11 @@ function equalSlotVForVariables(a, b, tokenStore) {
return false
}
}
- 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)
+ )
/**
* Determines whether the two given nodes are considered to be equal.
diff --git a/lib/rules/syntaxes/v-bind-same-name-shorthand.js b/lib/rules/syntaxes/v-bind-same-name-shorthand.js
new file mode 100644
index 000000000..d9e7a388c
--- /dev/null
+++ b/lib/rules/syntaxes/v-bind-same-name-shorthand.js
@@ -0,0 +1,34 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../../utils')
+
+module.exports = {
+ supported: '>=3.4.0',
+ /** @param {RuleContext} context @returns {TemplateListener} */
+ createTemplateBodyVisitor(context) {
+ /**
+ * Verify the directive node
+ * @param {VDirective} node The directive node to check
+ * @returns {void}
+ */
+ function checkDirective(node) {
+ if (utils.isVBindSameNameShorthand(node)) {
+ context.report({
+ node,
+ messageId: 'forbiddenVBindSameNameShorthand',
+ // fix to use `:x="x"` (downgrade)
+ fix: (fixer) =>
+ fixer.insertTextAfter(node, `="${node.value.expression.name}"`)
+ })
+ }
+ }
+
+ return {
+ "VAttribute[directive=true][key.name.name='bind']": checkDirective
+ }
+ }
+}
diff --git a/lib/rules/syntaxes/v-model-custom-modifiers.js b/lib/rules/syntaxes/v-model-custom-modifiers.js
index d11116b2d..4b17b47d6 100644
--- a/lib/rules/syntaxes/v-model-custom-modifiers.js
+++ b/lib/rules/syntaxes/v-model-custom-modifiers.js
@@ -4,10 +4,6 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
const BUILTIN_MODIFIERS = new Set(['lazy', 'number', 'trim'])
module.exports = {
diff --git a/lib/rules/syntaxes/v-slot.js b/lib/rules/syntaxes/v-slot.js
index 9a9ba1fba..a6a4b4ebd 100644
--- a/lib/rules/syntaxes/v-slot.js
+++ b/lib/rules/syntaxes/v-slot.js
@@ -3,23 +3,22 @@
* See LICENSE file in root directory for full license.
*/
'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 = {
supported: '>=2.6.0',
/** @param {RuleContext} context @returns {TemplateListener} */
createTemplateBodyVisitor(context) {
const sourceCode = context.getSourceCode()
- /**
- * 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`.
* @param {RuleFixer} fixer fixer
@@ -28,7 +27,7 @@ module.exports = {
*/
function fixVSlotToSlot(fixer, vSlotAttr) {
const key = vSlotAttr.key
- if (key.modifiers.length) {
+ if (key.modifiers.length > 0) {
// unknown modifiers
return null
}
@@ -54,7 +53,7 @@ module.exports = {
if (scopedValueNode) {
attrs.push(`slot-scope=${sourceCode.getText(scopedValueNode)}`)
}
- if (!attrs.length) {
+ if (attrs.length === 0) {
attrs.push('slot') // useless
}
return fixer.replaceText(vSlotAttr, attrs.join(' '))
diff --git a/lib/rules/template-curly-spacing.js b/lib/rules/template-curly-spacing.js
index 549c790b6..e215108e9 100644
--- a/lib/rules/template-curly-spacing.js
+++ b/lib/rules/template-curly-spacing.js
@@ -3,10 +3,10 @@
*/
'use strict'
-const { wrapCoreRule } = require('../utils')
+const { wrapStylisticOrCoreRule } = require('../utils')
-// eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
-module.exports = wrapCoreRule('template-curly-spacing', {
+// eslint-disable-next-line internal/no-invalid-meta
+module.exports = wrapStylisticOrCoreRule('template-curly-spacing', {
skipDynamicArguments: true,
applyDocument: true
})
diff --git a/lib/rules/this-in-template.js b/lib/rules/this-in-template.js
index 671ac56a0..a664a8edf 100644
--- a/lib/rules/this-in-template.js
+++ b/lib/rules/this-in-template.js
@@ -4,23 +4,15 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
const RESERVED_NAMES = new Set(require('../utils/js-reserved.json'))
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow usage of `this` in template',
- categories: ['vue3-recommended', 'recommended'],
+ categories: ['vue3-recommended', 'vue2-recommended'],
url: 'https://eslint.vuejs.org/rules/this-in-template.html'
},
fixable: 'code',
@@ -28,7 +20,11 @@ module.exports = {
{
enum: ['always', 'never']
}
- ]
+ ],
+ messages: {
+ unexpected: "Unexpected usage of 'this'.",
+ expected: "Expected 'this'."
+ }
},
/**
@@ -38,7 +34,7 @@ module.exports = {
* @returns {Object} AST event handlers.
*/
create(context) {
- const options = context.options[0] !== 'always' ? 'never' : 'always'
+ const options = context.options[0] === 'always' ? 'always' : 'never'
/**
* @typedef {object} ScopeStack
* @property {ScopeStack | null} parent
@@ -54,7 +50,7 @@ module.exports = {
scopeStack = {
parent: scopeStack,
nodes: scopeStack
- ? scopeStack.nodes.slice() // make copy
+ ? [...scopeStack.nodes] // make copy
: []
}
if (node.variables) {
@@ -83,7 +79,7 @@ module.exports = {
!propertyName ||
scopeStack.nodes.some((el) => el.name === propertyName) ||
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']
) {
return
}
@@ -95,7 +91,7 @@ module.exports = {
// node.parent should be some code like `this.test`, `this?.['result']`
return fixer.replaceText(node.parent, propertyName)
},
- message: "Unexpected usage of 'this'."
+ messageId: 'unexpected'
})
}
}
@@ -120,7 +116,7 @@ module.exports = {
context.report({
node: reference.id,
loc: reference.id.loc,
- message: "Expected 'this'.",
+ messageId: 'expected',
fix(fixer) {
return fixer.insertTextBefore(reference.id, 'this.')
}
diff --git a/lib/rules/use-v-on-exact.js b/lib/rules/use-v-on-exact.js
index 0a1790658..f048b70ef 100644
--- a/lib/rules/use-v-on-exact.js
+++ b/lib/rules/use-v-on-exact.js
@@ -4,10 +4,6 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
/**
* @typedef { {name: string, node: VDirectiveKey, modifiers: string[] } } EventDirective
*/
@@ -25,10 +21,6 @@ const GLOBAL_MODIFIERS = new Set([
'native'
])
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
/**
* Finds and returns all keys for event directives
*
@@ -82,17 +74,18 @@ function hasSystemModifier(modifiers) {
* with keys represinting each event name
*
* @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
}
/**
@@ -141,9 +134,9 @@ function hasConflictedModifiers(baseEvent, event) {
const baseEventSystemModifiers = getSystemModifiersString(baseEvent.modifiers)
return (
- baseEvent.modifiers.length >= 1 &&
+ baseEvent.modifiers.length > 0 &&
baseEventSystemModifiers !== eventSystemModifiers &&
- baseEventSystemModifiers.indexOf(eventSystemModifiers) > -1
+ baseEventSystemModifiers.includes(eventSystemModifiers)
)
}
@@ -154,30 +147,31 @@ function hasConflictedModifiers(baseEvent, event) {
* @returns {EventDirective[]} conflicted events, without duplicates
*/
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
}
-// ------------------------------------------------------------------------------
-// Rule details
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'enforce usage of `exact` modifier on `v-on`',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/use-v-on-exact.html'
},
fixable: null,
- schema: []
+ schema: [],
+ messages: {
+ considerExact: "Consider to use '.exact' modifier."
+ }
},
/**
@@ -204,24 +198,24 @@ module.exports = {
const grouppedEvents = groupEvents(events)
- Object.keys(grouppedEvents).forEach((eventName) => {
+ for (const eventName of Object.keys(grouppedEvents)) {
const eventsInGroup = grouppedEvents[eventName]
const hasEventWithKeyModifier = eventsInGroup.some((event) =>
hasSystemModifier(event.modifiers)
)
- if (!hasEventWithKeyModifier) return
+ if (!hasEventWithKeyModifier) continue
const conflictedEvents = findConflictedEvents(eventsInGroup)
- conflictedEvents.forEach((e) => {
+ for (const e of conflictedEvents) {
context.report({
node: e.node,
loc: e.node.loc,
- message: "Consider to use '.exact' modifier."
+ messageId: 'considerExact'
})
- })
- })
+ }
+ }
}
})
}
diff --git a/lib/rules/v-bind-style.js b/lib/rules/v-bind-style.js
index e6878a349..752a8fdf0 100644
--- a/lib/rules/v-bind-style.js
+++ b/lib/rules/v-bind-style.js
@@ -5,71 +5,166 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
+const casing = require('../utils/casing')
+
+/**
+ * @typedef { VDirectiveKey & { name: VIdentifier & { name: 'bind' }, argument: VExpressionContainer | VIdentifier } } VBindDirectiveKey
+ * @typedef { VDirective & { key: VBindDirectiveKey } } VBindDirective
+ */
+
+/**
+ * @param {string} name
+ * @returns {string}
+ */
+function kebabCaseToCamelCase(name) {
+ return casing.isKebabCase(name) ? casing.camelCase(name) : name
+}
+
+/**
+ * @param {VBindDirective} node
+ * @returns {boolean}
+ */
+function isSameName(node) {
+ const attrName =
+ node.key.argument.type === 'VIdentifier' ? node.key.argument.rawName : null
+ const valueName =
+ node.value?.expression?.type === 'Identifier'
+ ? node.value.expression.name
+ : null
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
+ if (!attrName || !valueName) return false
+
+ return kebabCaseToCamelCase(attrName) === kebabCaseToCamelCase(valueName)
+}
+
+/**
+ * @param {VBindDirectiveKey} key
+ * @returns {number}
+ */
+function getCutStart(key) {
+ const modifiers = key.modifiers
+ return modifiers.length > 0
+ ? modifiers[modifiers.length - 1].range[1]
+ : key.argument.range[1]
+}
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'enforce `v-bind` directive style',
- categories: ['vue3-strongly-recommended', 'strongly-recommended'],
+ categories: ['vue3-strongly-recommended', 'vue2-strongly-recommended'],
url: 'https://eslint.vuejs.org/rules/v-bind-style.html'
},
fixable: 'code',
- schema: [{ enum: ['shorthand', 'longform'] }]
+ schema: [
+ { enum: ['shorthand', 'longform'] },
+ {
+ type: 'object',
+ properties: {
+ sameNameShorthand: { enum: ['always', 'never', 'ignore'] }
+ },
+ additionalProperties: false
+ }
+ ],
+ messages: {
+ expectedLonghand: "Expected 'v-bind' before ':'.",
+ unexpectedLonghand: "Unexpected 'v-bind' before ':'.",
+ expectedLonghandForProp: "Expected 'v-bind:' instead of '.'.",
+ expectedShorthand: 'Expected same-name shorthand.',
+ unexpectedShorthand: 'Unexpected same-name shorthand.'
+ }
},
/** @param {RuleContext} context */
create(context) {
const preferShorthand = context.options[0] !== 'longform'
+ /** @type {"always" | "never" | "ignore"} */
+ const sameNameShorthand = context.options[1]?.sameNameShorthand || 'ignore'
- return utils.defineTemplateBodyVisitor(context, {
- /** @param {VDirective} node */
- "VAttribute[directive=true][key.name.name='bind'][key.argument!=null]"(
- node
- ) {
- const shorthandProp = node.key.name.rawName === '.'
- const shorthand = node.key.name.rawName === ':' || shorthandProp
- if (shorthand === preferShorthand) {
- return
- }
+ /** @param {VBindDirective} node */
+ function checkAttributeStyle(node) {
+ const shorthandProp = node.key.name.rawName === '.'
+ const shorthand = node.key.name.rawName === ':' || shorthandProp
+ if (shorthand === preferShorthand) {
+ return
+ }
+
+ let messageId = 'expectedLonghand'
+ if (preferShorthand) {
+ messageId = 'unexpectedLonghand'
+ } else if (shorthandProp) {
+ messageId = 'expectedLonghandForProp'
+ }
+
+ context.report({
+ node,
+ loc: node.loc,
+ messageId,
+ *fix(fixer) {
+ if (preferShorthand) {
+ yield fixer.remove(node.key.name)
+ } else {
+ yield fixer.insertTextBefore(node, 'v-bind')
- context.report({
- node,
- loc: node.loc,
- message: preferShorthand
- ? "Unexpected 'v-bind' before ':'."
- : shorthandProp
- ? "Expected 'v-bind:' instead of '.'."
- : /* otherwise */ "Expected 'v-bind' before ':'.",
- *fix(fixer) {
- if (preferShorthand) {
- yield fixer.remove(node.key.name)
- } else {
- yield fixer.insertTextBefore(node, 'v-bind')
-
- if (shorthandProp) {
- // Replace `.` by `:`.
- yield fixer.replaceText(node.key.name, ':')
-
- // Insert `.prop` modifier if it doesn't exist.
- const modifier = node.key.modifiers[0]
- const isAutoGeneratedPropModifier =
- modifier.name === 'prop' && modifier.rawName === ''
- if (isAutoGeneratedPropModifier) {
- yield fixer.insertTextBefore(modifier, '.prop')
- }
+ if (shorthandProp) {
+ // Replace `.` by `:`.
+ yield fixer.replaceText(node.key.name, ':')
+
+ // Insert `.prop` modifier if it doesn't exist.
+ const modifier = node.key.modifiers[0]
+ const isAutoGeneratedPropModifier =
+ modifier.name === 'prop' && modifier.rawName === ''
+ if (isAutoGeneratedPropModifier) {
+ yield fixer.insertTextBefore(modifier, '.prop')
}
}
}
- })
+ }
+ })
+ }
+
+ /** @param {VBindDirective} node */
+ function checkAttributeSameName(node) {
+ if (sameNameShorthand === 'ignore' || !isSameName(node)) return
+
+ const preferShorthand = sameNameShorthand === 'always'
+ const isShorthand = utils.isVBindSameNameShorthand(node)
+ if (isShorthand === preferShorthand) {
+ return
+ }
+
+ const messageId = preferShorthand
+ ? 'expectedShorthand'
+ : 'unexpectedShorthand'
+
+ context.report({
+ node,
+ loc: node.loc,
+ messageId,
+ *fix(fixer) {
+ if (preferShorthand) {
+ /** @type {Range} */
+ const valueRange = [getCutStart(node.key), node.range[1]]
+
+ yield fixer.removeRange(valueRange)
+ } else if (node.key.argument.type === 'VIdentifier') {
+ yield fixer.insertTextAfter(
+ node,
+ `="${kebabCaseToCamelCase(node.key.argument.rawName)}"`
+ )
+ }
+ }
+ })
+ }
+
+ return utils.defineTemplateBodyVisitor(context, {
+ /** @param {VBindDirective} node */
+ "VAttribute[directive=true][key.name.name='bind'][key.argument!=null]"(
+ node
+ ) {
+ checkAttributeSameName(node)
+ checkAttributeStyle(node)
}
})
}
diff --git a/lib/rules/v-for-delimiter-style.js b/lib/rules/v-for-delimiter-style.js
index a50b6a592..2b20cfd0a 100644
--- a/lib/rules/v-for-delimiter-style.js
+++ b/lib/rules/v-for-delimiter-style.js
@@ -6,27 +6,22 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'layout',
docs: {
description: "enforce `v-for` directive's delimiter style",
categories: undefined,
- recommended: false,
url: 'https://eslint.vuejs.org/rules/v-for-delimiter-style.html'
},
fixable: 'code',
- schema: [{ enum: ['in', 'of'] }]
+ schema: [{ enum: ['in', 'of'] }],
+ messages: {
+ expected:
+ "Expected '{{preferredDelimiter}}' instead of '{{usedDelimiter}}' in 'v-for'."
+ }
},
/** @param {RuleContext} context */
create(context) {
@@ -36,16 +31,17 @@ module.exports = {
return utils.defineTemplateBodyVisitor(context, {
/** @param {VForExpression} node */
VForExpression(node) {
+ const sourceCode = context.getSourceCode()
const tokenStore =
- context.parserServices.getTemplateBodyTokenStore &&
- context.parserServices.getTemplateBodyTokenStore()
+ sourceCode.parserServices.getTemplateBodyTokenStore &&
+ sourceCode.parserServices.getTemplateBodyTokenStore()
const delimiterToken = /** @type {Token} */ (
tokenStore.getTokenAfter(
- node.left.length
+ node.left.length > 0
? node.left[node.left.length - 1]
: tokenStore.getFirstToken(node),
- (token) => token.type !== 'Punctuator' || token.value !== ')'
+ (token) => token.type !== 'Punctuator'
)
)
@@ -56,7 +52,7 @@ module.exports = {
context.report({
node,
loc: node.loc,
- message: `Expected '{{preferredDelimiter}}' instead of '{{usedDelimiter}}' in 'v-for'.`,
+ messageId: 'expected',
data: {
preferredDelimiter,
usedDelimiter: delimiterToken.value
diff --git a/lib/rules/v-if-else-key.js b/lib/rules/v-if-else-key.js
new file mode 100644
index 000000000..d8e913670
--- /dev/null
+++ b/lib/rules/v-if-else-key.js
@@ -0,0 +1,317 @@
+/**
+ * @author Felipe Melendez
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// =============================================================================
+// Requirements
+// =============================================================================
+
+const utils = require('../utils')
+const casing = require('../utils/casing')
+
+// =============================================================================
+// Rule Helpers
+// =============================================================================
+
+/**
+ * A conditional family is made up of a group of repeated components that are conditionally rendered
+ * using v-if, v-else-if, and v-else.
+ *
+ * @typedef {Object} ConditionalFamily
+ * @property {VElement} if - The node associated with the 'v-if' directive.
+ * @property {VElement[]} elseIf - An array of nodes associated with 'v-else-if' directives.
+ * @property {VElement | null} else - The node associated with the 'v-else' directive, or null if there isn't one.
+ */
+
+/**
+ * Checks if a given node has sibling nodes of the same type that are also conditionally rendered.
+ * This is used to determine if multiple instances of the same component are being conditionally
+ * rendered within the same parent scope.
+ *
+ * @param {VElement} node - The Vue component node to check for conditional rendering siblings.
+ * @param {string} componentName - The name of the component to check for sibling instances.
+ * @returns {boolean} True if there are sibling nodes of the same type and conditionally rendered, false otherwise.
+ */
+const hasConditionalRenderedSiblings = (node, componentName) => {
+ if (!node.parent || node.parent.type !== 'VElement') {
+ return false
+ }
+ return node.parent.children.some(
+ (sibling) =>
+ sibling !== node &&
+ sibling.type === 'VElement' &&
+ sibling.rawName === componentName &&
+ hasConditionalDirective(sibling)
+ )
+}
+
+/**
+ * Checks for the presence of a 'key' attribute in the given node. If the 'key' attribute is missing
+ * and the node is part of a conditional family a report is generated.
+ * The fix proposed adds a unique key based on the component's name and count,
+ * following the format '${kebabCase(componentName)}-${componentCount}', e.g., 'some-component-2'.
+ *
+ * @param {VElement} node - The Vue component node to check for a 'key' attribute.
+ * @param {RuleContext} context - The rule's context object, used for reporting.
+ * @param {string} componentName - Name of the component.
+ * @param {string} uniqueKey - A unique key for the repeated component, used for the fix.
+ * @param {Map} conditionalFamilies - Map of conditionally rendered components and their respective conditional directives.
+ */
+const checkForKey = (
+ node,
+ context,
+ componentName,
+ uniqueKey,
+ conditionalFamilies
+) => {
+ if (
+ !node.parent ||
+ node.parent.type !== 'VElement' ||
+ !hasConditionalRenderedSiblings(node, componentName)
+ ) {
+ return
+ }
+
+ const conditionalFamily = conditionalFamilies.get(node.parent)
+
+ if (!conditionalFamily || utils.hasAttribute(node, 'key')) {
+ return
+ }
+
+ const needsKey =
+ conditionalFamily.if === node ||
+ conditionalFamily.else === node ||
+ conditionalFamily.elseIf.includes(node)
+
+ if (needsKey) {
+ context.report({
+ node: node.startTag,
+ loc: node.startTag.loc,
+ messageId: 'requireKey',
+ data: { componentName },
+ fix(fixer) {
+ const afterComponentNamePosition =
+ node.startTag.range[0] + componentName.length + 1
+ return fixer.insertTextBeforeRange(
+ [afterComponentNamePosition, afterComponentNamePosition],
+ ` key="${uniqueKey}"`
+ )
+ }
+ })
+ }
+}
+
+/**
+ * Checks for the presence of conditional directives in the given node.
+ *
+ * @param {VElement} node - The node to check for conditional directives.
+ * @returns {boolean} Returns true if a conditional directive is found in the node or its parents,
+ * false otherwise.
+ */
+const hasConditionalDirective = (node) =>
+ utils.hasDirective(node, 'if') ||
+ utils.hasDirective(node, 'else-if') ||
+ utils.hasDirective(node, 'else')
+
+// =============================================================================
+// Rule Definition
+// =============================================================================
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description:
+ 'require key attribute for conditionally rendered repeated components',
+ categories: null,
+ url: 'https://eslint.vuejs.org/rules/v-if-else-key.html'
+ },
+ // eslint-disable-next-line eslint-plugin/require-meta-fixable -- fixer is not recognized
+ fixable: 'code',
+ schema: [],
+ messages: {
+ requireKey:
+ "Conditionally rendered repeated component '{{componentName}}' expected to have a 'key' attribute."
+ }
+ },
+ /**
+ * Creates and returns a rule object which checks usage of repeated components. If a component
+ * is used more than once, it checks for the presence of a key.
+ *
+ * @param {RuleContext} context - The context object.
+ * @returns {Object} A dictionary of functions to be called on traversal of the template body by
+ * the eslint parser.
+ */
+ create(context) {
+ /**
+ * Map to store conditionally rendered components and their respective conditional directives.
+ *
+ * @type {Map}
+ */
+ const conditionalFamilies = new Map()
+
+ /**
+ * Array of Maps to keep track of components and their usage counts along with the first
+ * node instance. Each Map represents a different scope level, and maps a component name to
+ * an object containing the count and a reference to the first node.
+ */
+ /** @type {Map[]} */
+ const componentUsageStack = [new Map()]
+
+ /**
+ * Checks if a given node represents a custom component without any conditional directives.
+ *
+ * @param {VElement} node - The AST node to check.
+ * @returns {boolean} True if the node represents a custom component without any conditional directives, false otherwise.
+ */
+ const isCustomComponentWithoutCondition = (node) =>
+ node.type === 'VElement' &&
+ utils.isCustomComponent(node) &&
+ !hasConditionalDirective(node)
+
+ /** Set of built-in Vue components that are exempt from the rule. */
+ /** @type {Set} */
+ const exemptTags = new Set(['component', 'slot', 'template'])
+
+ /** Set to keep track of nodes we've pushed to the stack. */
+ /** @type {Set} */
+ const pushedNodes = new Set()
+
+ /**
+ * Creates and returns an object representing a conditional family.
+ *
+ * @param {VElement} ifNode - The VElement associated with the 'v-if' directive.
+ * @returns {ConditionalFamily}
+ */
+ const createConditionalFamily = (ifNode) => ({
+ if: ifNode,
+ elseIf: [],
+ else: null
+ })
+
+ return utils.defineTemplateBodyVisitor(context, {
+ /**
+ * Callback to be executed when a Vue element is traversed. This function checks if the
+ * element is a component, increments the usage count of the component in the
+ * current scope, and checks for the key directive if the component is repeated.
+ *
+ * @param {VElement} node - The traversed Vue element.
+ */
+ VElement(node) {
+ if (exemptTags.has(node.rawName)) {
+ return
+ }
+
+ const condition =
+ utils.getDirective(node, 'if') ||
+ utils.getDirective(node, 'else-if') ||
+ utils.getDirective(node, 'else')
+
+ if (condition) {
+ const conditionType = condition.key.name.name
+
+ if (node.parent && node.parent.type === 'VElement') {
+ let conditionalFamily = conditionalFamilies.get(node.parent)
+
+ if (!conditionalFamily) {
+ conditionalFamily = createConditionalFamily(node)
+ conditionalFamilies.set(node.parent, conditionalFamily)
+ }
+
+ if (conditionalFamily) {
+ switch (conditionType) {
+ case 'if': {
+ conditionalFamily = createConditionalFamily(node)
+ conditionalFamilies.set(node.parent, conditionalFamily)
+ break
+ }
+ case 'else-if': {
+ conditionalFamily.elseIf.push(node)
+ break
+ }
+ case 'else': {
+ conditionalFamily.else = node
+ break
+ }
+ }
+ }
+ }
+ }
+
+ if (isCustomComponentWithoutCondition(node)) {
+ componentUsageStack.push(new Map())
+ return
+ }
+
+ if (!utils.isCustomComponent(node)) {
+ return
+ }
+
+ const componentName = node.rawName
+ const currentScope = componentUsageStack[componentUsageStack.length - 1]
+ const usageInfo = currentScope.get(componentName) || {
+ count: 0,
+ firstNode: null
+ }
+
+ if (hasConditionalDirective(node)) {
+ // Store the first node if this is the first occurrence
+ if (usageInfo.count === 0) {
+ usageInfo.firstNode = node
+ }
+
+ if (usageInfo.count > 0) {
+ const uniqueKey = `${casing.kebabCase(componentName)}-${
+ usageInfo.count + 1
+ }`
+ checkForKey(
+ node,
+ context,
+ componentName,
+ uniqueKey,
+ conditionalFamilies
+ )
+
+ // If this is the second occurrence, also apply a fix to the first occurrence
+ if (usageInfo.count === 1) {
+ const uniqueKeyForFirstInstance = `${casing.kebabCase(
+ componentName
+ )}-1`
+ checkForKey(
+ usageInfo.firstNode,
+ context,
+ componentName,
+ uniqueKeyForFirstInstance,
+ conditionalFamilies
+ )
+ }
+ }
+ usageInfo.count += 1
+ currentScope.set(componentName, usageInfo)
+ }
+ componentUsageStack.push(new Map())
+ pushedNodes.add(node)
+ },
+
+ 'VElement:exit'(node) {
+ if (exemptTags.has(node.rawName)) {
+ return
+ }
+ if (isCustomComponentWithoutCondition(node)) {
+ componentUsageStack.pop()
+ return
+ }
+ if (!utils.isCustomComponent(node)) {
+ return
+ }
+ if (pushedNodes.has(node)) {
+ componentUsageStack.pop()
+ pushedNodes.delete(node)
+ }
+ }
+ })
+ }
+}
diff --git a/lib/rules/v-on-event-hyphenation.js b/lib/rules/v-on-event-hyphenation.js
index 16ea753ab..c9fac76e8 100644
--- a/lib/rules/v-on-event-hyphenation.js
+++ b/lib/rules/v-on-event-hyphenation.js
@@ -2,14 +2,19 @@
const utils = require('../utils')
const casing = require('../utils/casing')
+const { toRegExp } = require('../utils/regexp')
module.exports = {
meta: {
+ type: 'suggestion',
docs: {
description:
'enforce v-on event naming style on custom components in template',
categories: ['vue3-strongly-recommended'],
- url: 'https://eslint.vuejs.org/rules/v-on-event-hyphenation.html'
+ url: 'https://eslint.vuejs.org/rules/v-on-event-hyphenation.html',
+ defaultOptions: {
+ vue3: ['always', { autofix: true }]
+ }
},
fixable: 'code',
schema: [
@@ -26,17 +31,28 @@ module.exports = {
allOf: [
{ type: 'string' },
{ not: { type: 'string', pattern: ':exit$' } },
- { not: { type: 'string', pattern: '^\\s*$' } }
+ { not: { type: 'string', pattern: String.raw`^\s*$` } }
]
},
uniqueItems: true,
additionalItems: false
+ },
+ ignoreTags: {
+ type: 'array',
+ items: { type: 'string' },
+ uniqueItems: true,
+ additionalItems: false
}
},
additionalProperties: false
}
],
- type: 'suggestion'
+ messages: {
+ // eslint-disable-next-line eslint-plugin/report-message-format
+ mustBeHyphenated: "v-on event '{{text}}' must be hyphenated.",
+ // eslint-disable-next-line eslint-plugin/report-message-format
+ cannotBeHyphenated: "v-on event '{{text}}' can't be hyphenated."
+ }
},
/** @param {RuleContext} context */
@@ -47,6 +63,11 @@ module.exports = {
const useHyphenated = option !== 'never'
/** @type {string[]} */
const ignoredAttributes = (optionsPayload && optionsPayload.ignore) || []
+ /** @type {RegExp[]} */
+ const ignoredTagsRegexps = (
+ (optionsPayload && optionsPayload.ignoreTags) ||
+ []
+ ).map(toRegExp)
const autofix = Boolean(optionsPayload && optionsPayload.autofix)
const caseConverter = casing.getConverter(
@@ -64,9 +85,7 @@ module.exports = {
context.report({
node: node.key,
loc: node.loc,
- message: useHyphenated
- ? "v-on event '{{text}}' must be hyphenated."
- : "v-on event '{{text}}' can't be hyphenated.",
+ messageId: useHyphenated ? 'mustBeHyphenated' : 'cannotBeHyphenated',
data: {
text
},
@@ -74,9 +93,7 @@ module.exports = {
autofix &&
// It cannot be converted in snake_case.
!name.includes('_')
- ? (fixer) => {
- return fixer.replaceText(argument, caseConverter(name))
- }
+ ? (fixer) => fixer.replaceText(argument, caseConverter(name))
: null
})
}
@@ -85,9 +102,7 @@ module.exports = {
* @param {string} value
*/
function isIgnoredAttribute(value) {
- const isIgnored = ignoredAttributes.some((attr) => {
- return value.includes(attr)
- })
+ const isIgnored = ignoredAttributes.some((attr) => value.includes(attr))
if (isIgnored) {
return true
@@ -96,9 +111,20 @@ module.exports = {
return useHyphenated ? value.toLowerCase() === value : !/-/.test(value)
}
+ /** @param {string} name */
+ function isIgnoredTagName(name) {
+ return ignoredTagsRegexps.some((re) => re.test(name))
+ }
+
return utils.defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name.name='on']"(node) {
- if (!utils.isCustomComponent(node.parent.parent)) return
+ const element = node.parent.parent
+ if (
+ !utils.isCustomComponent(element) ||
+ isIgnoredTagName(element.rawName)
+ ) {
+ return
+ }
if (!node.key.argument || node.key.argument.type !== 'VIdentifier') {
return
}
diff --git a/lib/rules/v-on-function-call.js b/lib/rules/v-on-function-call.js
deleted file mode 100644
index 90da5ed43..000000000
--- a/lib/rules/v-on-function-call.js
+++ /dev/null
@@ -1,209 +0,0 @@
-/**
- * @author Niklas Higi
- */
-'use strict'
-
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
-const utils = require('../utils')
-
-/**
- * @typedef { import('../utils').ComponentPropertyData } ComponentPropertyData
- */
-
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
-/**
- * Check whether the given token is a quote.
- * @param {Token} token The token to check.
- * @returns {boolean} `true` if the token is a quote.
- */
-function isQuote(token) {
- return (
- token != null &&
- token.type === 'Punctuator' &&
- (token.value === '"' || token.value === "'")
- )
-}
-
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
-module.exports = {
- meta: {
- type: 'suggestion',
- docs: {
- description:
- 'enforce or forbid parentheses after method calls without arguments in `v-on` directives',
- categories: undefined,
- url: 'https://eslint.vuejs.org/rules/v-on-function-call.html'
- },
- fixable: 'code',
- schema: [
- { enum: ['always', 'never'] },
- {
- type: 'object',
- properties: {
- ignoreIncludesComment: {
- type: 'boolean'
- }
- },
- additionalProperties: false
- }
- ]
- },
- /** @param {RuleContext} context */
- create(context) {
- const always = context.options[0] === 'always'
-
- /**
- * @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) {
- return utils.defineTemplateBodyVisitor(context, {
- /** @param {Identifier} node */
- "VAttribute[directive=true][key.name.name='on'][key.argument!=null] > VExpressionContainer > Identifier"(
- node
- ) {
- context.report({
- node,
- message:
- "Method calls inside of 'v-on' directives must have parentheses."
- })
- }
- })
- }
-
- const option = context.options[1] || {}
- const ignoreIncludesComment = !!option.ignoreIncludesComment
- /** @type {Set} */
- const useArgsMethods = new Set()
-
- return utils.defineTemplateBodyVisitor(
- context,
- {
- /** @param {VOnExpression} node */
- "VAttribute[directive=true][key.name.name='on'][key.argument!=null] VOnExpression"(
- node
- ) {
- const expression = getInvalidNeverCallExpression(node)
- if (!expression) {
- return
- }
-
- const tokenStore = context.parserServices.getTemplateBodyTokenStore()
- const tokens = tokenStore.getTokens(node.parent, {
- includeComments: true
- })
- /** @type {Token | undefined} */
- let leftQuote
- /** @type {Token | undefined} */
- let rightQuote
- if (isQuote(tokens[0])) {
- leftQuote = tokens.shift()
- rightQuote = tokens.pop()
- }
-
- const hasComment = tokens.some(
- (token) => token.type === 'Block' || token.type === 'Line'
- )
-
- if (ignoreIncludesComment && hasComment) {
- return
- }
-
- if (expression.callee.type === 'Identifier') {
- if (useArgsMethods.has(expression.callee.name)) {
- // The behavior of target method can change given the arguments.
- return
- }
- }
-
- context.report({
- node: expression,
- message:
- "Method calls without arguments inside of 'v-on' directives must not have parentheses.",
- fix: hasComment
- ? null /* The comment is included and cannot be fixed. */
- : (fixer) => {
- /** @type {Range} */
- const range =
- leftQuote && rightQuote
- ? [leftQuote.range[1], rightQuote.range[0]]
- : [tokens[0].range[0], tokens[tokens.length - 1].range[1]]
-
- return fixer.replaceTextRange(
- range,
- context.getSourceCode().getText(expression.callee)
- )
- }
- })
- }
- },
- utils.defineVueVisitor(context, {
- onVueObjectEnter(node) {
- for (const method of utils.iterateProperties(
- node,
- new Set(['methods'])
- )) {
- if (useArgsMethods.has(method.name)) {
- continue
- }
- if (method.type !== 'object') {
- continue
- }
- const value = method.property.value
- if (
- (value.type === 'FunctionExpression' ||
- value.type === 'ArrowFunctionExpression') &&
- value.params.length > 0
- ) {
- useArgsMethods.add(method.name)
- }
- }
- }
- })
- )
- }
-}
diff --git a/lib/rules/v-on-handler-style.js b/lib/rules/v-on-handler-style.js
new file mode 100644
index 000000000..10ff9b6b1
--- /dev/null
+++ b/lib/rules/v-on-handler-style.js
@@ -0,0 +1,587 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../utils')
+
+/**
+ * @typedef {import('eslint').ReportDescriptorFix} ReportDescriptorFix
+ * @typedef {'method' | 'inline' | 'inline-function'} HandlerKind
+ * @typedef {object} ObjectOption
+ * @property {boolean} [ignoreIncludesComment]
+ */
+
+/**
+ * @param {RuleContext} context
+ */
+function parseOptions(context) {
+ /** @type {[HandlerKind | HandlerKind[] | undefined, ObjectOption | undefined]} */
+ const options = /** @type {any} */ (context.options)
+ /** @type {HandlerKind[]} */
+ const allows = []
+ if (options[0]) {
+ if (Array.isArray(options[0])) {
+ allows.push(...options[0])
+ } else {
+ allows.push(options[0])
+ }
+ } else {
+ allows.push('method', 'inline-function')
+ }
+
+ const option = options[1] || {}
+ const ignoreIncludesComment = !!option.ignoreIncludesComment
+
+ return { allows, ignoreIncludesComment }
+}
+
+/**
+ * Check whether the given token is a quote.
+ * @param {Token} token The token to check.
+ * @returns {boolean} `true` if the token is a quote.
+ */
+function isQuote(token) {
+ return (
+ token != null &&
+ token.type === 'Punctuator' &&
+ (token.value === '"' || token.value === "'")
+ )
+}
+/**
+ * Check whether the given node is an identifier call expression. e.g. `foo()`
+ * @param {Expression} node The node to check.
+ * @returns {node is CallExpression & {callee: Identifier}}
+ */
+function isIdentifierCallExpression(node) {
+ if (node.type !== 'CallExpression') {
+ return false
+ }
+ if (node.optional) {
+ // optional chaining
+ return false
+ }
+ const callee = node.callee
+ return callee.type === 'Identifier'
+}
+
+/**
+ * Returns a call expression node if the given VOnExpression or BlockStatement consists
+ * of only a single identifier call expression.
+ * e.g.
+ * @click="foo()"
+ * @click="{ foo() }"
+ * @click="foo();;"
+ * @param {VOnExpression | BlockStatement} node
+ * @returns {CallExpression & {callee: Identifier} | null}
+ */
+function getIdentifierCallExpression(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 (!isIdentifierCallExpression(expression)) {
+ return null
+ }
+ return expression
+}
+
+module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'enforce writing style for handlers in `v-on` directives',
+ categories: undefined,
+ url: 'https://eslint.vuejs.org/rules/v-on-handler-style.html'
+ },
+ fixable: 'code',
+ schema: [
+ {
+ oneOf: [
+ { enum: ['inline', 'inline-function'] },
+ {
+ type: 'array',
+ items: [
+ { const: 'method' },
+ { enum: ['inline', 'inline-function'] }
+ ],
+ uniqueItems: true,
+ additionalItems: false,
+ minItems: 2,
+ maxItems: 2
+ }
+ ]
+ },
+ {
+ type: 'object',
+ properties: {
+ ignoreIncludesComment: {
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ messages: {
+ preferMethodOverInline:
+ 'Prefer method handler over inline handler in v-on.',
+ preferMethodOverInlineWithoutIdCall:
+ 'Prefer method handler over inline handler in v-on. Note that you may need to create a new method.',
+ preferMethodOverInlineFunction:
+ 'Prefer method handler over inline function in v-on.',
+ preferMethodOverInlineFunctionWithoutIdCall:
+ 'Prefer method handler over inline function in v-on. Note that you may need to create a new method.',
+ preferInlineOverMethod:
+ 'Prefer inline handler over method handler in v-on.',
+ preferInlineOverInlineFunction:
+ 'Prefer inline handler over inline function in v-on.',
+ preferInlineOverInlineFunctionWithMultipleParams:
+ 'Prefer inline handler over inline function in v-on. Note that the custom event must be changed to a single payload.',
+ preferInlineFunctionOverMethod:
+ 'Prefer inline function over method handler in v-on.',
+ preferInlineFunctionOverInline:
+ 'Prefer inline function over inline handler in v-on.'
+ }
+ },
+ /** @param {RuleContext} context */
+ create(context) {
+ const { allows, ignoreIncludesComment } = parseOptions(context)
+
+ /** @type {Set} */
+ const upperElements = new Set()
+ /** @type {Map} */
+ const methodParamCountMap = new Map()
+ /** @type {Identifier[]} */
+ const $eventIdentifiers = []
+
+ /**
+ * Verify for inline handler.
+ * @param {VOnExpression} node
+ * @param {HandlerKind} kind
+ * @returns {boolean} Returns `true` if reported.
+ */
+ function verifyForInlineHandler(node, kind) {
+ switch (kind) {
+ case 'method': {
+ return verifyCanUseMethodHandlerForInlineHandler(node)
+ }
+ case 'inline-function': {
+ reportCanUseInlineFunctionForInlineHandler(node)
+ return true
+ }
+ }
+ return false
+ }
+ /**
+ * Report for method handler.
+ * @param {Identifier} node
+ * @param {HandlerKind} kind
+ * @returns {boolean} Returns `true` if reported.
+ */
+ function reportForMethodHandler(node, kind) {
+ switch (kind) {
+ case 'inline':
+ case 'inline-function': {
+ context.report({
+ node,
+ messageId:
+ kind === 'inline'
+ ? 'preferInlineOverMethod'
+ : 'preferInlineFunctionOverMethod'
+ })
+ return true
+ }
+ }
+ // This path is currently not taken.
+ return false
+ }
+ /**
+ * Verify for inline function handler.
+ * @param {ArrowFunctionExpression | FunctionExpression} node
+ * @param {HandlerKind} kind
+ * @returns {boolean} Returns `true` if reported.
+ */
+ function verifyForInlineFunction(node, kind) {
+ switch (kind) {
+ case 'method': {
+ return verifyCanUseMethodHandlerForInlineFunction(node)
+ }
+ case 'inline': {
+ reportCanUseInlineHandlerForInlineFunction(node)
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
+ * Get token information for the given VExpressionContainer node.
+ * @param {VExpressionContainer} node
+ */
+ function getVExpressionContainerTokenInfo(node) {
+ const sourceCode = context.getSourceCode()
+ const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
+ const tokens = tokenStore.getTokens(node, {
+ includeComments: true
+ })
+ const firstToken = tokens[0]
+ const lastToken = tokens[tokens.length - 1]
+
+ const hasQuote = isQuote(firstToken)
+ /** @type {Range} */
+ const rangeWithoutQuotes = hasQuote
+ ? [firstToken.range[1], lastToken.range[0]]
+ : [firstToken.range[0], lastToken.range[1]]
+
+ return {
+ rangeWithoutQuotes,
+ get hasComment() {
+ return tokens.some(
+ (token) => token.type === 'Block' || token.type === 'Line'
+ )
+ },
+ hasQuote
+ }
+ }
+
+ /**
+ * Checks whether the given node refers to a variable of the element.
+ * @param {Expression | VOnExpression} node
+ */
+ function hasReferenceUpperElementVariable(node) {
+ for (const element of upperElements) {
+ for (const vv of element.variables) {
+ for (const reference of vv.references) {
+ const { range } = reference.id
+ if (node.range[0] <= range[0] && range[1] <= node.range[1]) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+ }
+ /**
+ * Check if `v-on:click="foo()"` can be converted to `v-on:click="foo"` and report if it can.
+ * @param {VOnExpression} node
+ * @returns {boolean} Returns `true` if reported.
+ */
+ function verifyCanUseMethodHandlerForInlineHandler(node) {
+ const { rangeWithoutQuotes, hasComment } =
+ getVExpressionContainerTokenInfo(node.parent)
+ if (ignoreIncludesComment && hasComment) {
+ return false
+ }
+
+ const idCallExpr = getIdentifierCallExpression(node)
+ if (
+ (!idCallExpr || idCallExpr.arguments.length > 0) &&
+ hasReferenceUpperElementVariable(node)
+ ) {
+ // It cannot be converted to method because it refers to the variable of the element.
+ // e.g.
+ return false
+ }
+
+ context.report({
+ node,
+ messageId: idCallExpr
+ ? 'preferMethodOverInline'
+ : 'preferMethodOverInlineWithoutIdCall',
+ fix: (fixer) => {
+ if (
+ hasComment /* The statement contains comment and cannot be fixed. */ ||
+ !idCallExpr /* The statement is not a simple identifier call and cannot be fixed. */ ||
+ idCallExpr.arguments.length > 0
+ ) {
+ return null
+ }
+ const paramCount = methodParamCountMap.get(idCallExpr.callee.name)
+ if (paramCount != null && paramCount > 0) {
+ // The behavior of target method can change given the arguments.
+ return null
+ }
+ return fixer.replaceTextRange(
+ rangeWithoutQuotes,
+ context.getSourceCode().getText(idCallExpr.callee)
+ )
+ }
+ })
+ return true
+ }
+ /**
+ * Check if `v-on:click="() => foo()"` can be converted to `v-on:click="foo"` and report if it can.
+ * @param {ArrowFunctionExpression | FunctionExpression} node
+ * @returns {boolean} Returns `true` if reported.
+ */
+ function verifyCanUseMethodHandlerForInlineFunction(node) {
+ const { rangeWithoutQuotes, hasComment } =
+ getVExpressionContainerTokenInfo(
+ /** @type {VExpressionContainer} */ (node.parent)
+ )
+ if (ignoreIncludesComment && hasComment) {
+ return false
+ }
+
+ /** @type {CallExpression & {callee: Identifier} | null} */
+ let idCallExpr = null
+ if (node.body.type === 'BlockStatement') {
+ idCallExpr = getIdentifierCallExpression(node.body)
+ } else if (isIdentifierCallExpression(node.body)) {
+ idCallExpr = node.body
+ }
+ if (
+ (!idCallExpr || !isSameParamsAndArgs(idCallExpr)) &&
+ hasReferenceUpperElementVariable(node)
+ ) {
+ // It cannot be converted to method because it refers to the variable of the element.
+ // e.g.
+ return false
+ }
+
+ context.report({
+ node,
+ messageId: idCallExpr
+ ? 'preferMethodOverInlineFunction'
+ : 'preferMethodOverInlineFunctionWithoutIdCall',
+ fix: (fixer) => {
+ if (
+ hasComment /* The function contains comment and cannot be fixed. */ ||
+ !idCallExpr /* The function is not a simple identifier call and cannot be fixed. */
+ ) {
+ return null
+ }
+ if (!isSameParamsAndArgs(idCallExpr)) {
+ // It is not a call with the arguments given as is.
+ return null
+ }
+ const paramCount = methodParamCountMap.get(idCallExpr.callee.name)
+ if (
+ paramCount != null &&
+ paramCount !== idCallExpr.arguments.length
+ ) {
+ // The behavior of target method can change given the arguments.
+ return null
+ }
+ return fixer.replaceTextRange(
+ rangeWithoutQuotes,
+ context.getSourceCode().getText(idCallExpr.callee)
+ )
+ }
+ })
+ return true
+
+ /**
+ * Checks whether parameters are passed as arguments as-is.
+ * @param {CallExpression} expression
+ */
+ function isSameParamsAndArgs(expression) {
+ return (
+ node.params.length === expression.arguments.length &&
+ node.params.every((param, index) => {
+ if (param.type !== 'Identifier') {
+ return false
+ }
+ const arg = expression.arguments[index]
+ if (!arg || arg.type !== 'Identifier') {
+ return false
+ }
+ return param.name === arg.name
+ })
+ )
+ }
+ }
+ /**
+ * Report `v-on:click="foo()"` can be converted to `v-on:click="()=>foo()"`.
+ * @param {VOnExpression} node
+ * @returns {void}
+ */
+ function reportCanUseInlineFunctionForInlineHandler(node) {
+ context.report({
+ node,
+ messageId: 'preferInlineFunctionOverInline',
+ *fix(fixer) {
+ const has$Event = $eventIdentifiers.some(
+ ({ range }) =>
+ node.range[0] <= range[0] && range[1] <= node.range[1]
+ )
+ if (has$Event) {
+ /* The statements contains $event and cannot be fixed. */
+ return
+ }
+ const { rangeWithoutQuotes, hasQuote } =
+ getVExpressionContainerTokenInfo(node.parent)
+ if (!hasQuote) {
+ /* The statements is not enclosed in quotes and cannot be fixed. */
+ return
+ }
+ yield fixer.insertTextBeforeRange(rangeWithoutQuotes, '() => ')
+ const sourceCode = context.getSourceCode()
+ const tokenStore =
+ sourceCode.parserServices.getTemplateBodyTokenStore()
+ const firstToken = tokenStore.getFirstToken(node)
+ const lastToken = tokenStore.getLastToken(node)
+ if (firstToken.value === '{' && lastToken.value === '}') return
+ if (
+ lastToken.value !== ';' &&
+ node.body.length === 1 &&
+ node.body[0].type === 'ExpressionStatement'
+ ) {
+ // it is a single expression
+ return
+ }
+ yield fixer.insertTextBefore(firstToken, '{')
+ yield fixer.insertTextAfter(lastToken, '}')
+ }
+ })
+ }
+ /**
+ * Report `v-on:click="() => foo()"` can be converted to `v-on:click="foo()"`.
+ * @param {ArrowFunctionExpression | FunctionExpression} node
+ * @returns {void}
+ */
+ function reportCanUseInlineHandlerForInlineFunction(node) {
+ // If a function has one parameter, you can turn it into an inline handler using $event.
+ // If a function has two or more parameters, it cannot be easily converted to an inline handler.
+ // However, users can use inline handlers by changing the payload of the component's custom event.
+ // So we report it regardless of the number of parameters.
+
+ context.report({
+ node,
+ messageId:
+ node.params.length > 1
+ ? 'preferInlineOverInlineFunctionWithMultipleParams'
+ : 'preferInlineOverInlineFunction',
+ fix:
+ node.params.length > 0
+ ? null /* The function has parameters and cannot be fixed. */
+ : (fixer) => {
+ let text = context.getSourceCode().getText(node.body)
+ if (node.body.type === 'BlockStatement') {
+ text = text.slice(1, -1) // strip braces
+ }
+ return fixer.replaceText(node, text)
+ }
+ })
+ }
+
+ return utils.defineTemplateBodyVisitor(
+ context,
+ {
+ VElement(node) {
+ upperElements.add(node)
+ },
+ 'VElement:exit'(node) {
+ upperElements.delete(node)
+ },
+ /** @param {VExpressionContainer} node */
+ "VAttribute[directive=true][key.name.name='on'][key.argument!=null] > VExpressionContainer.value:exit"(
+ node
+ ) {
+ const expression = node.expression
+ if (!expression) {
+ return
+ }
+ switch (expression.type) {
+ case 'VOnExpression': {
+ // e.g. v-on:click="foo()"
+ if (allows[0] === 'inline') {
+ return
+ }
+ for (const allow of allows) {
+ if (verifyForInlineHandler(expression, allow)) {
+ return
+ }
+ }
+ break
+ }
+ case 'Identifier': {
+ // e.g. v-on:click="foo"
+ if (allows[0] === 'method') {
+ return
+ }
+ for (const allow of allows) {
+ if (reportForMethodHandler(expression, allow)) {
+ return
+ }
+ }
+ break
+ }
+ case 'ArrowFunctionExpression':
+ case 'FunctionExpression': {
+ // e.g. v-on:click="()=>foo()"
+ if (allows[0] === 'inline-function') {
+ return
+ }
+ for (const allow of allows) {
+ if (verifyForInlineFunction(expression, allow)) {
+ return
+ }
+ }
+ break
+ }
+ default: {
+ return
+ }
+ }
+ },
+ ...(allows.includes('inline-function')
+ ? // Collect $event identifiers to check for side effects
+ // when converting from `v-on:click="foo($event)"` to `v-on:click="()=>foo($event)"` .
+ {
+ 'Identifier[name="$event"]'(node) {
+ $eventIdentifiers.push(node)
+ }
+ }
+ : {})
+ },
+ allows.includes('method')
+ ? // Collect method definition with params information to check for side effects.
+ // when converting from `v-on:click="foo()"` to `v-on:click="foo"`, or
+ // converting from `v-on:click="() => foo()"` to `v-on:click="foo"`.
+ utils.defineVueVisitor(context, {
+ onVueObjectEnter(node) {
+ for (const method of utils.iterateProperties(
+ node,
+ new Set(['methods'])
+ )) {
+ if (method.type !== 'object') {
+ // This branch is usually not passed.
+ continue
+ }
+ const value = method.property.value
+ if (
+ value.type === 'FunctionExpression' ||
+ value.type === 'ArrowFunctionExpression'
+ ) {
+ methodParamCountMap.set(
+ method.name,
+ value.params.some((p) => p.type === 'RestElement')
+ ? Number.POSITIVE_INFINITY
+ : value.params.length
+ )
+ }
+ }
+ }
+ })
+ : {}
+ )
+ }
+}
diff --git a/lib/rules/v-on-style.js b/lib/rules/v-on-style.js
index 4649aacf9..eb5950026 100644
--- a/lib/rules/v-on-style.js
+++ b/lib/rules/v-on-style.js
@@ -5,26 +5,22 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'enforce `v-on` directive style',
- categories: ['vue3-strongly-recommended', 'strongly-recommended'],
+ categories: ['vue3-strongly-recommended', 'vue2-strongly-recommended'],
url: 'https://eslint.vuejs.org/rules/v-on-style.html'
},
fixable: 'code',
- schema: [{ enum: ['shorthand', 'longform'] }]
+ schema: [{ enum: ['shorthand', 'longform'] }],
+ messages: {
+ expectedShorthand: "Expected '@' instead of 'v-on:'.",
+ expectedLonghand: "Expected 'v-on:' instead of '@'."
+ }
},
/** @param {RuleContext} context */
create(context) {
@@ -44,9 +40,7 @@ module.exports = {
context.report({
node,
loc: node.loc,
- message: preferShorthand
- ? "Expected '@' instead of 'v-on:'."
- : "Expected 'v-on:' instead of '@'.",
+ messageId: preferShorthand ? 'expectedShorthand' : 'expectedLonghand',
fix: (fixer) =>
preferShorthand
? fixer.replaceTextRange([pos, pos + 5], '@')
diff --git a/lib/rules/v-slot-style.js b/lib/rules/v-slot-style.js
index 1bb5f57f2..964550b98 100644
--- a/lib/rules/v-slot-style.js
+++ b/lib/rules/v-slot-style.js
@@ -86,13 +86,13 @@ module.exports = {
type: 'suggestion',
docs: {
description: 'enforce `v-slot` directive style',
- categories: ['vue3-strongly-recommended', 'strongly-recommended'],
+ categories: ['vue3-strongly-recommended', 'vue2-strongly-recommended'],
url: 'https://eslint.vuejs.org/rules/v-slot-style.html'
},
fixable: 'code',
schema: [
{
- anyOf: [
+ oneOf: [
{ enum: ['shorthand', 'longform'] },
{
type: 'object',
@@ -141,14 +141,18 @@ module.exports = {
fix(fixer) {
switch (expected) {
- case 'shorthand':
+ case 'shorthand': {
return fixer.replaceTextRange(range, `#${argumentText}`)
- case 'longform':
+ }
+ case 'longform': {
return fixer.replaceTextRange(range, `v-slot:${argumentText}`)
- case 'v-slot':
+ }
+ case 'v-slot': {
return fixer.replaceTextRange(range, 'v-slot')
- default:
+ }
+ default: {
return null
+ }
}
}
})
diff --git a/lib/rules/valid-attribute-name.js b/lib/rules/valid-attribute-name.js
new file mode 100644
index 000000000..51d52a1b6
--- /dev/null
+++ b/lib/rules/valid-attribute-name.js
@@ -0,0 +1,69 @@
+/**
+ * @author Doug Wade
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../utils')
+const xnv = require('xml-name-validator')
+
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'require valid attribute names',
+ categories: ['vue3-essential', 'vue2-essential'],
+ url: 'https://eslint.vuejs.org/rules/valid-attribute-name.html'
+ },
+ fixable: null,
+ schema: [],
+ messages: {
+ attribute: 'Attribute name {{name}} is not valid.'
+ }
+ },
+ /** @param {RuleContext} context */
+ create(context) {
+ /**
+ * @param {string | VIdentifier} key
+ * @return {string}
+ */
+ const getName = (key) => (typeof key === 'string' ? key : key.name)
+
+ return utils.defineTemplateBodyVisitor(context, {
+ /** @param {VDirective | VAttribute} node */
+ VAttribute(node) {
+ if (utils.isCustomComponent(node.parent.parent)) {
+ return
+ }
+
+ const name = getName(node.key.name)
+
+ if (
+ node.directive &&
+ name === 'bind' &&
+ node.key.argument &&
+ node.key.argument.type === 'VIdentifier' &&
+ !xnv.name(node.key.argument.name)
+ ) {
+ context.report({
+ node,
+ messageId: 'attribute',
+ data: {
+ name: node.key.argument.name
+ }
+ })
+ }
+
+ if (!node.directive && !xnv.name(name)) {
+ context.report({
+ node,
+ messageId: 'attribute',
+ data: {
+ name
+ }
+ })
+ }
+ }
+ })
+ }
+}
diff --git a/lib/rules/valid-define-emits.js b/lib/rules/valid-define-emits.js
index 6f433a835..8d2306c23 100644
--- a/lib/rules/valid-define-emits.js
+++ b/lib/rules/valid-define-emits.js
@@ -4,7 +4,7 @@
*/
'use strict'
-const { findVariable } = require('eslint-utils')
+const { findVariable } = require('@eslint-community/eslint-utils')
const utils = require('../utils')
module.exports = {
@@ -12,7 +12,7 @@ module.exports = {
type: 'problem',
docs: {
description: 'enforce valid `defineEmits` compiler macro',
- categories: ['vue3-essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-define-emits.html'
},
fixable: null,
@@ -20,7 +20,7 @@ module.exports = {
messages: {
hasTypeAndArg: '`defineEmits` has both a type-only emit and an argument.',
referencingLocally:
- '`defineEmits` are referencing locally declared variables.',
+ '`defineEmits` is referencing locally declared variables.',
multiple: '`defineEmits` has been called multiple times.',
notDefined: 'Custom events are not defined.',
definedInBoth:
@@ -47,8 +47,10 @@ module.exports = {
onDefineEmitsEnter(node) {
defineEmitsNodes.push(node)
- if (node.arguments.length >= 1) {
- if (node.typeParameters && node.typeParameters.params.length >= 1) {
+ const typeArguments =
+ 'typeArguments' in node ? node.typeArguments : node.typeParameters
+ if (node.arguments.length > 0) {
+ if (typeArguments && typeArguments.params.length > 0) {
// `defineEmits` has both a literal type and an argument.
context.report({
node,
@@ -59,10 +61,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
}
}
@@ -70,28 +69,26 @@ module.exports = {
Identifier(node) {
for (const defineEmits of emitsDefExpressions) {
if (utils.inRange(defineEmits.range, node)) {
- const variable = findVariable(context.getScope(), node)
+ const variable = findVariable(utils.getScope(context, node), node)
if (
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) =>
- 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` is referencing locally declared variables.
+ context.report({
+ node,
+ messageId: 'referencingLocally'
+ })
}
}
}
@@ -108,7 +105,7 @@ module.exports = {
}),
{
'Program:exit'() {
- if (!defineEmitsNodes.length) {
+ if (defineEmitsNodes.length === 0) {
return
}
if (defineEmitsNodes.length > 1) {
diff --git a/lib/rules/valid-define-options.js b/lib/rules/valid-define-options.js
new file mode 100644
index 000000000..12771b8fe
--- /dev/null
+++ b/lib/rules/valid-define-options.js
@@ -0,0 +1,127 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const { findVariable } = require('@eslint-community/eslint-utils')
+const utils = require('../utils')
+
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'enforce valid `defineOptions` compiler macro',
+ categories: ['vue3-essential'],
+ url: 'https://eslint.vuejs.org/rules/valid-define-options.html'
+ },
+ fixable: null,
+ schema: [],
+ messages: {
+ referencingLocally:
+ '`defineOptions` is referencing locally declared variables.',
+ multiple: '`defineOptions` has been called multiple times.',
+ notDefined: 'Options are not defined.',
+ disallowProp:
+ '`defineOptions()` cannot be used to declare `{{propName}}`. Use `{{insteadMacro}}()` instead.',
+ typeArgs: '`defineOptions()` cannot accept type arguments.'
+ }
+ },
+ /** @param {RuleContext} context */
+ create(context) {
+ const scriptSetup = utils.getScriptSetupElement(context)
+ if (!scriptSetup) {
+ return {}
+ }
+
+ /** @type {Set} */
+ const optionsDefExpressions = new Set()
+ /** @type {CallExpression[]} */
+ const defineOptionsNodes = []
+
+ return utils.compositingVisitors(
+ utils.defineScriptSetupVisitor(context, {
+ onDefineOptionsEnter(node) {
+ defineOptionsNodes.push(node)
+
+ if (node.arguments.length > 0) {
+ const define = node.arguments[0]
+ if (define.type === 'ObjectExpression') {
+ for (const [propName, insteadMacro] of [
+ ['props', 'defineProps'],
+ ['emits', 'defineEmits'],
+ ['expose', 'defineExpose'],
+ ['slots', 'defineSlots']
+ ]) {
+ const prop = utils.findProperty(define, propName)
+ if (prop) {
+ context.report({
+ node,
+ messageId: 'disallowProp',
+ data: { propName, insteadMacro }
+ })
+ }
+ }
+ }
+
+ optionsDefExpressions.add(node.arguments[0])
+ } else {
+ context.report({
+ node,
+ messageId: 'notDefined'
+ })
+ }
+
+ const typeArguments =
+ 'typeArguments' in node ? node.typeArguments : node.typeParameters
+ if (typeArguments) {
+ context.report({
+ node: typeArguments,
+ messageId: 'typeArgs'
+ })
+ }
+ },
+ Identifier(node) {
+ for (const defineOptions of optionsDefExpressions) {
+ if (utils.inRange(defineOptions.range, node)) {
+ const variable = findVariable(utils.getScope(context, node), node)
+ if (
+ variable &&
+ 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(defineOptions.range, def.name)
+ )
+ ) {
+ if (utils.withinTypeNode(node)) {
+ continue
+ }
+ //`defineOptions` is referencing locally declared variables.
+ context.report({
+ node,
+ messageId: 'referencingLocally'
+ })
+ }
+ }
+ }
+ }
+ }),
+ {
+ 'Program:exit'() {
+ if (defineOptionsNodes.length > 1) {
+ // `defineOptions` has been called multiple times.
+ for (const node of defineOptionsNodes) {
+ context.report({
+ node,
+ messageId: 'multiple'
+ })
+ }
+ }
+ }
+ }
+ )
+ }
+}
diff --git a/lib/rules/valid-define-props.js b/lib/rules/valid-define-props.js
index 3a084dd68..2ba81b188 100644
--- a/lib/rules/valid-define-props.js
+++ b/lib/rules/valid-define-props.js
@@ -4,7 +4,7 @@
*/
'use strict'
-const { findVariable } = require('eslint-utils')
+const { findVariable } = require('@eslint-community/eslint-utils')
const utils = require('../utils')
module.exports = {
@@ -12,7 +12,7 @@ module.exports = {
type: 'problem',
docs: {
description: 'enforce valid `defineProps` compiler macro',
- categories: ['vue3-essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-define-props.html'
},
fixable: null,
@@ -21,7 +21,7 @@ module.exports = {
hasTypeAndArg:
'`defineProps` has both a type-only props and an argument.',
referencingLocally:
- '`defineProps` are referencing locally declared variables.',
+ '`defineProps` is referencing locally declared variables.',
multiple: '`defineProps` has been called multiple times.',
notDefined: 'Props are not defined.',
definedInBoth:
@@ -48,8 +48,10 @@ module.exports = {
onDefinePropsEnter(node) {
definePropsNodes.push(node)
- if (node.arguments.length >= 1) {
- if (node.typeParameters && node.typeParameters.params.length >= 1) {
+ const typeArguments =
+ 'typeArguments' in node ? node.typeArguments : node.typeParameters
+ if (node.arguments.length > 0) {
+ if (typeArguments && typeArguments.params.length > 0) {
// `defineProps` has both a literal type and an argument.
context.report({
node,
@@ -60,10 +62,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
}
}
@@ -71,28 +70,26 @@ module.exports = {
Identifier(node) {
for (const defineProps of propsDefExpressions) {
if (utils.inRange(defineProps.range, node)) {
- const variable = findVariable(context.getScope(), node)
+ const variable = findVariable(utils.getScope(context, node), node)
if (
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) =>
- 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` is referencing locally declared variables.
+ context.report({
+ node,
+ messageId: 'referencingLocally'
+ })
}
}
}
@@ -109,7 +106,7 @@ module.exports = {
}),
{
'Program:exit'() {
- if (!definePropsNodes.length) {
+ if (definePropsNodes.length === 0) {
return
}
if (definePropsNodes.length > 1) {
diff --git a/lib/rules/no-invalid-model-keys.js b/lib/rules/valid-model-definition.js
similarity index 59%
rename from lib/rules/no-invalid-model-keys.js
rename to lib/rules/valid-model-definition.js
index f86587ff1..6d82fca32 100644
--- a/lib/rules/no-invalid-model-keys.js
+++ b/lib/rules/valid-model-definition.js
@@ -6,28 +6,25 @@
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-const VALID_MODEL_KEYS = ['prop', 'event']
+const VALID_MODEL_KEYS = new Set(['prop', 'event'])
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'require valid keys in model option',
- categories: undefined,
- url: 'https://eslint.vuejs.org/rules/no-invalid-model-keys.html'
+ categories: ['vue2-essential'],
+ url: 'https://eslint.vuejs.org/rules/valid-model-definition.html'
},
fixable: null,
- schema: []
+ deprecated: true,
+ schema: [],
+ messages: {
+ invalidKey: "Invalid key '{{name}}' in model option."
+ }
},
/** @param {RuleContext} context */
create(context) {
- // ----------------------------------------------------------------------
- // Public
- // ----------------------------------------------------------------------
-
return utils.executeOnVue(context, (obj) => {
const modelProperty = utils.findProperty(obj, 'model')
if (!modelProperty || modelProperty.value.type !== 'ObjectExpression') {
@@ -42,10 +39,10 @@ module.exports = {
if (!name) {
continue
}
- if (VALID_MODEL_KEYS.indexOf(name) === -1) {
+ if (!VALID_MODEL_KEYS.has(name)) {
context.report({
node: p,
- message: "Invalid key '{{name}}' in model option.",
+ messageId: 'invalidKey',
data: {
name
}
diff --git a/lib/rules/valid-next-tick.js b/lib/rules/valid-next-tick.js
index ed5137f8c..c384f6ac0 100644
--- a/lib/rules/valid-next-tick.js
+++ b/lib/rules/valid-next-tick.js
@@ -6,16 +6,8 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-const { findVariable } = require('eslint-utils')
-
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
+const { findVariable } = require('@eslint-community/eslint-utils')
/**
* @param {Identifier} identifier
@@ -43,7 +35,7 @@ function getVueNextTickNode(identifier, context) {
}
// Vue 3 Global API: import { nextTick as nt } from 'vue'; nt()
- const variable = findVariable(context.getScope(), identifier)
+ const variable = findVariable(utils.getScope(context, identifier), identifier)
if (variable != null && variable.defs.length === 1) {
const def = variable.defs[0]
@@ -116,21 +108,26 @@ function isAwaitedPromise(callExpression) {
return false
}
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
- hasSuggestions: true,
type: 'problem',
docs: {
description: 'enforce valid `nextTick` function calls',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-next-tick.html'
},
fixable: 'code',
- schema: []
+ hasSuggestions: true,
+ schema: [],
+ messages: {
+ shouldBeFunction: '`nextTick` is a function.',
+ missingCallbackOrAwait:
+ 'Await the Promise returned by `nextTick` or pass a callback function.',
+ addAwait: 'Add missing `await` statement.',
+ tooManyParameters: '`nextTick` expects zero or one parameters.',
+ eitherAwaitOrCallback:
+ 'Either await the Promise or pass a callback function to `nextTick`.'
+ }
},
/** @param {RuleContext} context */
create(context) {
@@ -142,7 +139,12 @@ module.exports = {
return
}
- const parentNode = nextTickNode.parent
+ let parentNode = nextTickNode.parent
+
+ // skip conditional expressions like `foo ? nextTick : bar`
+ if (parentNode.type === 'ConditionalExpression') {
+ parentNode = parentNode.parent
+ }
if (
parentNode.type === 'CallExpression' &&
@@ -163,7 +165,7 @@ module.exports = {
if (parentNode.type !== 'CallExpression') {
context.report({
node,
- message: '`nextTick` is a function.',
+ messageId: 'shouldBeFunction',
fix(fixer) {
return fixer.insertTextAfter(node, '()')
}
@@ -175,11 +177,10 @@ module.exports = {
if (!isAwaitedPromise(parentNode)) {
context.report({
node,
- message:
- 'Await the Promise returned by `nextTick` or pass a callback function.',
+ messageId: 'missingCallbackOrAwait',
suggest: [
{
- desc: 'Add missing `await` statement.',
+ messageId: 'addAwait',
fix(fixer) {
return fixer.insertTextBefore(parentNode, 'await ')
}
@@ -193,7 +194,7 @@ module.exports = {
if (parentNode.arguments.length > 1) {
context.report({
node,
- message: '`nextTick` expects zero or one parameters.'
+ messageId: 'tooManyParameters'
})
return
}
@@ -201,8 +202,7 @@ module.exports = {
if (isAwaitedPromise(parentNode)) {
context.report({
node,
- message:
- 'Either await the Promise or pass a callback function to `nextTick`.'
+ messageId: 'eitherAwaitOrCallback'
})
}
}
diff --git a/lib/rules/valid-template-root.js b/lib/rules/valid-template-root.js
index 703ab8b8e..c604a43a8 100644
--- a/lib/rules/valid-template-root.js
+++ b/lib/rules/valid-template-root.js
@@ -5,26 +5,23 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid template root',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-template-root.html'
},
fixable: null,
- schema: []
+ schema: [],
+ messages: {
+ emptySrc:
+ "The template root with 'src' attribute is required to be empty.",
+ noChild: 'The template requires child element.'
+ }
},
/** @param {RuleContext} context */
create(context) {
@@ -47,20 +44,19 @@ module.exports = {
}
}
- if (hasSrc && rootElements.length) {
+ if (hasSrc && rootElements.length > 0) {
for (const element of rootElements) {
context.report({
node: element,
loc: element.loc,
- message:
- "The template root with 'src' attribute is required to be empty."
+ messageId: 'emptySrc'
})
}
} else if (rootElements.length === 0 && !hasSrc) {
context.report({
node: element,
loc: element.loc,
- message: 'The template requires child element.'
+ messageId: 'noChild'
})
}
}
diff --git a/lib/rules/valid-v-bind-sync.js b/lib/rules/valid-v-bind-sync.js
index 2a42e1b32..ba04d6117 100644
--- a/lib/rules/valid-v-bind-sync.js
+++ b/lib/rules/valid-v-bind-sync.js
@@ -4,16 +4,8 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
/**
* Check whether the given node is valid or not.
* @param {VElement} node The element node to check.
@@ -78,19 +70,16 @@ function maybeNullObjectMemberExpression(node) {
return false
}
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `.sync` modifier on `v-bind` directives',
- categories: ['essential'],
+ categories: ['vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-bind-sync.html'
},
fixable: null,
+ deprecated: true,
schema: [],
messages: {
unexpectedInvalidElement:
diff --git a/lib/rules/valid-v-bind.js b/lib/rules/valid-v-bind.js
index 6605ed1c7..0f44dad26 100644
--- a/lib/rules/valid-v-bind.js
+++ b/lib/rules/valid-v-bind.js
@@ -5,28 +5,16 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
const VALID_MODIFIERS = new Set(['prop', 'camel', 'sync', 'attr'])
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-bind` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-bind.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-cloak.js b/lib/rules/valid-v-cloak.js
index efcaf6940..5ff956818 100644
--- a/lib/rules/valid-v-cloak.js
+++ b/lib/rules/valid-v-cloak.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-cloak` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-cloak.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-else-if.js b/lib/rules/valid-v-else-if.js
index c2c0c8851..b94437466 100644
--- a/lib/rules/valid-v-else-if.js
+++ b/lib/rules/valid-v-else-if.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-else-if` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-else-if.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-else.js b/lib/rules/valid-v-else.js
index 7c4bd28aa..f35ac661d 100644
--- a/lib/rules/valid-v-else.js
+++ b/lib/rules/valid-v-else.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-else` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-else.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-for.js b/lib/rules/valid-v-for.js
index 0acf7dba2..05183dd7e 100644
--- a/lib/rules/valid-v-for.js
+++ b/lib/rules/valid-v-for.js
@@ -5,16 +5,8 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
/**
* Check whether the given attribute is using the variables which are defined by `v-for` directives.
* @param {VDirective} vFor The attribute node of `v-for` to check.
@@ -95,16 +87,12 @@ function checkKey(context, vFor, element) {
}
}
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-for` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-for.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-html.js b/lib/rules/valid-v-html.js
index be39026d0..1df254033 100644
--- a/lib/rules/valid-v-html.js
+++ b/lib/rules/valid-v-html.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-html` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-html.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-if.js b/lib/rules/valid-v-if.js
index 7ac4287b3..2e8651b44 100644
--- a/lib/rules/valid-v-if.js
+++ b/lib/rules/valid-v-if.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-if` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-if.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-is.js b/lib/rules/valid-v-is.js
index 15cde7431..2f97d1fd9 100644
--- a/lib/rules/valid-v-is.js
+++ b/lib/rules/valid-v-is.js
@@ -4,16 +4,8 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
/**
* Check whether the given node is valid or not.
* @param {VElement} node The element node to check.
@@ -30,10 +22,6 @@ function isValidElement(node) {
return true
}
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
diff --git a/lib/rules/valid-v-memo.js b/lib/rules/valid-v-memo.js
index e3c85fcf4..66ba9a75d 100644
--- a/lib/rules/valid-v-memo.js
+++ b/lib/rules/valid-v-memo.js
@@ -4,16 +4,8 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
@@ -85,31 +77,40 @@ module.exports = {
const expressions = [node.value.expression]
let expression
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
+ }
}
}
}
diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js
index eb8b63b2b..ca2830dc6 100644
--- a/lib/rules/valid-v-model.js
+++ b/lib/rules/valid-v-model.js
@@ -5,16 +5,8 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])
/**
@@ -57,6 +49,10 @@ function isOptionalChainingMemberExpression(node) {
* @returns {boolean} `true` if the node can be LHS.
*/
function isLhs(node) {
+ if (node.type === 'TSAsExpression' || node.type === 'TSNonNullExpression') {
+ return isLhs(node.expression)
+ }
+
return node.type === 'Identifier' || node.type === 'MemberExpression'
}
@@ -114,17 +110,13 @@ function getVariable(name, leafNode) {
return null
}
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
/** @type {RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-model` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-model.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-on.js b/lib/rules/valid-v-on.js
index 864a5ccf3..82612d496 100644
--- a/lib/rules/valid-v-on.js
+++ b/lib/rules/valid-v-on.js
@@ -5,17 +5,9 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
const keyAliases = require('../utils/key-aliases.json')
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
const VALID_MODIFIERS = new Set([
'stop',
'prevent',
@@ -56,9 +48,9 @@ function isValidModifier(modifierNode, customModifiers) {
// built-in aliases
VALID_MODIFIERS.has(modifier) ||
// 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)
KEY_ALIASES.has(modifier) ||
// custom modifiers
@@ -66,16 +58,12 @@ function isValidModifier(modifierNode, customModifiers) {
)
}
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-on` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-on.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-once.js b/lib/rules/valid-v-once.js
index bacf83304..9d3d8bf8f 100644
--- a/lib/rules/valid-v-once.js
+++ b/lib/rules/valid-v-once.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-once` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-once.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-pre.js b/lib/rules/valid-v-pre.js
index 0112ebde3..0505b9cb5 100644
--- a/lib/rules/valid-v-pre.js
+++ b/lib/rules/valid-v-pre.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-pre` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-pre.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-show.js b/lib/rules/valid-v-show.js
index 59ba9c432..1da617806 100644
--- a/lib/rules/valid-v-show.js
+++ b/lib/rules/valid-v-show.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-show` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-show.html'
},
fixable: null,
diff --git a/lib/rules/valid-v-slot.js b/lib/rules/valid-v-slot.js
index df0fa7c65..d5c0efd1e 100644
--- a/lib/rules/valid-v-slot.js
+++ b/lib/rules/valid-v-slot.js
@@ -100,7 +100,7 @@ function filterSameSlot(
return true
})
)
- .filter((slots) => slots.length >= 1)
+ .filter((slots) => slots.length > 0)
}
/**
@@ -142,14 +142,11 @@ function equalVSlotVForVariables(a, b, tokenStore) {
return false
}
}
- 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)
+ )
/**
* Determines whether the two given nodes are considered to be equal.
@@ -176,7 +173,7 @@ function getVSlotVForVariableIfUsingIterationVars(vSlot, vFor) {
vFor && vFor.value && /** @type {VForExpression} */ (vFor.value.expression)
const variables =
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
}
/**
@@ -235,8 +232,8 @@ function isUsingScopeVar(vSlot) {
*/
function hasInvalidModifiers(vSlot, allowModifiers) {
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
}
module.exports = {
@@ -244,7 +241,7 @@ module.exports = {
type: 'problem',
docs: {
description: 'enforce valid `v-slot` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-slot.html'
},
fixable: null,
@@ -281,8 +278,8 @@ module.exports = {
create(context) {
const sourceCode = context.getSourceCode()
const tokenStore =
- context.parserServices.getTemplateBodyTokenStore &&
- context.parserServices.getTemplateBodyTokenStore()
+ sourceCode.parserServices.getTemplateBodyTokenStore &&
+ sourceCode.parserServices.getTemplateBodyTokenStore()
const options = context.options[0] || {}
const allowModifiers = options.allowModifiers === true
@@ -317,7 +314,7 @@ module.exports = {
messageId: 'namedSlotMustBeOnTemplate'
})
}
- if (ownerElement === element && vSlotGroupsOnChildren.length >= 1) {
+ if (ownerElement === element && vSlotGroupsOnChildren.length > 0) {
context.report({
node,
messageId: 'defaultSlotMustBeOnTemplate'
diff --git a/lib/rules/valid-v-text.js b/lib/rules/valid-v-text.js
index 1e93c14e0..b41e7f2b3 100644
--- a/lib/rules/valid-v-text.js
+++ b/lib/rules/valid-v-text.js
@@ -5,22 +5,14 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const utils = require('../utils')
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `v-text` directives',
- categories: ['vue3-essential', 'essential'],
+ categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/valid-v-text.html'
},
fixable: null,
diff --git a/lib/utils/casing.js b/lib/utils/casing.js
index b2f2e89b5..a47e978cd 100644
--- a/lib/utils/casing.js
+++ b/lib/utils/casing.js
@@ -1,7 +1,3 @@
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
/**
* Capitalize a string.
* @param {string} str
@@ -41,15 +37,12 @@ function kebabCase(str) {
* @param {string} str
*/
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)
+ )
}
/**
@@ -69,10 +62,7 @@ function snakeCase(str) {
* @param {string} str
*/
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)
}
/**
@@ -92,14 +82,7 @@ function camelCase(str) {
* @param {string} str
*/
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)
}
/**
@@ -116,14 +99,7 @@ function pascalCase(str) {
* @param {string} str
*/
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)
}
const convertersMap = {
diff --git a/lib/utils/comments.js b/lib/utils/comments.js
new file mode 100644
index 000000000..d285e7cac
--- /dev/null
+++ b/lib/utils/comments.js
@@ -0,0 +1,21 @@
+/**
+ * @param {Comment} node
+ * @returns {boolean}
+ */
+const isJSDocComment = (node) =>
+ node.type === 'Block' &&
+ node.value.charAt(0) === '*' &&
+ node.value.charAt(1) !== '*'
+
+/**
+ * @param {Comment} node
+ * @returns {boolean}
+ */
+const isBlockComment = (node) =>
+ node.type === 'Block' &&
+ (node.value.charAt(0) !== '*' || node.value.charAt(1) === '*')
+
+module.exports = {
+ isJSDocComment,
+ isBlockComment
+}
diff --git a/lib/utils/deprecated-html-elements.json b/lib/utils/deprecated-html-elements.json
index daf23f512..63a3f7162 100644
--- a/lib/utils/deprecated-html-elements.json
+++ b/lib/utils/deprecated-html-elements.json
@@ -1 +1,31 @@
-["acronym","applet","basefont","bgsound","big","blink","center","command","content","dir","element","font","frame","frameset","image","isindex","keygen","listing","marquee","menuitem","multicol","nextid","nobr","noembed","noframes","plaintext","shadow","spacer","strike","tt","xmp"]
\ No newline at end of file
+[
+ "acronym",
+ "applet",
+ "basefont",
+ "bgsound",
+ "big",
+ "blink",
+ "center",
+ "dir",
+ "font",
+ "frame",
+ "frameset",
+ "isindex",
+ "keygen",
+ "listing",
+ "marquee",
+ "menuitem",
+ "multicol",
+ "nextid",
+ "nobr",
+ "noembed",
+ "noframes",
+ "param",
+ "plaintext",
+ "rb",
+ "rtc",
+ "spacer",
+ "strike",
+ "tt",
+ "xmp"
+]
diff --git a/lib/utils/html-comments.js b/lib/utils/html-comments.js
index ec957af71..53f7df5ae 100644
--- a/lib/utils/html-comments.js
+++ b/lib/utils/html-comments.js
@@ -10,19 +10,11 @@
* @typedef { Token & { type: 'HTMLCommentCloseDecoration' } } HTMLCommentCloseDecoration
* @typedef { { open: HTMLCommentOpen, openDecoration: HTMLCommentOpenDecoration | null, value: HTMLCommentValue | null, closeDecoration: HTMLCommentCloseDecoration | null, close: HTMLCommentClose } } ParsedHTMLComment
*/
-// -----------------------------------------------------------------------------
-// Requirements
-// -----------------------------------------------------------------------------
-
const utils = require('./')
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-
const COMMENT_DIRECTIVE = /^\s*eslint-(?:en|dis)able/
const IE_CONDITIONAL_IF = /^\[if\s+/
-const IE_CONDITIONAL_ENDIF = /\[endif\]$/
+const IE_CONDITIONAL_ENDIF = /\[endif]$/
/** @type { 'HTMLCommentOpen' } */
const TYPE_HTML_COMMENT_OPEN = 'HTMLCommentOpen'
diff --git a/lib/utils/html-elements.json b/lib/utils/html-elements.json
index 721f7876d..ff9cdf313 100644
--- a/lib/utils/html-elements.json
+++ b/lib/utils/html-elements.json
@@ -1 +1,116 @@
-["html","body","base","head","link","meta","style","title","address","article","aside","footer","header","h1","h2","h3","h4","h5","h6","hgroup","nav","section","div","dd","dl","dt","figcaption","figure","hr","img","li","main","ol","p","pre","ul","a","b","abbr","bdi","bdo","br","cite","code","data","dfn","em","i","kbd","mark","q","rp","rt","rtc","ruby","s","samp","small","span","strong","sub","sup","time","u","var","wbr","area","audio","map","track","video","embed","object","param","source","canvas","script","noscript","del","ins","caption","col","colgroup","table","thead","tbody","tfoot","td","th","tr","button","datalist","fieldset","form","input","label","legend","meter","optgroup","option","output","progress","select","textarea","details","dialog","menu","menuitem","summary","content","element","shadow","template","slot","blockquote","iframe","noframes","picture"]
+[
+ "a",
+ "abbr",
+ "address",
+ "area",
+ "article",
+ "aside",
+ "audio",
+ "b",
+ "base",
+ "bdi",
+ "bdo",
+ "blockquote",
+ "body",
+ "br",
+ "button",
+ "canvas",
+ "caption",
+ "cite",
+ "code",
+ "col",
+ "colgroup",
+ "data",
+ "datalist",
+ "dd",
+ "del",
+ "details",
+ "dfn",
+ "dialog",
+ "div",
+ "dl",
+ "dt",
+ "em",
+ "embed",
+ "fencedframe",
+ "fieldset",
+ "figcaption",
+ "figure",
+ "footer",
+ "form",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "head",
+ "header",
+ "hgroup",
+ "hr",
+ "html",
+ "i",
+ "iframe",
+ "img",
+ "input",
+ "ins",
+ "kbd",
+ "label",
+ "legend",
+ "li",
+ "link",
+ "main",
+ "map",
+ "mark",
+ "menu",
+ "meta",
+ "meter",
+ "nav",
+ "noscript",
+ "object",
+ "ol",
+ "optgroup",
+ "option",
+ "output",
+ "p",
+ "picture",
+ "pre",
+ "progress",
+ "q",
+ "rp",
+ "rt",
+ "ruby",
+ "s",
+ "samp",
+ "script",
+ "search",
+ "section",
+ "select",
+ "selectedcontent",
+ "slot",
+ "small",
+ "source",
+ "span",
+ "strong",
+ "style",
+ "sub",
+ "summary",
+ "sup",
+ "table",
+ "tbody",
+ "td",
+ "template",
+ "textarea",
+ "tfoot",
+ "th",
+ "thead",
+ "time",
+ "title",
+ "tr",
+ "track",
+ "u",
+ "ul",
+ "var",
+ "video",
+ "wbr"
+]
diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js
index 39ef58670..d2879b120 100644
--- a/lib/utils/indent-common.js
+++ b/lib/utils/indent-common.js
@@ -4,10 +4,6 @@
*/
'use strict'
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
const {
isArrowToken,
isOpeningParenToken,
@@ -19,8 +15,9 @@ const {
isNotOpeningBraceToken,
isOpeningBracketToken,
isClosingBracketToken,
- isSemicolonToken
-} = require('eslint-utils')
+ isSemicolonToken,
+ isNotSemicolonToken
+} = require('@eslint-community/eslint-utils')
const {
isComment,
isNotComment,
@@ -38,17 +35,14 @@ const { defineVisitor: tsDefineVisitor } = require('./indent-ts')
* @typedef { { type: string } & HasLocation } MaybeNode
*/
-// ------------------------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------------------------
-const LT_CHAR = /[\r\n\u2028\u2029]/
-const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g
+const LT_CHAR = /[\n\r\u2028\u2029]/
+const LINES = /[^\n\r\u2028\u2029]+(?:$|\r\n|[\n\r\u2028\u2029])/g
const BLOCK_COMMENT_PREFIX = /^\s*\*/
const ITERATION_OPTS = Object.freeze({
includeComments: true,
filter: isNotWhitespace
})
-const PREFORMATTED_ELEMENT_NAMES = ['pre', 'textarea']
+const PREFORMATTED_ELEMENT_NAMES = new Set(['pre', 'textarea'])
/**
* @typedef {object} IndentOptions
@@ -300,7 +294,7 @@ module.exports.defineVisitor = function create(
tokenStore.getTokenAfter(node)
/** @type {SourceCode.CursorWithSkipOptions} */
- const option = {
+ const cursorOptions = {
includeComments: true,
filter: (token) =>
token != null &&
@@ -310,11 +304,11 @@ module.exports.defineVisitor = function create(
token.type === 'HTMLEndTagOpen' ||
token.type === 'HTMLComment')
}
- for (const token of tokenStore.getTokensBetween(
- node.startTag,
- endToken,
- option
- )) {
+ const contentTokens = endToken
+ ? tokenStore.getTokensBetween(node.startTag, endToken, cursorOptions)
+ : tokenStore.getTokensAfter(node.startTag, cursorOptions)
+
+ for (const token of contentTokens) {
ignoreTokens.add(token)
}
ignoreTokens.add(endToken)
@@ -328,7 +322,7 @@ module.exports.defineVisitor = function create(
* @returns {{firstToken:Token,lastToken:Token}} The gotten tokens.
*/
function getFirstAndLastTokens(node, borderOffset = 0) {
- borderOffset |= 0
+ borderOffset = Math.trunc(borderOffset)
let firstToken = tokenStore.getFirstToken(node)
let lastToken = tokenStore.getLastToken(node)
@@ -365,21 +359,20 @@ module.exports.defineVisitor = function create(
const leftToken = left && tokenStore.getFirstToken(left)
const rightToken = right && tokenStore.getFirstToken(right)
- if (nodeList.length >= 1) {
+ if (nodeList.length > 0) {
let baseToken = null
let lastToken = left
const alignTokensBeforeBaseToken = []
const alignTokens = []
- for (let i = 0; i < nodeList.length; ++i) {
- const node = nodeList[i]
+ for (const node of nodeList) {
if (node == null) {
// Holes of an array.
continue
}
const elementTokens = getFirstAndLastTokens(
node,
- lastToken != null ? lastToken.range[1] : 0
+ lastToken == null ? 0 : lastToken.range[1]
)
// Collect comma/comment tokens between the last token of the previous node and the first token of this node.
@@ -585,15 +578,15 @@ module.exports.defineVisitor = function create(
function processTopLevelNode(node, expectedIndent) {
const token = tokenStore.getFirstToken(node)
const offsetInfo = offsets.get(token)
- if (offsetInfo != null) {
- offsetInfo.expectedIndent = expectedIndent
- } else {
+ if (offsetInfo == null) {
offsets.set(token, {
baseToken: null,
offset: 0,
baseline: false,
expectedIndent
})
+ } else {
+ offsetInfo.expectedIndent = expectedIndent
}
}
@@ -642,14 +635,11 @@ module.exports.defineVisitor = function create(
function getExpectedIndents(tokens) {
const expectedIndents = []
- for (let i = 0; i < tokens.length; ++i) {
- const token = tokens[i]
+ for (const [i, token] of tokens.entries()) {
const offsetInfo = offsets.get(token)
if (offsetInfo != null) {
- if (offsetInfo.expectedIndent != null) {
- expectedIndents.push(offsetInfo.expectedIndent)
- } else {
+ if (offsetInfo.expectedIndent == null) {
const baseOffsetInfo = offsets.get(offsetInfo.baseToken)
if (
baseOffsetInfo != null &&
@@ -664,16 +654,18 @@ module.exports.defineVisitor = function create(
break
}
}
+ } else {
+ expectedIndents.push(offsetInfo.expectedIndent)
}
}
}
- if (!expectedIndents.length) {
+ if (expectedIndents.length === 0) {
return null
}
return {
expectedIndent: expectedIndents[0],
- expectedBaseIndent: expectedIndents.reduce((a, b) => Math.min(a, b))
+ expectedBaseIndent: Math.min(...expectedIndents)
}
}
@@ -750,8 +742,8 @@ module.exports.defineVisitor = function create(
const actualIndent = token.loc.start.column
const unit = options.indentChar === '\t' ? 'tab' : 'space'
- for (let i = 0; i < indentText.length; ++i) {
- if (indentText[i] !== options.indentChar) {
+ for (const [i, char] of [...indentText].entries()) {
+ if (char !== options.indentChar) {
context.report({
loc: {
start: { line, column: i },
@@ -761,7 +753,7 @@ module.exports.defineVisitor = function create(
'Expected {{expected}} character, but found {{actual}} character.',
data: {
expected: JSON.stringify(options.indentChar),
- actual: JSON.stringify(indentText[i])
+ actual: JSON.stringify(char)
},
fix: defineFix(token, actualIndent, expectedIndent)
})
@@ -870,17 +862,15 @@ module.exports.defineVisitor = function create(
if (offsetInfo != null) {
if (offsetInfo.baseline) {
// This is a baseline token, so the expected indent is the column of this token.
- if (options.indentChar === ' ') {
- offsetInfo.expectedIndent = Math.max(
- 0,
- token.loc.start.column + expectedBaseIndent - actualIndent
- )
- } else {
- // In hard-tabs mode, it cannot align tokens strictly, so use one additional offset.
- // But the additional offset isn't needed if it's at the beginning of the line.
- offsetInfo.expectedIndent =
- expectedBaseIndent + (token === tokens[0] ? 0 : 1)
- }
+ offsetInfo.expectedIndent =
+ options.indentChar === ' '
+ ? Math.max(
+ 0,
+ token.loc.start.column + expectedBaseIndent - actualIndent
+ )
+ : // In hard-tabs mode, it cannot align tokens strictly, so use one additional offset.
+ // But the additional offset isn't needed if it's at the beginning of the line.
+ expectedBaseIndent + (token === tokens[0] ? 0 : 1)
baseline.add(token)
} else if (baseline.has(offsetInfo.baseToken)) {
// The base token is a baseline token on this line, so inherit it.
@@ -953,7 +943,12 @@ module.exports.defineVisitor = function create(
},
/** @param {VElement} node */
VElement(node) {
- if (!PREFORMATTED_ELEMENT_NAMES.includes(node.name)) {
+ if (PREFORMATTED_ELEMENT_NAMES.has(node.name)) {
+ const startTagToken = tokenStore.getFirstToken(node)
+ const endTagToken = node.endTag && tokenStore.getFirstToken(node.endTag)
+ setOffset(endTagToken, 0, startTagToken)
+ setPreformattedTokens(node)
+ } else {
const isTopLevel = node.parent.type !== 'VElement'
const offset = isTopLevel ? options.baseIndent : 1
processNodeList(
@@ -963,11 +958,6 @@ module.exports.defineVisitor = function create(
offset,
false
)
- } else {
- const startTagToken = tokenStore.getFirstToken(node)
- const endTagToken = node.endTag && tokenStore.getFirstToken(node.endTag)
- setOffset(endTagToken, 0, startTagToken)
- setPreformattedTokens(node)
}
},
/** @param {VEndTag} node */
@@ -1060,9 +1050,9 @@ module.exports.defineVisitor = function create(
)
if (closeToken != null && closeToken.type.endsWith('TagClose')) {
const offset =
- closeToken.type !== 'HTMLSelfClosingTagClose'
- ? options.closeBracket.startTag
- : options.closeBracket.selfClosingTag
+ closeToken.type === 'HTMLSelfClosingTagClose'
+ ? options.closeBracket.selfClosingTag
+ : options.closeBracket.startTag
setOffset(closeToken, offset, openToken)
}
},
@@ -1178,14 +1168,23 @@ module.exports.defineVisitor = function create(
},
/** @param {CallExpression} node */
CallExpression(node) {
+ const typeArguments =
+ 'typeArguments' in node ? node.typeArguments : node.typeParameters
const firstToken = tokenStore.getFirstToken(node)
const rightToken = tokenStore.getLastToken(node)
const leftToken = /** @type {Token} */ (
- tokenStore.getTokenAfter(node.callee, isOpeningParenToken)
+ tokenStore.getTokenAfter(
+ typeArguments || node.callee,
+ isOpeningParenToken
+ )
)
+ if (typeArguments) {
+ setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken)
+ }
+
for (const optionalToken of tokenStore.getTokensBetween(
- tokenStore.getLastToken(node.callee),
+ tokenStore.getLastToken(typeArguments || node.callee),
leftToken,
isOptionalToken
)) {
@@ -1288,24 +1287,42 @@ module.exports.defineVisitor = function create(
},
/** @param {ExportAllDeclaration} node */
ExportAllDeclaration(node) {
- const tokens = tokenStore.getTokens(node)
- const firstToken = /** @type {Token} */ (tokens.shift())
- if (isSemicolonToken(tokens[tokens.length - 1])) {
- tokens.pop()
- }
- if (!node.exported) {
- setOffset(tokens, 1, firstToken)
- } else {
+ const exportToken = tokenStore.getFirstToken(node)
+ const tokens = [
+ ...tokenStore.getTokensBetween(exportToken, node.source),
+ tokenStore.getFirstToken(node.source)
+ ]
+ if (node.exported) {
// export * as foo from "mod"
const starToken = /** @type {Token} */ (tokens.find(isWildcard))
const asToken = tokenStore.getTokenAfter(starToken)
const exportedToken = tokenStore.getTokenAfter(asToken)
const afterTokens = tokens.slice(tokens.indexOf(exportedToken) + 1)
- setOffset(starToken, 1, firstToken)
+ setOffset(starToken, 1, exportToken)
setOffset(asToken, 1, starToken)
setOffset(exportedToken, 1, starToken)
- setOffset(afterTokens, 1, firstToken)
+ setOffset(afterTokens, 1, exportToken)
+ } else {
+ setOffset(tokens, 1, exportToken)
+ }
+
+ // assertions
+ const lastToken = /** @type {Token} */ (
+ tokenStore.getLastToken(node, isNotSemicolonToken)
+ )
+ const assertionTokens = tokenStore.getTokensBetween(
+ node.source,
+ lastToken
+ )
+ if (assertionTokens.length > 0) {
+ const assertToken = /** @type {Token} */ (assertionTokens.shift())
+ setOffset(assertToken, 0, exportToken)
+ const assertionOpen = assertionTokens.shift()
+ if (assertionOpen) {
+ setOffset(assertionOpen, 1, assertToken)
+ processNodeList(assertionTokens, assertionOpen, lastToken, 1)
+ }
}
},
/** @param {ExportDefaultDeclaration} node */
@@ -1328,28 +1345,66 @@ module.exports.defineVisitor = function create(
const firstSpecifier = node.specifiers[0]
if (!firstSpecifier || firstSpecifier.type === 'ExportSpecifier') {
// export {foo, bar}; or export {foo, bar} from "mod";
- const leftParenToken = tokenStore.getFirstToken(node, 1)
- const rightParenToken = /** @type {Token} */ (
- tokenStore.getLastToken(node, isClosingBraceToken)
+ const leftBraceTokens = firstSpecifier
+ ? tokenStore.getTokensBetween(exportToken, firstSpecifier)
+ : [tokenStore.getTokenAfter(exportToken)]
+ const rightBraceToken = /** @type {Token} */ (
+ node.source
+ ? tokenStore.getTokenBefore(node.source, isClosingBraceToken)
+ : tokenStore.getLastToken(node, isClosingBraceToken)
)
- setOffset(leftParenToken, 0, exportToken)
- processNodeList(node.specifiers, leftParenToken, rightParenToken, 1)
-
- const maybeFromToken = tokenStore.getTokenAfter(rightParenToken)
- if (maybeFromToken != null && maybeFromToken.value === 'from') {
- const fromToken = maybeFromToken
- const nameToken = tokenStore.getTokenAfter(fromToken)
- setOffset([fromToken, nameToken], 1, exportToken)
+ setOffset(leftBraceTokens, 0, exportToken)
+ processNodeList(
+ node.specifiers,
+ /** @type {Token} */ (last(leftBraceTokens)),
+ rightBraceToken,
+ 1
+ )
+
+ if (node.source) {
+ const tokens = tokenStore.getTokensBetween(
+ rightBraceToken,
+ node.source
+ )
+ setOffset(
+ [...tokens, sourceCode.getFirstToken(node.source)],
+ 1,
+ exportToken
+ )
+
+ // assertions
+ const lastToken = /** @type {Token} */ (
+ tokenStore.getLastToken(node, isNotSemicolonToken)
+ )
+ const assertionTokens = tokenStore.getTokensBetween(
+ node.source,
+ lastToken
+ )
+ if (assertionTokens.length > 0) {
+ const assertToken = /** @type {Token} */ (assertionTokens.shift())
+ setOffset(assertToken, 0, exportToken)
+ const assertionOpen = assertionTokens.shift()
+ if (assertionOpen) {
+ setOffset(assertionOpen, 1, assertToken)
+ processNodeList(assertionTokens, assertionOpen, lastToken, 1)
+ }
+ }
}
} else {
// maybe babel parser
}
}
},
- /** @param {ExportSpecifier} node */
- ExportSpecifier(node) {
+ /** @param {ExportSpecifier | ImportSpecifier} node */
+ 'ExportSpecifier, ImportSpecifier'(node) {
const tokens = tokenStore.getTokens(node)
- const firstToken = /** @type {Token} */ (tokens.shift())
+ let firstToken = /** @type {Token} */ (tokens.shift())
+ if (firstToken.value === 'type') {
+ const typeToken = firstToken
+ firstToken = /** @type {Token} */ (tokens.shift())
+ setOffset(firstToken, 0, typeToken)
+ }
+
setOffset(tokens, 1, firstToken)
},
/** @param {ForInStatement | ForOfStatement} node */
@@ -1476,8 +1531,13 @@ module.exports.defineVisitor = function create(
const tokens = tokenStore.getTokensBetween(importToken, node.source)
const fromIndex = tokens.map((t) => t.value).lastIndexOf('from')
const { fromToken, beforeTokens, afterTokens } =
- fromIndex >= 0
+ fromIndex === -1
? {
+ fromToken: null,
+ beforeTokens: [...tokens, tokenStore.getFirstToken(node.source)],
+ afterTokens: []
+ }
+ : {
fromToken: tokens[fromIndex],
beforeTokens: tokens.slice(0, fromIndex),
afterTokens: [
@@ -1485,11 +1545,6 @@ module.exports.defineVisitor = function create(
tokenStore.getFirstToken(node.source)
]
}
- : {
- fromToken: null,
- beforeTokens: [...tokens, tokenStore.getFirstToken(node.source)],
- afterTokens: []
- }
/** @type {ImportSpecifier[]} */
const namedSpecifiers = []
@@ -1501,13 +1556,13 @@ module.exports.defineVisitor = function create(
removeTokens.shift()
for (const token of removeTokens) {
const i = beforeTokens.indexOf(token)
- if (i >= 0) {
+ if (i !== -1) {
beforeTokens.splice(i, 1)
}
}
}
}
- if (namedSpecifiers.length) {
+ if (namedSpecifiers.length > 0) {
const leftBrace = tokenStore.getTokenBefore(namedSpecifiers[0])
const rightBrace = /** @type {Token} */ (
tokenStore.getTokenAfter(
@@ -1516,12 +1571,12 @@ module.exports.defineVisitor = function create(
)
)
processNodeList(namedSpecifiers, leftBrace, rightBrace, 1)
- for (const token of tokenStore.getTokensBetween(
- leftBrace,
+ for (const token of [
+ ...tokenStore.getTokensBetween(leftBrace, rightBrace),
rightBrace
- )) {
+ ]) {
const i = beforeTokens.indexOf(token)
- if (i >= 0) {
+ if (i !== -1) {
beforeTokens.splice(i, 1)
}
}
@@ -1540,13 +1595,23 @@ module.exports.defineVisitor = function create(
setOffset(fromToken, 1, importToken)
setOffset(afterTokens, 0, fromToken)
}
- },
- /** @param {ImportSpecifier} node */
- ImportSpecifier(node) {
- if (node.local.range[0] !== node.imported.range[0]) {
- const tokens = tokenStore.getTokens(node)
- const firstToken = /** @type {Token} */ (tokens.shift())
- setOffset(tokens, 1, firstToken)
+
+ // assertions
+ const lastToken = /** @type {Token} */ (
+ tokenStore.getLastToken(node, isNotSemicolonToken)
+ )
+ const assertionTokens = tokenStore.getTokensBetween(
+ node.source,
+ lastToken
+ )
+ if (assertionTokens.length > 0) {
+ const assertToken = /** @type {Token} */ (assertionTokens.shift())
+ setOffset(assertToken, 0, importToken)
+ const assertionOpen = assertionTokens.shift()
+ if (assertionOpen) {
+ setOffset(assertionOpen, 1, assertToken)
+ processNodeList(assertionTokens, assertionOpen, lastToken, 1)
+ }
}
},
/** @param {ImportNamespaceSpecifier} node */
@@ -1631,17 +1696,23 @@ module.exports.defineVisitor = function create(
},
/** @param {NewExpression} node */
NewExpression(node) {
+ const typeArguments =
+ 'typeArguments' in node ? 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.callee,
+ typeArguments || node.callee,
rightToken,
isOpeningParenToken
)
: null
+ if (typeArguments) {
+ setOffset(tokenStore.getFirstToken(typeArguments), 1, calleeToken)
+ }
+
setOffset(calleeToken, 1, newToken)
if (leftToken != null) {
setOffset(leftToken, 1, calleeToken)
@@ -1665,7 +1736,11 @@ module.exports.defineVisitor = function create(
SwitchCase(node) {
const caseToken = tokenStore.getFirstToken(node)
- if (node.test != null) {
+ if (node.test == null) {
+ const colonToken = tokenStore.getTokenAfter(caseToken)
+
+ setOffset(colonToken, 1, caseToken)
+ } else {
const testToken = tokenStore.getTokenAfter(caseToken)
const colonToken = tokenStore.getTokenAfter(
node.test,
@@ -1673,10 +1748,6 @@ module.exports.defineVisitor = function create(
)
setOffset([testToken, colonToken], 1, caseToken)
- } else {
- const colonToken = tokenStore.getTokenAfter(caseToken)
-
- setOffset(colonToken, 1, caseToken)
}
if (
@@ -1684,7 +1755,7 @@ module.exports.defineVisitor = function create(
node.consequent[0].type === 'BlockStatement'
) {
setOffset(tokenStore.getFirstToken(node.consequent[0]), 0, caseToken)
- } else if (node.consequent.length >= 1) {
+ } else if (node.consequent.length > 0) {
setOffset(tokenStore.getFirstToken(node.consequent[0]), 1, caseToken)
processNodeList(node.consequent, null, null, 0)
}
@@ -1926,7 +1997,7 @@ module.exports.defineVisitor = function create(
comments = []
}
}
- if (tokensOnSameLine.length >= 1 && tokensOnSameLine.some(isNotComment)) {
+ if (tokensOnSameLine.some(isNotComment)) {
validate(tokensOnSameLine, comments, lastValidatedToken)
}
}
diff --git a/lib/utils/indent-ts.js b/lib/utils/indent-ts.js
index b98e9d105..314858c9a 100644
--- a/lib/utils/indent-ts.js
+++ b/lib/utils/indent-ts.js
@@ -11,8 +11,8 @@ const {
isNotClosingParenToken,
isClosingBracketToken,
isOpeningBracketToken
-} = require('eslint-utils')
-const { isTypeNode } = require('./ts-ast-utils')
+} = require('@eslint-community/eslint-utils')
+const { isTypeNode } = require('./ts-utils')
/**
* @typedef {import('../../typings/eslint-plugin-vue/util-types/indent-helper').TSNodeListener} TSNodeListener
@@ -29,6 +29,7 @@ const { isTypeNode } = require('./ts-ast-utils')
* @typedef {import('@typescript-eslint/types').TSESTree.TSImportEqualsDeclaration} TSImportEqualsDeclaration
* @typedef {import('@typescript-eslint/types').TSESTree.TSAbstractMethodDefinition} TSAbstractMethodDefinition
* @typedef {import('@typescript-eslint/types').TSESTree.TSAbstractPropertyDefinition} TSAbstractPropertyDefinition
+ * @typedef {import('@typescript-eslint/types').TSESTree.TSAbstractAccessorProperty} TSAbstractAccessorProperty
* @typedef {import('@typescript-eslint/types').TSESTree.TSEnumMember} TSEnumMember
* @typedef {import('@typescript-eslint/types').TSESTree.TSPropertySignature} TSPropertySignature
* @typedef {import('@typescript-eslint/types').TSESTree.TSIndexSignature} TSIndexSignature
@@ -50,6 +51,10 @@ const { isTypeNode } = require('./ts-ast-utils')
* @typedef {import('@typescript-eslint/types').TSESTree.TSInferType} TSInferType
* @typedef {import('@typescript-eslint/types').TSESTree.TSOptionalType} TSOptionalType
* @typedef {import('@typescript-eslint/types').TSESTree.TSNonNullExpression} TSNonNullExpression
+ * @typedef {import('@typescript-eslint/types').TSESTree.TSAsExpression} TSAsExpression
+ * @typedef {import('@typescript-eslint/types').TSESTree.TSSatisfiesExpression} TSSatisfiesExpression
+ * @typedef {import('@typescript-eslint/types').TSESTree.TSTypeReference} TSTypeReference
+ * @typedef {import('@typescript-eslint/types').TSESTree.TSInstantiationExpression} TSInstantiationExpression
* @typedef {import('@typescript-eslint/types').TSESTree.JSXChild} JSXChild
* @typedef {import('@typescript-eslint/types').TSESTree.TypeNode} TypeNode
*
@@ -191,7 +196,7 @@ function defineVisitor({
tokenStore.getFirstToken(node.superClass)
)
}
- if (node.implements != null && node.implements.length) {
+ if (node.implements != null && node.implements.length > 0) {
const classToken = tokenStore.getFirstToken(node)
const implementsToken = tokenStore.getTokenBefore(node.implements[0])
setOffset(implementsToken, 1, classToken)
@@ -206,24 +211,24 @@ function defineVisitor({
* | TSImportEqualsDeclaration
* | TSAbstractMethodDefinition
* | TSAbstractPropertyDefinition
- * | TSAbstractClassProperty
+ * | TSAbstractAccessorProperty
* | TSEnumMember
- * | ClassProperty
* | TSPropertySignature
* | TSIndexSignature
- * | TSMethodSignature} node
+ * | TSMethodSignature
+ * | ClassProperty
+ * | TSAbstractClassProperty} node
*/
['TSTypeAliasDeclaration, TSCallSignatureDeclaration, TSConstructSignatureDeclaration, TSImportEqualsDeclaration,' +
- 'TSAbstractMethodDefinition, TSAbstractPropertyDefinition, TSEnumMember,' +
+ 'TSAbstractMethodDefinition, TSAbstractPropertyDefinition, TSAbstractAccessorProperty, TSEnumMember,' +
'TSPropertySignature, TSIndexSignature, TSMethodSignature,' +
// Deprecated in @typescript-eslint/parser v5
'ClassProperty, TSAbstractClassProperty'](node) {
processSemicolons(node)
},
/**
- * @param {TSESTreeNode} node
+ * @param {ASTNode} node
*/
- // eslint-disable-next-line complexity -- ignore
'*[type=/^TS/]'(node) {
if (!isTypeNode(node)) {
return
@@ -279,40 +284,61 @@ function defineVisitor({
}
},
/**
- * Process as expression
+ * Process as expression or satisfies expression
*
* e.g.
* ```
* var foo = bar as boolean
* // ^^^^^^^^^^^^^^
* ```
+ *
+ * e.g.
+ * ```
+ * var foo = bar satisfies Bar
+ * // ^^^^^^^^^^^^^^^^^
+ * ```
+ *
+ * @param {TSAsExpression | TSSatisfiesExpression} node
*/
- TSAsExpression(node) {
+ 'TSAsExpression, TSSatisfiesExpression'(node) {
const expressionTokens = getFirstAndLastTokens(node.expression)
- const asToken = tokenStore.getTokenAfter(expressionTokens.lastToken)
+ const asOrSatisfiesToken = tokenStore.getTokenAfter(
+ expressionTokens.lastToken
+ )
setOffset(
- [asToken, getFirstAndLastTokens(node.typeAnnotation).firstToken],
+ [
+ asOrSatisfiesToken,
+ getFirstAndLastTokens(node.typeAnnotation).firstToken
+ ],
1,
expressionTokens.firstToken
)
},
/**
- * Process type reference
+ * Process type reference and instantiation expression
*
* e.g.
* ```
* const foo: Type
* // ^^^^^^^
* ```
+ *
+ * e.g.
+ * ```
+ * const ErrorMap = Map;
+ * // ^^^^^^^^^^^^^^^^^^
+ * ```
+ *
+ * @param {TSTypeReference | TSInstantiationExpression} node
*/
- TSTypeReference(node) {
- if (node.typeParameters) {
- const typeNameTokens = getFirstAndLastTokens(node.typeName)
- setOffset(
- tokenStore.getFirstToken(node.typeParameters),
- 1,
- typeNameTokens.firstToken
- )
+ 'TSTypeReference, TSInstantiationExpression'(node) {
+ const typeArguments =
+ 'typeArguments' in node
+ ? node.typeArguments
+ : /** @type {any} typescript-eslint v5 */ (node).typeParameters
+ if (typeArguments) {
+ const firstToken = tokenStore.getFirstToken(node)
+ setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken)
}
},
/**
@@ -637,7 +663,7 @@ function defineVisitor({
const [, ...removeTokens] = tokenStore.getTokens(child)
for (const token of removeTokens) {
const i = afterTokens.indexOf(token)
- if (i >= 0) {
+ if (i !== -1) {
afterTokens.splice(i, 1)
}
}
@@ -723,7 +749,7 @@ function defineVisitor({
tokenStore.getFirstToken(node.id)
)
}
- if (node.extends != null && node.extends.length) {
+ if (node.extends != null && node.extends.length > 0) {
const extendsToken = tokenStore.getTokenBefore(node.extends[0])
setOffset(extendsToken, 1, interfaceToken)
processNodeList(node.extends, extendsToken, null, 1)
@@ -1053,10 +1079,10 @@ function defineVisitor({
* // ^^^^^^^
* ```
*
- * @param {TSAbstractMethodDefinition | TSAbstractPropertyDefinition | TSEnumMember | TSAbstractClassProperty | ClassProperty} node
+ * @param {TSAbstractMethodDefinition | TSAbstractPropertyDefinition | TSAbstractAccessorProperty | TSEnumMember | TSAbstractClassProperty | ClassProperty} node
*
*/
- ['TSAbstractMethodDefinition, TSAbstractPropertyDefinition, TSEnumMember,' +
+ ['TSAbstractMethodDefinition, TSAbstractPropertyDefinition, TSAbstractAccessorProperty, TSEnumMember,' +
// Deprecated in @typescript-eslint/parser v5
'ClassProperty, TSAbstractClassProperty'](node) {
const { keyNode, valueNode } =
@@ -1146,18 +1172,25 @@ function defineVisitor({
isOpeningParenToken
)
setOffset(leftParenToken, 1, firstToken)
+ const argument =
+ node.argument ||
+ /** @type {any} typescript-eslint v5 */ (node).parameter
const rightParenToken = tokenStore.getTokenAfter(
- node.parameter,
+ argument,
isClosingParenToken
)
- processNodeList([node.parameter], leftParenToken, rightParenToken, 1)
+ processNodeList([argument], leftParenToken, rightParenToken, 1)
if (node.qualifier) {
const dotToken = tokenStore.getTokenBefore(node.qualifier)
const propertyToken = tokenStore.getTokenAfter(dotToken)
setOffset([dotToken, propertyToken], 1, firstToken)
}
- if (node.typeParameters) {
- setOffset(tokenStore.getFirstToken(node.typeParameters), 1, firstToken)
+ const typeArguments =
+ 'typeArguments' in node
+ ? node.typeArguments
+ : /** @type {any} typescript-eslint v5 */ (node).typeParameters
+ if (typeArguments) {
+ setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken)
}
},
TSParameterProperty(node) {
@@ -1277,12 +1310,12 @@ function defineVisitor({
})
setOffset(secondToken, 0, atToken)
const parent = /** @type {any} */ (node.parent)
- const { decorators } = parent
+ const { decorators, range } = parent
if (!decorators || decorators.length === 0) {
return
}
if (decorators[0] === node) {
- if (parent.range[0] === node.range[0]) {
+ if (range[0] === node.range[0]) {
const startParentToken = tokenStore.getTokenAfter(
decorators[decorators.length - 1]
)
@@ -1302,6 +1335,63 @@ function defineVisitor({
setOffset(atToken, 0, tokenStore.getFirstToken(decorators[0]))
}
},
+ AccessorProperty(node) {
+ const keyNode = node.key
+ const valueNode = node.value
+ const firstToken = tokenStore.getFirstToken(node)
+ const keyTokens = getFirstAndLastTokens(keyNode)
+ const prefixTokens = tokenStore.getTokensBetween(
+ firstToken,
+ keyTokens.firstToken
+ )
+ if (node.computed) {
+ prefixTokens.pop() // pop opening bracket character (`[`)
+ }
+ setOffset(prefixTokens, 0, firstToken)
+ let lastKeyToken
+ if (node.computed) {
+ const leftBracketToken = tokenStore.getTokenBefore(keyTokens.firstToken)
+ const rightBracketToken = (lastKeyToken = tokenStore.getTokenAfter(
+ keyTokens.lastToken
+ ))
+ setOffset(leftBracketToken, 0, firstToken)
+ processNodeList([keyNode], leftBracketToken, rightBracketToken, 1)
+ } else {
+ setOffset(keyTokens.firstToken, 0, firstToken)
+ lastKeyToken = keyTokens.lastToken
+ }
+
+ if (valueNode != null) {
+ const initToken = tokenStore.getFirstToken(valueNode)
+ setOffset(
+ [...tokenStore.getTokensBetween(lastKeyToken, initToken), initToken],
+ 1,
+ lastKeyToken
+ )
+ }
+ processSemicolons(node)
+ },
+ ImportAttribute(node) {
+ const firstToken = tokenStore.getFirstToken(node)
+ const keyTokens = getFirstAndLastTokens(node.key)
+ const prefixTokens = tokenStore.getTokensBetween(
+ firstToken,
+ keyTokens.firstToken
+ )
+ setOffset(prefixTokens, 0, firstToken)
+
+ setOffset(keyTokens.firstToken, 0, firstToken)
+
+ const initToken = tokenStore.getFirstToken(node.value)
+ setOffset(
+ [
+ ...tokenStore.getTokensBetween(keyTokens.lastToken, initToken),
+ initToken
+ ],
+ 1,
+ keyTokens.lastToken
+ )
+ },
// ----------------------------------------------------------------------
// DEPRECATED NODES
diff --git a/lib/utils/index.js b/lib/utils/index.js
index d6c3a6f5a..769362966 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -5,6 +5,8 @@
*/
'use strict'
+const { getScope } = require('./scope')
+
/**
* @typedef {import('eslint').Rule.RuleModule} RuleModule
* @typedef {import('estree').Position} Position
@@ -15,9 +17,21 @@
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentArrayProp} ComponentArrayProp
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentObjectProp} ComponentObjectProp
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeProp} ComponentTypeProp
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentInferTypeProp} ComponentInferTypeProp
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentUnknownProp} ComponentUnknownProp
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentProp} ComponentProp
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentArrayEmit} ComponentArrayEmit
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentObjectEmit} ComponentObjectEmit
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeEmit} ComponentTypeEmit
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentInferTypeEmit} ComponentInferTypeEmit
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentUnknownEmit} ComponentUnknownEmit
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentEmit} ComponentEmit
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeSlot} ComponentTypeSlot
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentInferTypeSlot} ComponentInferTypeSlot
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentUnknownSlot} ComponentUnknownSlot
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentSlot} ComponentSlot
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentModelName} ComponentModelName
+ * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentModel} ComponentModel
*/
/**
* @typedef { {key: string | null, value: BlockStatement | null} } ComponentComputedProperty
@@ -41,6 +55,7 @@
const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json'))
const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json'))
+const MATH_ELEMENT_NAMES = new Set(require('./math-elements.json'))
const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json'))
const VUE2_BUILTIN_COMPONENT_NAMES = new Set(
require('./vue2-builtin-components')
@@ -48,15 +63,20 @@ 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 } = vueEslintParser.AST
-const { findVariable } = require('eslint-utils')
+const { traverseNodes, getFallbackKeys, NS } = vueEslintParser.AST
+const {
+ findVariable,
+ ReferenceTracker
+} = require('@eslint-community/eslint-utils')
const {
getComponentPropsFromTypeDefine,
getComponentEmitsFromTypeDefine,
+ getComponentSlotsFromTypeDefine,
isTypeNode
-} = require('./ts-ast-utils')
+} = require('./ts-utils')
/**
* @type { WeakMap }
@@ -64,15 +84,95 @@ const {
const componentComments = new WeakMap()
/** @type { Map | null } */
-let ruleMap = null
+let coreRuleMap = null
+/** @type { Map } */
+const stylisticRuleMap = new Map()
/**
* Get the core rule implementation from the rule name
* @param {string} name
* @returns {RuleModule | null}
*/
function getCoreRule(name) {
- const map = ruleMap || (ruleMap = new (require('eslint').Linter)().getRules())
- return map.get(name) || null
+ const eslint = require('eslint')
+ try {
+ const map = coreRuleMap || (coreRuleMap = new eslint.Linter().getRules())
+ return map.get(name) || null
+ } catch {
+ // getRules() is no longer available in flat config.
+ }
+
+ const { builtinRules } = require('eslint/use-at-your-own-risk')
+ return /** @type {any} */ (builtinRules.get(name) || null)
+}
+/**
+ * Get ESLint Stylistic rule implementation from the rule name
+ * @param {string} name
+ * @param {'@stylistic/eslint-plugin' | '@stylistic/eslint-plugin-ts' | '@stylistic/eslint-plugin-js'} [preferModule]
+ * @returns {RuleModule | null}
+ */
+function getStylisticRule(name, preferModule) {
+ if (!preferModule) {
+ const cached = stylisticRuleMap.get(name)
+ if (cached) {
+ return cached
+ }
+ }
+ const stylisticPluginNames = [
+ '@stylistic/eslint-plugin',
+ '@stylistic/eslint-plugin-ts',
+ '@stylistic/eslint-plugin-js'
+ ]
+ if (preferModule) {
+ stylisticPluginNames.unshift(preferModule)
+ }
+ for (const stylisticPluginName of stylisticPluginNames) {
+ try {
+ const plugin = createRequire(`${process.cwd()}/__placeholder__.js`)(
+ stylisticPluginName
+ )
+ const rule = plugin?.rules?.[name]
+ if (!preferModule) stylisticRuleMap.set(name, rule)
+ return rule
+ } catch {
+ // ignore
+ }
+ }
+ return null
+}
+
+/**
+ * @template {object} T
+ * @param {T} target
+ * @param {Partial[]} propsArray
+ * @returns {T}
+ */
+function newProxy(target, ...propsArray) {
+ const result = new Proxy(
+ {},
+ {
+ get(_object, key) {
+ for (const props of propsArray) {
+ if (key in props) {
+ // @ts-expect-error
+ return props[key]
+ }
+ }
+ // @ts-expect-error
+ return target[key]
+ },
+
+ has(_object, key) {
+ return key in target
+ },
+ ownKeys(_object) {
+ return Reflect.ownKeys(target)
+ },
+ getPrototypeOf(_object) {
+ return Reflect.getPrototypeOf(target)
+ }
+ }
+ )
+ return /** @type {T} */ (result)
}
/**
@@ -86,8 +186,8 @@ function getCoreRule(name) {
function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
const eslintSourceCode = context.getSourceCode()
const rootNode = options.applyDocument
- ? context.parserServices.getDocumentFragment &&
- context.parserServices.getDocumentFragment()
+ ? eslintSourceCode.parserServices.getDocumentFragment &&
+ eslintSourceCode.parserServices.getDocumentFragment()
: eslintSourceCode.ast.templateBody
/** @type {Token[] | null} */
let tokensAndComments = null
@@ -143,23 +243,25 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
})
return result
}
- const sourceCode = new Proxy(Object.assign({}, eslintSourceCode), {
- get(_object, key) {
- if (key === 'tokensAndComments') {
+ const sourceCode = newProxy(
+ eslintSourceCode,
+ {
+ get tokensAndComments() {
return getTokensAndComments()
- }
- if (key === 'getNodeByRangeIndex') {
- return getNodeByRangeIndex
- }
- // @ts-expect-error
- return key in tokenStore ? tokenStore[key] : eslintSourceCode[key]
- }
- })
+ },
+ getNodeByRangeIndex,
+ // @ts-expect-error -- Added in ESLint v8.38.0
+ getDeclaredVariables
+ },
+ tokenStore
+ )
+ /** @type {WeakMap} */
const containerScopes = new WeakMap()
/**
* @param {ASTNode} node
+ * @returns {import('eslint').Scope.ScopeManager|null}
*/
function getContainerScope(node) {
const exprContainer = getVExpressionContainer(node)
@@ -171,31 +273,23 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
return cache
}
const programNode = eslintSourceCode.ast
- const parserOptions = context.parserOptions || {}
+ const parserOptions =
+ context.languageOptions?.parserOptions ?? context.parserOptions ?? {}
const ecmaFeatures = parserOptions.ecmaFeatures || {}
- const ecmaVersion = parserOptions.ecmaVersion || 2020
+ const ecmaVersion =
+ context.languageOptions?.ecmaVersion ?? parserOptions.ecmaVersion ?? 2020
const sourceType = programNode.sourceType
try {
const eslintScope = createRequire(require.resolve('eslint'))(
'eslint-scope'
)
- const expStmt = new Proxy(exprContainer, {
- get(_object, key) {
- if (key === 'type') {
- return 'ExpressionStatement'
- }
- // @ts-expect-error
- return exprContainer[key]
- }
+ const expStmt = newProxy(exprContainer, {
+ // @ts-expect-error
+ type: 'ExpressionStatement'
})
- const scopeProgram = new Proxy(programNode, {
- get(_object, key) {
- if (key === 'body') {
- return [expStmt]
- }
- // @ts-expect-error
- return programNode[key]
- }
+ const scopeProgram = newProxy(programNode, {
+ // @ts-expect-error
+ body: [expStmt]
})
const scope = eslintScope.analyze(scopeProgram, {
ignoreEval: true,
@@ -207,27 +301,34 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
})
containerScopes.set(exprContainer, scope)
return scope
- } catch (e) {
+ } catch (error) {
// ignore
- // console.log(e)
+ // console.log(error)
}
return null
}
- return {
- // @ts-expect-error
- __proto__: context,
+ return newProxy(context, {
getSourceCode() {
return sourceCode
},
- getDeclaredVariables(node) {
- const scope = getContainerScope(node)
- if (scope) {
- return scope.getDeclaredVariables(node)
- }
+ get sourceCode() {
+ return sourceCode
+ },
+ getDeclaredVariables
+ })
- return context.getDeclaredVariables(node)
- }
+ /**
+ * @param {ESNode} node
+ * @returns {Variable[]}
+ */
+ function getDeclaredVariables(node) {
+ const scope = getContainerScope(node)
+ return (
+ scope?.getDeclaredVariables?.(node) ??
+ context.getDeclaredVariables?.(node) ??
+ []
+ )
}
}
@@ -244,7 +345,6 @@ function wrapContextToOverrideReportMethodToSkipDynamicArgument(context) {
}
/** @type {Range[]} */
const directiveKeyRanges = []
- const traverseNodes = vueEslintParser.AST.traverseNodes
traverseNodes(templateBody, {
enterNode(node, parent) {
if (
@@ -258,9 +358,7 @@ function wrapContextToOverrideReportMethodToSkipDynamicArgument(context) {
leaveNode() {}
})
- return {
- // @ts-expect-error
- __proto__: context,
+ return newProxy(context, {
report(descriptor, ...args) {
let range = null
if (descriptor.loc) {
@@ -285,6 +383,267 @@ function wrapContextToOverrideReportMethodToSkipDynamicArgument(context) {
}
context.report(descriptor, ...args)
}
+ })
+}
+
+/**
+ * @callback WrapRuleCreate
+ * @param {RuleContext} ruleContext
+ * @param {WrapRuleCreateContext} wrapContext
+ * @returns {TemplateListener}
+ *
+ * @typedef {object} WrapRuleCreateContext
+ * @property {RuleListener} baseHandlers
+ */
+/**
+ * @callback WrapRulePreprocess
+ * @param {RuleContext} ruleContext
+ * @param {WrapRulePreprocessContext} wrapContext
+ * @returns {void}
+ *
+ * @typedef {object} WrapRulePreprocessContext
+ * @property { (override: Partial) => RuleContext } wrapContextToOverrideProperties Wrap the rule context object to override
+ * @property { (visitor: TemplateListener) => void } defineVisitor Define template body visitor
+ */
+/**
+ * @typedef {object} WrapRuleOptions
+ * @property {string[]} [categories] The categories of this rule.
+ * @property {boolean} [skipDynamicArguments] If `true`, skip validation within dynamic arguments.
+ * @property {boolean} [skipDynamicArgumentsReport] If `true`, skip report within dynamic arguments.
+ * @property {boolean} [applyDocument] If `true`, apply check to document fragment.
+ * @property {boolean} [skipBaseHandlers] If `true`, skip base rule handlers.
+ * @property {WrapRulePreprocess} [preprocess] Preprocess to calling create of base rule.
+ * @property {WrapRuleCreate} [create] If define, extend base rule.
+ */
+/**
+ * Wrap a given core rule to apply it to Vue.js template.
+ * @param {string} coreRuleName The name of the core rule implementation to wrap.
+ * @param {WrapRuleOptions} [options] The option of this rule.
+ * @returns {RuleModule} The wrapped rule implementation.
+ */
+function wrapCoreRule(coreRuleName, options) {
+ const coreRule = getCoreRule(coreRuleName)
+ if (!coreRule) {
+ return {
+ meta: {
+ type: 'problem',
+ docs: {
+ url: `https://eslint.vuejs.org/rules/${coreRuleName}.html`
+ }
+ },
+ create(context) {
+ return defineTemplateBodyVisitor(context, {
+ "VElement[name='template'][parent.type='VDocumentFragment']"(node) {
+ context.report({
+ node,
+ message: `Failed to extend ESLint core rule "${coreRuleName}". You may be able to use this rule by upgrading the version of ESLint. If you cannot upgrade it, turn off this rule.`
+ })
+ }
+ })
+ }
+ }
+ }
+ const rule = wrapRuleModule(coreRule, coreRuleName, options)
+ const meta = {
+ ...rule.meta,
+ docs: {
+ ...rule.meta.docs,
+ extensionSource: {
+ url: coreRule.meta.docs.url,
+ name: 'ESLint core'
+ }
+ }
+ }
+ return {
+ ...rule,
+ meta
+ }
+}
+/**
+ * @typedef {object} RuleNames
+ * @property {string} core The name of the core rule implementation to wrap.
+ * @property {string} stylistic The name of ESLint Stylistic rule implementation to wrap.
+ * @property {string} vue The name of the wrapped rule
+ */
+/**
+ * Wrap a core rule or ESLint Stylistic rule to apply it to Vue.js template.
+ * @param {RuleNames|string} ruleNames The names of the rule implementation to wrap.
+ * @param {WrapRuleOptions} [options] The option of this rule.
+ * @returns {RuleModule} The wrapped rule implementation.
+ */
+function wrapStylisticOrCoreRule(ruleNames, options) {
+ const stylisticRuleName =
+ typeof ruleNames === 'string' ? ruleNames : ruleNames.stylistic
+ const coreRuleName =
+ typeof ruleNames === 'string' ? ruleNames : ruleNames.core
+ const vueRuleName = typeof ruleNames === 'string' ? ruleNames : ruleNames.vue
+ const stylisticRule = getStylisticRule(stylisticRuleName)
+ const baseRule = stylisticRule || getCoreRule(coreRuleName)
+ if (!baseRule) {
+ return {
+ meta: {
+ type: 'problem',
+ docs: {
+ url: `https://eslint.vuejs.org/rules/${vueRuleName}.html`
+ }
+ },
+ create(context) {
+ return defineTemplateBodyVisitor(context, {
+ "VElement[name='template'][parent.type='VDocumentFragment']"(node) {
+ context.report({
+ node,
+ message: `Failed to extend ESLint Stylistic rule "${stylisticRule}". You may be able to use this rule by installing ESLint Stylistic plugin (https://eslint.style/). If you cannot install it, turn off this rule.`
+ })
+ }
+ })
+ }
+ }
+ }
+ const rule = wrapRuleModule(baseRule, vueRuleName, options)
+ const jsRule = getStylisticRule(
+ stylisticRuleName,
+ '@stylistic/eslint-plugin-js'
+ )
+ const description = stylisticRule
+ ? `${jsRule?.meta.docs.description} in \`\``
+ : rule.meta.docs.description
+ const meta = {
+ ...rule.meta,
+ docs: {
+ ...rule.meta.docs,
+ description,
+ extensionSource: {
+ url: baseRule.meta.docs.url,
+ name: stylisticRule ? 'ESLint Stylistic' : 'ESLint core'
+ }
+ },
+ deprecated: undefined,
+ replacedBy: undefined
+ }
+ return {
+ ...rule,
+ meta
+ }
+}
+/**
+ * Wrap a given rule to apply it to Vue.js template.
+ * @param {RuleModule} baseRule The rule implementation to wrap.
+ * @param {string} ruleName The name of the wrapped rule.
+ * @param {WrapRuleOptions} [options] The option of this rule.
+ * @returns {RuleModule} The wrapped rule implementation.
+ */
+function wrapRuleModule(baseRule, ruleName, options) {
+ let description = baseRule.meta.docs.description
+ if (description) {
+ description += ' in ``'
+ }
+
+ const {
+ categories,
+ skipDynamicArguments,
+ skipDynamicArgumentsReport,
+ skipBaseHandlers,
+ applyDocument,
+ preprocess,
+ create
+ } = options || {}
+ return {
+ create(context) {
+ const sourceCode = context.getSourceCode()
+ const tokenStore =
+ sourceCode.parserServices.getTemplateBodyTokenStore &&
+ sourceCode.parserServices.getTemplateBodyTokenStore()
+
+ // The `context.getSourceCode()` cannot access the tokens of templates.
+ // So override the methods which access to tokens by the `tokenStore`.
+ if (tokenStore) {
+ context = wrapContextToOverrideTokenMethods(context, tokenStore, {
+ applyDocument
+ })
+ }
+
+ if (skipDynamicArgumentsReport) {
+ context =
+ wrapContextToOverrideReportMethodToSkipDynamicArgument(context)
+ }
+
+ /** @type {TemplateListener} */
+ const handlers = {}
+
+ if (preprocess) {
+ preprocess(context, {
+ wrapContextToOverrideProperties(override) {
+ context = newProxy(context, override)
+ return context
+ },
+ defineVisitor(visitor) {
+ compositingVisitors(handlers, visitor)
+ }
+ })
+ }
+
+ const baseHandlers = baseRule.create(context)
+ if (!skipBaseHandlers) {
+ compositingVisitors(handlers, baseHandlers)
+ }
+
+ // Move `Program` handlers to `VElement[parent.type!='VElement']`
+ if (handlers.Program) {
+ handlers[
+ applyDocument
+ ? 'VDocumentFragment'
+ : "VElement[parent.type!='VElement']"
+ ] = /** @type {any} */ (handlers.Program)
+ delete handlers.Program
+ }
+ if (handlers['Program:exit']) {
+ handlers[
+ applyDocument
+ ? 'VDocumentFragment:exit'
+ : "VElement[parent.type!='VElement']:exit"
+ ] = /** @type {any} */ (handlers['Program:exit'])
+ delete handlers['Program:exit']
+ }
+
+ if (skipDynamicArguments) {
+ let withinDynamicArguments = false
+ for (const name of Object.keys(handlers)) {
+ const original = handlers[name]
+ /** @param {any[]} args */
+ handlers[name] = (...args) => {
+ if (withinDynamicArguments) return
+ // @ts-expect-error
+ original(...args)
+ }
+ }
+ handlers['VDirectiveKey > VExpressionContainer'] = () => {
+ withinDynamicArguments = true
+ }
+ handlers['VDirectiveKey > VExpressionContainer:exit'] = () => {
+ withinDynamicArguments = false
+ }
+ }
+
+ if (create) {
+ compositingVisitors(handlers, create(context, { baseHandlers }))
+ }
+
+ if (applyDocument) {
+ // Apply the handlers to document.
+ return defineDocumentVisitor(context, handlers)
+ }
+ // Apply the handlers to templates.
+ return defineTemplateBodyVisitor(context, handlers)
+ },
+
+ meta: Object.assign({}, baseRule.meta, {
+ docs: Object.assign({}, baseRule.meta.docs, {
+ description,
+ category: null,
+ categories,
+ url: `https://eslint.vuejs.org/rules/${ruleName}.html`
+ })
+ })
}
}
@@ -317,133 +676,13 @@ module.exports = {
* @returns {RuleListener} The merged visitor.
*/
defineDocumentVisitor,
-
/**
* Wrap a given core rule to apply it to Vue.js template.
- * @param {string} coreRuleName The name of the core rule implementation to wrap.
- * @param {Object} [options] The option of this rule.
- * @param {string[]} [options.categories] The categories of this rule.
- * @param {boolean} [options.skipDynamicArguments] If `true`, skip validation within dynamic arguments.
- * @param {boolean} [options.skipDynamicArgumentsReport] If `true`, skip report within dynamic arguments.
- * @param {boolean} [options.applyDocument] If `true`, apply check to document fragment.
- * @param { (context: RuleContext, options: { coreHandlers: RuleListener }) => TemplateListener } [options.create] If define, extend core rule.
- * @returns {RuleModule} The wrapped rule implementation.
- */
- wrapCoreRule(coreRuleName, options) {
- const coreRule = getCoreRule(coreRuleName)
- if (!coreRule) {
- return {
- meta: {
- type: 'problem',
- docs: {
- url: `https://eslint.vuejs.org/rules/${coreRuleName}.html`
- }
- },
- create(context) {
- return defineTemplateBodyVisitor(context, {
- "VElement[name='template'][parent.type='VDocumentFragment']"(node) {
- context.report({
- node,
- message: `Failed to extend ESLint core rule "${coreRuleName}". You may be able to use this rule by upgrading the version of ESLint. If you cannot upgrade it, turn off this rule.`
- })
- }
- })
- }
- }
- }
- const {
- categories,
- skipDynamicArguments,
- skipDynamicArgumentsReport,
- applyDocument,
- create
- } = options || {}
- return {
- create(context) {
- const tokenStore =
- context.parserServices.getTemplateBodyTokenStore &&
- context.parserServices.getTemplateBodyTokenStore()
-
- // The `context.getSourceCode()` cannot access the tokens of templates.
- // So override the methods which access to tokens by the `tokenStore`.
- if (tokenStore) {
- context = wrapContextToOverrideTokenMethods(context, tokenStore, {
- applyDocument
- })
- }
-
- if (skipDynamicArgumentsReport) {
- context =
- wrapContextToOverrideReportMethodToSkipDynamicArgument(context)
- }
-
- // Move `Program` handlers to `VElement[parent.type!='VElement']`
- const coreHandlers = coreRule.create(context)
-
- const handlers = /** @type {TemplateListener} */ (
- Object.assign({}, coreHandlers)
- )
- if (handlers.Program) {
- handlers[
- applyDocument
- ? 'VDocumentFragment'
- : "VElement[parent.type!='VElement']"
- ] = /** @type {any} */ (handlers.Program)
- delete handlers.Program
- }
- if (handlers['Program:exit']) {
- handlers[
- applyDocument
- ? 'VDocumentFragment:exit'
- : "VElement[parent.type!='VElement']:exit"
- ] = /** @type {any} */ (handlers['Program:exit'])
- delete handlers['Program:exit']
- }
-
- if (skipDynamicArguments) {
- let withinDynamicArguments = false
- for (const name of Object.keys(handlers)) {
- const original = handlers[name]
- /** @param {any[]} args */
- handlers[name] = (...args) => {
- if (withinDynamicArguments) return
- // @ts-expect-error
- original(...args)
- }
- }
- handlers['VDirectiveKey > VExpressionContainer'] = () => {
- withinDynamicArguments = true
- }
- handlers['VDirectiveKey > VExpressionContainer:exit'] = () => {
- withinDynamicArguments = false
- }
- }
-
- if (create) {
- compositingVisitors(handlers, create(context, { coreHandlers }))
- }
-
- if (applyDocument) {
- // Apply the handlers to document.
- return defineDocumentVisitor(context, handlers)
- }
- // Apply the handlers to templates.
- return defineTemplateBodyVisitor(context, handlers)
- },
-
- meta: Object.assign({}, coreRule.meta, {
- docs: Object.assign({}, coreRule.meta.docs, {
- category: null,
- categories,
- url: `https://eslint.vuejs.org/rules/${path.basename(
- coreRule.meta.docs.url || ''
- )}.html`,
- extensionRule: true,
- coreRuleUrl: coreRule.meta.docs.url
- })
- })
- }
- },
+ * @type {typeof wrapCoreRule}
+ */
+ wrapCoreRule,
+ wrapStylisticOrCoreRule,
+ getCoreRule,
/**
* Checks whether the given value is defined.
* @template T
@@ -451,6 +690,13 @@ module.exports = {
* @returns {v is T}
*/
isDef,
+ /**
+ * Flattens arrays, objects and iterable objects.
+ * @template T
+ * @param {T | Iterable | null | undefined} v
+ * @returns {T[]}
+ */
+ flatten,
/**
* Get the previous sibling element of the given element.
* @param {VElement} node The element node to get the previous sibling element.
@@ -513,9 +759,9 @@ module.exports = {
if (node.value.expression != null) {
return false
}
-
+ const sourceCode = context.getSourceCode()
const valueNode = node.value
- const tokenStore = context.parserServices.getTemplateBodyTokenStore()
+ const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
let quote1 = null
let quote2 = null
// `node.value` may be only comments, so cannot get the correct tokens with `tokenStore.getTokens(node.value)`.
@@ -594,6 +840,8 @@ module.exports = {
*/
hasDirective,
+ isVBindSameNameShorthand,
+
/**
* Returns the list of all registered components
* @param {ObjectExpression} componentObject
@@ -605,13 +853,10 @@ module.exports = {
* @param {ESNode} p
* @returns {p is (Property & { key: Identifier & {name: 'components'}, value: ObjectExpression })}
*/
- (p) => {
- return (
- p.type === 'Property' &&
- getStaticPropertyName(p) === 'components' &&
- p.value.type === 'ObjectExpression'
- )
- }
+ (p) =>
+ p.type === 'Property' &&
+ getStaticPropertyName(p) === 'components' &&
+ p.value.type === 'ObjectExpression'
)
if (!componentsNode) {
@@ -673,7 +918,7 @@ module.exports = {
if (connected) {
elementChain.push(childNode)
} else {
- if (elementChain.length) {
+ if (elementChain.length > 0) {
yield elementChain
}
elementChain = [childNode]
@@ -682,25 +927,49 @@ module.exports = {
vIf = false
}
}
- if (elementChain.length) {
+ if (elementChain.length > 0) {
yield elementChain
}
},
+ /**
+ * @param {ASTNode} node
+ * @returns {node is Literal | TemplateLiteral}
+ */
+ isStringLiteral(node) {
+ return (
+ (node.type === 'Literal' && typeof node.value === 'string') ||
+ (node.type === 'TemplateLiteral' && node.expressions.length === 0)
+ )
+ },
+
/**
* Check whether the given node is a custom component or not.
* @param {VElement} node The start tag node to check.
+ * @param {boolean} [ignoreElementNamespaces=false] If `true`, ignore element namespaces.
* @returns {boolean} `true` if the node is a custom component.
*/
- isCustomComponent(node) {
- return (
- (this.isHtmlElementNode(node) &&
- !this.isHtmlWellKnownElementName(node.rawName)) ||
- (this.isSvgElementNode(node) &&
- !this.isSvgWellKnownElementName(node.rawName)) ||
+ isCustomComponent(node, ignoreElementNamespaces = false) {
+ if (
hasAttribute(node, 'is') ||
hasDirective(node, 'bind', 'is') ||
hasDirective(node, 'is')
+ ) {
+ return true
+ }
+
+ const isHtmlName = this.isHtmlWellKnownElementName(node.rawName)
+ const isSvgName = this.isSvgWellKnownElementName(node.rawName)
+ const isMathName = this.isMathWellKnownElementName(node.rawName)
+
+ if (ignoreElementNamespaces) {
+ return !isHtmlName && !isSvgName && !isMathName
+ }
+
+ return (
+ (this.isHtmlElementNode(node) && !isHtmlName) ||
+ (this.isSvgElementNode(node) && !isSvgName) ||
+ (this.isMathElementNode(node) && !isMathName)
)
},
@@ -710,7 +979,7 @@ module.exports = {
* @returns {boolean} `true` if the node is a HTML element.
*/
isHtmlElementNode(node) {
- return node.namespace === vueEslintParser.AST.NS.HTML
+ return node.namespace === NS.HTML
},
/**
@@ -719,7 +988,7 @@ module.exports = {
* @returns {boolean} `true` if the name is a SVG element.
*/
isSvgElementNode(node) {
- return node.namespace === vueEslintParser.AST.NS.SVG
+ return node.namespace === NS.SVG
},
/**
@@ -727,8 +996,8 @@ module.exports = {
* @param {VElement} node The node to check.
* @returns {boolean} `true` if the node is a MathML element.
*/
- isMathMLElementNode(node) {
- return node.namespace === vueEslintParser.AST.NS.MathML
+ isMathElementNode(node) {
+ return node.namespace === NS.MathML
},
/**
@@ -749,6 +1018,15 @@ module.exports = {
return SVG_ELEMENT_NAMES.has(name)
},
+ /**
+ * Check whether the given name is a well-known MathML element or not.
+ * @param {string} name The name to check.
+ * @returns {boolean} `true` if the name is a well-known MathML element name.
+ */
+ isMathWellKnownElementName(name) {
+ return MATH_ELEMENT_NAMES.has(name)
+ },
+
/**
* Check whether the given name is a void element name or not.
* @param {string} name The name to check.
@@ -770,6 +1048,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.
@@ -811,58 +1098,15 @@ module.exports = {
/**
* Get all props by looking at all component's properties
* @param {ObjectExpression} componentObject Object with component definition
- * @return {(ComponentArrayProp | ComponentObjectProp)[]} Array of component props in format: [{key?: String, value?: ASTNode, node: ASTNod}]
+ * @return {(ComponentArrayProp | ComponentObjectProp | ComponentUnknownProp)[]} Array of component props
*/
- getComponentProps(componentObject) {
- const propsNode = componentObject.properties.find(
- /**
- * @param {ESNode} p
- * @returns {p is (Property & { key: Identifier & {name: 'props'}, value: ObjectExpression | ArrayExpression })}
- */
- (p) => {
- return (
- p.type === 'Property' &&
- getStaticPropertyName(p) === 'props' &&
- (p.value.type === 'ObjectExpression' ||
- p.value.type === 'ArrayExpression')
- )
- }
- )
-
- if (!propsNode) {
- return []
- }
-
- return getComponentPropsFromDefine(propsNode.value)
- },
-
+ getComponentPropsFromOptions,
/**
* Get all emits by looking at all component's properties
* @param {ObjectExpression} componentObject Object with component definition
- * @return {(ComponentArrayEmit | ComponentObjectEmit)[]} Array of component emits in format: [{key?: String, value?: ASTNode, node: ASTNod}]
+ * @return {(ComponentArrayEmit | ComponentObjectEmit | ComponentUnknownEmit)[]} Array of component emits
*/
- getComponentEmits(componentObject) {
- const emitsNode = componentObject.properties.find(
- /**
- * @param {ESNode} p
- * @returns {p is (Property & { key: Identifier & {name: 'emits'}, value: ObjectExpression | ArrayExpression })}
- */
- (p) => {
- return (
- p.type === 'Property' &&
- getStaticPropertyName(p) === 'emits' &&
- (p.value.type === 'ObjectExpression' ||
- p.value.type === 'ArrayExpression')
- )
- }
- )
-
- if (!emitsNode) {
- return []
- }
-
- return getComponentEmitsFromDefine(emitsNode.value)
- },
+ getComponentEmitsFromOptions,
/**
* Get all computed properties by looking at all component's properties
@@ -875,13 +1119,10 @@ module.exports = {
* @param {ESNode} p
* @returns {p is (Property & { key: Identifier & {name: 'computed'}, value: ObjectExpression })}
*/
- (p) => {
- return (
- p.type === 'Property' &&
- getStaticPropertyName(p) === 'computed' &&
- p.value.type === 'ObjectExpression'
- )
- }
+ (p) =>
+ p.type === 'Property' &&
+ getStaticPropertyName(p) === 'computed' &&
+ p.value.type === 'ObjectExpression'
)
if (!computedPropertiesNode) {
@@ -951,6 +1192,8 @@ module.exports = {
return null
},
+ isTypeScriptFile,
+
isVueFile,
/**
@@ -1103,6 +1346,14 @@ module.exports = {
* - `onDefinePropsExit` ... Event when defineProps visit ends.
* - `onDefineEmitsEnter` ... Event when defineEmits is found.
* - `onDefineEmitsExit` ... Event when defineEmits visit ends.
+ * - `onDefineOptionsEnter` ... Event when defineOptions is found.
+ * - `onDefineOptionsExit` ... Event when defineOptions visit ends.
+ * - `onDefineSlotsEnter` ... Event when defineSlots is found.
+ * - `onDefineSlotsExit` ... Event when defineSlots visit ends.
+ * - `onDefineExposeEnter` ... Event when defineExpose is found.
+ * - `onDefineExposeExit` ... Event when defineExpose visit ends.
+ * - `onDefineModelEnter` ... Event when defineModel is found.
+ * - `onDefineModelExit` ... Event when defineModel visit ends.
*
* @param {RuleContext} context The ESLint rule context object.
* @param {ScriptSetupVisitor} visitor The visitor to traverse the AST nodes.
@@ -1129,11 +1380,9 @@ module.exports = {
* @param {any[]} args
*/
function callVisitor(key, node, ...args) {
- if (visitor[key]) {
- if (inScriptSetup(node)) {
- // @ts-expect-error
- visitor[key](node, ...args)
- }
+ if (visitor[key] && (node.type === 'Program' || inScriptSetup(node))) {
+ // @ts-expect-error
+ visitor[key](node, ...args)
}
}
@@ -1143,40 +1392,72 @@ module.exports = {
scriptSetupVisitor[key] = (node) => callVisitor(key, node)
}
- /**
- * @param {ESNode} node
- * @returns {ObjectExpression | ArrayExpression | null}
- */
- function getObjectOrArray(node) {
- if (node.type === 'ObjectExpression') {
- return node
- }
- if (node.type === 'ArrayExpression') {
- return node
- }
- if (node.type === 'Identifier') {
- const variable = findVariable(context.getScope(), node)
-
- if (variable != null && variable.defs.length === 1) {
- const def = variable.defs[0]
- if (
- def.type === 'Variable' &&
- def.parent.kind === 'const' &&
- def.node.id.type === 'Identifier' &&
- def.node.init
- ) {
- return getObjectOrArray(def.node.init)
- }
- }
- }
- return null
- }
-
- const hasPropsEvent =
- visitor.onDefinePropsEnter || visitor.onDefinePropsExit
- const hasEmitsEvent =
- visitor.onDefineEmitsEnter || visitor.onDefineEmitsExit
- if (hasPropsEvent || hasEmitsEvent) {
+ class MacroListener {
+ /**
+ * @param {string} name
+ * @param {string} enterName
+ * @param {string} exitName
+ * @param {(candidateMacro: Expression | null, node: CallExpression) => boolean} isMacroNode
+ * @param {(context: RuleContext, node: CallExpression) => unknown} buildParam
+ */
+ constructor(name, enterName, exitName, isMacroNode, buildParam) {
+ this.name = name
+ this.enterName = enterName
+ this.exitName = exitName
+ this.isMacroNode = isMacroNode
+ this.buildParam = buildParam
+ this.hasListener = Boolean(
+ visitor[this.enterName] || visitor[this.exitName]
+ )
+ this.paramsMap = new Map()
+ }
+ }
+ const macroListenerList = [
+ new MacroListener(
+ 'defineProps',
+ 'onDefinePropsEnter',
+ 'onDefinePropsExit',
+ (candidateMacro, node) =>
+ candidateMacro === node || candidateMacro === getWithDefaults(node),
+ getComponentPropsFromDefineProps
+ ),
+ new MacroListener(
+ 'defineEmits',
+ 'onDefineEmitsEnter',
+ 'onDefineEmitsExit',
+ (candidateMacro, node) => candidateMacro === node,
+ getComponentEmitsFromDefineEmits
+ ),
+ new MacroListener(
+ 'defineOptions',
+ 'onDefineOptionsEnter',
+ 'onDefineOptionsExit',
+ (candidateMacro, node) => candidateMacro === node,
+ () => undefined
+ ),
+ new MacroListener(
+ 'defineSlots',
+ 'onDefineSlotsEnter',
+ 'onDefineSlotsExit',
+ (candidateMacro, node) => candidateMacro === node,
+ getComponentSlotsFromDefineSlots
+ ),
+ new MacroListener(
+ 'defineExpose',
+ 'onDefineExposeEnter',
+ 'onDefineExposeExit',
+ (candidateMacro, node) => candidateMacro === node,
+ () => undefined
+ ),
+ new MacroListener(
+ 'defineModel',
+ 'onDefineModelEnter',
+ 'onDefineModelExit',
+ (candidateMacro, node) => candidateMacro === node,
+ getComponentModelFromDefineModel
+ )
+ ].filter((m) => m.hasListener)
+ if (macroListenerList.length > 0) {
/** @type {Expression | null} */
let candidateMacro = null
/** @param {VariableDeclarator|ExpressionStatement} node */
@@ -1199,8 +1480,6 @@ module.exports = {
candidateMacro = null
}
}
- const definePropsMap = new Map()
- const defineEmitsMap = new Map()
/**
* @param {CallExpression} node
*/
@@ -1210,66 +1489,32 @@ module.exports = {
inScriptSetup(node) &&
node.callee.type === 'Identifier'
) {
- if (
- hasPropsEvent &&
- (candidateMacro === node ||
- candidateMacro === getWithDefaults(node)) &&
- node.callee.name === 'defineProps'
- ) {
- /** @type {(ComponentArrayProp | ComponentObjectProp | ComponentTypeProp)[]} */
- let props = []
- if (node.arguments.length >= 1) {
- const defNode = getObjectOrArray(node.arguments[0])
- if (defNode) {
- props = getComponentPropsFromDefine(defNode)
- }
- } else if (
- node.typeParameters &&
- node.typeParameters.params.length >= 1
- ) {
- props = getComponentPropsFromTypeDefine(
- context,
- node.typeParameters.params[0]
- )
- }
- callVisitor('onDefinePropsEnter', node, props)
- definePropsMap.set(node, props)
- } else if (
- hasEmitsEvent &&
- candidateMacro === node &&
- node.callee.name === 'defineEmits'
- ) {
- /** @type {(ComponentArrayEmit | ComponentObjectEmit | ComponentTypeEmit)[]} */
- let emits = []
- if (node.arguments.length >= 1) {
- const defNode = getObjectOrArray(node.arguments[0])
- if (defNode) {
- emits = getComponentEmitsFromDefine(defNode)
- }
- } else if (
- node.typeParameters &&
- node.typeParameters.params.length >= 1
+ for (const macroListener of macroListenerList) {
+ if (
+ node.callee.name !== macroListener.name ||
+ !macroListener.isMacroNode(candidateMacro, node)
) {
- emits = getComponentEmitsFromTypeDefine(
- context,
- node.typeParameters.params[0]
- )
+ continue
}
- callVisitor('onDefineEmitsEnter', node, emits)
- defineEmitsMap.set(node, emits)
+ const param = macroListener.buildParam(context, node)
+ callVisitor(macroListener.enterName, node, param)
+ macroListener.paramsMap.set(node, param)
+ break
}
}
callVisitor('CallExpression', node)
}
scriptSetupVisitor['CallExpression:exit'] = (node) => {
callVisitor('CallExpression:exit', node)
- if (definePropsMap.has(node)) {
- callVisitor('onDefinePropsExit', node, definePropsMap.get(node))
- definePropsMap.delete(node)
- }
- if (defineEmitsMap.has(node)) {
- callVisitor('onDefineEmitsExit', node, defineEmitsMap.get(node))
- defineEmitsMap.delete(node)
+ for (const macroListener of macroListenerList) {
+ if (macroListener.paramsMap.has(node)) {
+ callVisitor(
+ macroListener.exitName,
+ node,
+ macroListener.paramsMap.get(node)
+ )
+ macroListener.paramsMap.delete(node)
+ }
}
}
}
@@ -1308,13 +1553,35 @@ module.exports = {
* @returns { { [key: string]: Property | undefined } }
*/
getWithDefaultsProps,
+ /**
+ * Gets the default definition nodes for defineProp
+ * using the props destructure with assignment pattern.
+ * @param {CallExpression} node The node of defineProps
+ * @returns { Record }
+ */
+ getDefaultPropExpressionsForPropsDestructure,
+ /**
+ * Checks whether the given defineProps node is using Props Destructure.
+ * @param {CallExpression} node The node of defineProps
+ * @returns {boolean}
+ */
+ isUsingPropsDestructure(node) {
+ const left = getLeftOfDefineProps(node)
+ return left?.type === 'ObjectPattern'
+ },
+ /**
+ * Gets the props destructure property nodes for defineProp.
+ * @param {CallExpression} node The node of defineProps
+ * @returns { Record }
+ */
+ getPropsDestructure,
getVueObjectType,
/**
* Get the Vue component definition type from given node
* Vue.component('xxx', {}) || component('xxx', {})
* @param {ObjectExpression} node Node to check
- * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | null}
+ * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | 'defineNuxtComponent' | null}
*/
getVueComponentDefinitionType,
/**
@@ -1383,7 +1650,7 @@ module.exports = {
calleeObject.type === 'Identifier' &&
// calleeObject.name === 'Vue' && // Any names can be used in Vue.js 3.x. e.g. app.component()
callee.property === node &&
- callExpr.arguments.length >= 1
+ callExpr.arguments.length > 0
) {
cb(callExpr)
}
@@ -1406,14 +1673,23 @@ module.exports = {
const name = /** @type {GroupName | null} */ (getStaticPropertyName(item))
if (!name || !groups.has(name)) continue
- if (item.value.type === 'ArrayExpression') {
- yield* this.iterateArrayExpression(item.value, name)
- } else if (item.value.type === 'ObjectExpression') {
- yield* this.iterateObjectExpression(item.value, name)
- } else if (item.value.type === 'FunctionExpression') {
- yield* this.iterateFunctionExpression(item.value, name)
- } else if (item.value.type === 'ArrowFunctionExpression') {
- yield* this.iterateArrowFunctionExpression(item.value, name)
+ switch (item.value.type) {
+ case 'ArrayExpression': {
+ yield* this.iterateArrayExpression(item.value, name)
+ break
+ }
+ case 'ObjectExpression': {
+ yield* this.iterateObjectExpression(item.value, name)
+ break
+ }
+ case 'FunctionExpression': {
+ yield* this.iterateFunctionExpression(item.value, name)
+ break
+ }
+ case 'ArrowFunctionExpression': {
+ yield* this.iterateArrowFunctionExpression(item.value, name)
+ break
+ }
}
}
},
@@ -1457,29 +1733,28 @@ module.exports = {
) {
const name = getStaticPropertyName(item)
if (name) {
- if (item.kind === 'set') {
- // find getter pair
- if (
- node.properties.some((item2) => {
- if (item2.type === 'Property' && item2.kind === 'get') {
- if (!usedGetter) {
- usedGetter = new Set()
- }
- if (usedGetter.has(item2)) {
- return false
- }
- const getterName = getStaticPropertyName(item2)
- if (getterName === name) {
- usedGetter.add(item2)
- return true
- }
+ // find getter pair
+ if (
+ item.kind === 'set' &&
+ node.properties.some((item2) => {
+ if (item2.type === 'Property' && item2.kind === 'get') {
+ if (!usedGetter) {
+ usedGetter = new Set()
}
- return false
- })
- ) {
- // has getter pair
- continue
- }
+ if (usedGetter.has(item2)) {
+ return false
+ }
+ const getterName = getStaticPropertyName(item2)
+ if (getterName === name) {
+ usedGetter.add(item2)
+ return true
+ }
+ }
+ return false
+ })
+ ) {
+ // has getter pair
+ continue
}
yield {
type: 'object',
@@ -1551,24 +1826,17 @@ module.exports = {
* @property {boolean} hasReturn
* @property {boolean} hasReturnValue
* @property {ArrowFunctionExpression | FunctionExpression | FunctionDeclaration} node
+ * @property {CodePathSegment[]} currentSegments
*/
/** @type {FuncInfo | null} */
let funcInfo = null
- /** @param {CodePathSegment} segment */
- function isReachable(segment) {
- return segment.reachable
- }
-
function isValidReturn() {
if (!funcInfo) {
return true
}
- if (
- funcInfo.codePath &&
- funcInfo.codePath.currentSegments.some(isReachable)
- ) {
+ if (funcInfo.currentSegments.some((segment) => segment.reachable)) {
return false
}
return !treatUndefinedAsUnspecified || funcInfo.hasReturnValue
@@ -1587,6 +1855,7 @@ module.exports = {
) {
funcInfo = {
codePath,
+ currentSegments: [],
funcInfo,
hasReturn: false,
hasReturnValue: false,
@@ -1594,6 +1863,12 @@ module.exports = {
}
}
},
+ onCodePathSegmentStart(segment) {
+ funcInfo?.currentSegments.push(segment)
+ },
+ onCodePathSegmentEnd() {
+ funcInfo?.currentSegments.pop()
+ },
onCodePathEnd() {
funcInfo = funcInfo && funcInfo.funcInfo
},
@@ -1683,11 +1958,10 @@ module.exports = {
}
for (let i = 1; i <= alen; i++) {
for (let j = 1; j <= blen; j++) {
- if (a[i - 1] === b[j - 1]) {
- dp[i][j] = dp[i - 1][j - 1]
- } else {
- dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
- }
+ dp[i][j] =
+ a[i - 1] === b[j - 1]
+ ? dp[i - 1][j - 1]
+ : Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
}
}
return dp[alen][blen]
@@ -1744,6 +2018,10 @@ module.exports = {
withinTypeNode,
findVariableByIdentifier,
getScope,
+ /**
+ * Marks a variable with the given name in the current scope as used. This affects the no-unused-vars rule.
+ */
+ markVariableAsUsed,
/**
* Checks whether the given node is in export default.
* @param {ASTNode} node
@@ -1765,17 +2043,14 @@ module.exports = {
return false
}
const parent = node.parent
- if (parent.type === 'MemberExpression') {
- if (parent.property === node) {
- return false
- }
- } else if (parent.type === 'Property') {
- if (parent.key === node && !parent.computed) {
- return false
- }
+ if (
+ (parent.type === 'MemberExpression' && parent.property === node) ||
+ (parent.type === 'Property' && parent.key === node && !parent.computed)
+ ) {
+ return false
}
- const variable = findVariable(context.getScope(), node)
+ const variable = findVariable(getScope(context, node), node)
if (variable != null && variable.defs.length === 1) {
const def = variable.defs[0]
@@ -1803,52 +2078,71 @@ module.exports = {
let node = props
let target = node.parent
while (true) {
- if (target.type === 'AssignmentExpression') {
- if (target.left === node) {
- // this.xxx <=|+=|-=>
+ switch (target.type) {
+ case 'AssignmentExpression': {
+ if (target.left === node) {
+ // this.xxx <=|+=|-=>
+ return {
+ kind: 'assignment',
+ node: target,
+ pathNodes
+ }
+ }
+ break
+ }
+ case 'UpdateExpression': {
+ // this.xxx <++|-->
return {
- kind: 'assignment',
+ kind: 'update',
node: target,
pathNodes
}
}
- } else if (target.type === 'UpdateExpression') {
- // this.xxx <++|-->
- return {
- kind: 'update',
- node: target,
- pathNodes
- }
- } else if (target.type === 'CallExpression') {
- if (pathNodes.length > 0 && target.callee === node) {
- const mem = pathNodes[pathNodes.length - 1]
- const callName = getStaticPropertyName(mem)
- if (
- callName &&
- /^(?:push|pop|shift|unshift|reverse|splice|sort|copyWithin|fill)$/u.exec(
- callName
- )
- ) {
- // this.xxx.push()
- pathNodes.pop()
+ case 'UnaryExpression': {
+ if (target.operator === 'delete') {
return {
- kind: 'call',
+ kind: 'update',
node: target,
pathNodes
}
}
+ break
}
- } else if (target.type === 'MemberExpression') {
- if (target.object === node) {
- pathNodes.push(target)
+ case 'CallExpression': {
+ if (pathNodes.length > 0 && target.callee === node) {
+ const mem = pathNodes[pathNodes.length - 1]
+ const callName = getStaticPropertyName(mem)
+ if (
+ callName &&
+ /^(?:push|pop|shift|unshift|reverse|splice|sort|copyWithin|fill)$/u.test(
+ callName
+ )
+ ) {
+ // this.xxx.push()
+ pathNodes.pop()
+ return {
+ kind: 'call',
+ node: target,
+ pathNodes
+ }
+ }
+ }
+ break
+ }
+ case 'MemberExpression': {
+ if (target.object === node) {
+ pathNodes.push(target)
+ node = target
+ target = target.parent
+ continue // loop
+ }
+ break
+ }
+ case 'ChainExpression': {
node = target
target = target.parent
continue // loop
}
- } else if (target.type === 'ChainExpression') {
- node = target
- target = target.parent
- continue // loop
}
return null
@@ -1863,14 +2157,38 @@ module.exports = {
iterateWatchHandlerValues,
/**
- * Wraps composition API trace map in both 'vue' and '@vue/composition-api' imports
- * @param {import('eslint-utils').TYPES.TraceMap} map
+ * Wraps composition API trace map in both 'vue' and '@vue/composition-api' imports, or '#imports' from unimport
+ * @param {import('@eslint-community/eslint-utils').TYPES.TraceMap} map
*/
createCompositionApiTraceMap: (map) => ({
vue: map,
- '@vue/composition-api': map
+ '@vue/composition-api': map,
+ '#imports': map
}),
+ /**
+ * Iterates all references in the given trace map.
+ * Take the third argument option to detect auto-imported references.
+ *
+ * @param {import('@eslint-community/eslint-utils').ReferenceTracker} tracker
+ * @param {import('@eslint-community/eslint-utils').TYPES.TraceMap} map
+ * @returns {ReturnType}
+ */
+ *iterateReferencesTraceMap(tracker, map) {
+ const esmTraceMap = this.createCompositionApiTraceMap({
+ ...map,
+ [ReferenceTracker.ESM]: true
+ })
+
+ for (const ref of tracker.iterateEsmReferences(esmTraceMap)) {
+ yield ref
+ }
+
+ for (const ref of tracker.iterateGlobalReferences(map)) {
+ yield ref
+ }
+ },
+
/**
* Checks whether or not the tokens of two given nodes are same.
* @param {ASTNode} left A node 1 to compare.
@@ -1885,16 +2203,11 @@ module.exports = {
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
+ return tokensL.every(
+ (token, i) =>
+ token.type === tokensR[i].type && token.value === tokensR[i].value
+ )
}
}
@@ -1912,6 +2225,33 @@ function isDef(v) {
return v != null
}
+/**
+ * Flattens arrays, objects and iterable objects.
+ * @template T
+ * @param {T | Iterable | null | undefined} v
+ * @returns {T[]}
+ */
+function flatten(v) {
+ /** @type {T[]} */
+ const result = []
+ if (v) {
+ if (isIterable(v)) {
+ result.push(...v)
+ } else {
+ result.push(v)
+ }
+ }
+ return result
+}
+
+/**
+ * @param {*} v
+ * @returns {v is Iterable}
+ */
+function isIterable(v) {
+ return v && Symbol.iterator in v
+}
+
// ------------------------------------------------------------------------------
// Nodejs Helpers
// ------------------------------------------------------------------------------
@@ -1963,7 +2303,8 @@ function defineTemplateBodyVisitor(
scriptVisitor,
options
) {
- if (context.parserServices.defineTemplateBodyVisitor == null) {
+ const sourceCode = context.getSourceCode()
+ if (sourceCode.parserServices.defineTemplateBodyVisitor == null) {
const filename = context.getFilename()
if (path.extname(filename) === '.vue') {
context.report({
@@ -1974,7 +2315,7 @@ function defineTemplateBodyVisitor(
}
return {}
}
- return context.parserServices.defineTemplateBodyVisitor(
+ return sourceCode.parserServices.defineTemplateBodyVisitor(
templateBodyVisitor,
scriptVisitor,
options
@@ -1991,7 +2332,8 @@ function defineTemplateBodyVisitor(
* @returns {RuleListener} The merged visitor.
*/
function defineDocumentVisitor(context, documentVisitor, options) {
- if (context.parserServices.defineDocumentVisitor == null) {
+ const sourceCode = context.getSourceCode()
+ if (sourceCode.parserServices.defineDocumentVisitor == null) {
const filename = context.getFilename()
if (path.extname(filename) === '.vue') {
context.report({
@@ -2002,7 +2344,10 @@ function defineDocumentVisitor(context, documentVisitor, options) {
}
return {}
}
- return context.parserServices.defineDocumentVisitor(documentVisitor, options)
+ return sourceCode.parserServices.defineDocumentVisitor(
+ documentVisitor,
+ options
+ )
}
/**
@@ -2047,33 +2392,6 @@ function findVariableByIdentifier(context, node) {
return findVariable(getScope(context, node), node)
}
-/**
- * Gets the scope for the current node
- * @param {RuleContext} context The rule context
- * @param {ESNode} currentNode The node to get the scope of
- * @returns { import('eslint').Scope.Scope } The scope information for this node
- */
-function getScope(context, currentNode) {
- // On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
- const inner = currentNode.type !== 'Program'
- const scopeManager = context.getSourceCode().scopeManager
-
- /** @type {ESNode | null} */
- let node = currentNode
- for (; node; node = /** @type {ESNode | null} */ (node.parent)) {
- const scope = scopeManager.acquire(node, inner)
-
- if (scope) {
- if (scope.type === 'function-expression-name') {
- return scope.childScopes[0]
- }
- return scope
- }
- }
-
- return scopeManager.scopes[0]
-}
-
/**
* Finds the property with the given name from the given ObjectExpression node.
* @param {ObjectExpression} node
@@ -2321,10 +2639,12 @@ function getStringLiteralValue(node, stringOnly) {
}
return null
}
- if (node.type === 'TemplateLiteral') {
- if (node.expressions.length === 0 && node.quasis.length === 1) {
- return node.quasis[0].value.cooked
- }
+ if (
+ node.type === 'TemplateLiteral' &&
+ node.expressions.length === 0 &&
+ node.quasis.length === 1
+ ) {
+ return node.quasis[0].value.cooked
}
return null
}
@@ -2342,6 +2662,33 @@ function getVExpressionContainer(node) {
return n
}
+/**
+ * @param {string} path
+ */
+function isTypeScriptFile(path) {
+ return path.endsWith('.ts') || path.endsWith('.tsx') || path.endsWith('.mts')
+}
+
+// ------------------------------------------------------------------------------
+// ESLint Helpers
+// ------------------------------------------------------------------------------
+/**
+ * Marks a variable with the given name in the current scope as used. This affects the no-unused-vars rule.
+ * @param {RuleContext} context
+ * @param {string} name
+ * @param {ASTNode} node The node to get the current scope.
+ */
+function markVariableAsUsed(context, name, node) {
+ const sourceCode = context.getSourceCode()
+ if (sourceCode.markVariableAsUsed) {
+ sourceCode.markVariableAsUsed(name, node)
+ } else {
+ // This function does not use the given node, but the currently visited node.
+ // If we need to determine the scope of a given node, we need to implement it yourself.
+ context.markVariableAsUsed?.(name)
+ }
+}
+
// ------------------------------------------------------------------------------
// Vue Helpers
// ------------------------------------------------------------------------------
@@ -2350,7 +2697,7 @@ function getVExpressionContainer(node) {
* @param {string} path
*/
function isVueFile(path) {
- return path.endsWith('.vue') || path.endsWith('.jsx')
+ return path.endsWith('.vue') || path.endsWith('.jsx') || path.endsWith('.tsx')
}
/**
@@ -2366,15 +2713,17 @@ function isScriptSetup(context) {
* @returns {VElement | null} the element of `
+
diff --git a/tests/fixtures/script-indent/jsx-01.vue b/tests/fixtures/script-indent/jsx-01.vue
index 7cd302aa3..d5e73c650 100644
--- a/tests/fixtures/script-indent/jsx-01.vue
+++ b/tests/fixtures/script-indent/jsx-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-abstract-class-property-01.vue b/tests/fixtures/script-indent/ts-abstract-class-property-01.vue
index ec38f5ff3..500b4c493 100644
--- a/tests/fixtures/script-indent/ts-abstract-class-property-01.vue
+++ b/tests/fixtures/script-indent/ts-abstract-class-property-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-accessor-property-02.vue b/tests/fixtures/script-indent/ts-accessor-property-02.vue
new file mode 100644
index 000000000..da749a32e
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-accessor-property-02.vue
@@ -0,0 +1,10 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-accessor-property-03.vue b/tests/fixtures/script-indent/ts-accessor-property-03.vue
new file mode 100644
index 000000000..993e52527
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-accessor-property-03.vue
@@ -0,0 +1,9 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-accessor-property-04.vue b/tests/fixtures/script-indent/ts-accessor-property-04.vue
new file mode 100644
index 000000000..e69e294fc
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-accessor-property-04.vue
@@ -0,0 +1,10 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-accessor-property-05.vue b/tests/fixtures/script-indent/ts-accessor-property-05.vue
new file mode 100644
index 000000000..dc3503ca7
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-accessor-property-05.vue
@@ -0,0 +1,12 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-as-expression-01.vue b/tests/fixtures/script-indent/ts-as-expression-01.vue
index 869101939..377dd3436 100644
--- a/tests/fixtures/script-indent/ts-as-expression-01.vue
+++ b/tests/fixtures/script-indent/ts-as-expression-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-call-signature-declaration-01.vue b/tests/fixtures/script-indent/ts-call-signature-declaration-01.vue
index a6b14b935..b734c69c6 100644
--- a/tests/fixtures/script-indent/ts-call-signature-declaration-01.vue
+++ b/tests/fixtures/script-indent/ts-call-signature-declaration-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-class-declaration-07.vue b/tests/fixtures/script-indent/ts-class-declaration-07.vue
index 5568aa6d3..dfca03b8a 100644
--- a/tests/fixtures/script-indent/ts-class-declaration-07.vue
+++ b/tests/fixtures/script-indent/ts-class-declaration-07.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-declare-function-04.vue b/tests/fixtures/script-indent/ts-declare-function-04.vue
index 3a62cc896..7ae12d894 100644
--- a/tests/fixtures/script-indent/ts-declare-function-04.vue
+++ b/tests/fixtures/script-indent/ts-declare-function-04.vue
@@ -1,7 +1,6 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-import-assertion-02.vue b/tests/fixtures/script-indent/ts-import-assertion-02.vue
new file mode 100644
index 000000000..c6e0cb866
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-import-assertion-02.vue
@@ -0,0 +1,15 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-import-assertion-03.vue b/tests/fixtures/script-indent/ts-import-assertion-03.vue
new file mode 100644
index 000000000..7cdf02501
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-import-assertion-03.vue
@@ -0,0 +1,10 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-import-assertion-04.vue b/tests/fixtures/script-indent/ts-import-assertion-04.vue
new file mode 100644
index 000000000..fc1bee6ee
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-import-assertion-04.vue
@@ -0,0 +1,10 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-import-equal-01.vue b/tests/fixtures/script-indent/ts-import-equal-01.vue
index e2136f100..b35c28cd0 100644
--- a/tests/fixtures/script-indent/ts-import-equal-01.vue
+++ b/tests/fixtures/script-indent/ts-import-equal-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-interface-declaration-01.vue b/tests/fixtures/script-indent/ts-interface-declaration-01.vue
index f3584b7f8..6dc683226 100644
--- a/tests/fixtures/script-indent/ts-interface-declaration-01.vue
+++ b/tests/fixtures/script-indent/ts-interface-declaration-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-interface-declaration-06.vue b/tests/fixtures/script-indent/ts-interface-declaration-06.vue
index 1bf582d42..3390cb963 100644
--- a/tests/fixtures/script-indent/ts-interface-declaration-06.vue
+++ b/tests/fixtures/script-indent/ts-interface-declaration-06.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-qualified-name-01.vue b/tests/fixtures/script-indent/ts-qualified-name-01.vue
index 5c5668a23..aa8fa0a0f 100644
--- a/tests/fixtures/script-indent/ts-qualified-name-01.vue
+++ b/tests/fixtures/script-indent/ts-qualified-name-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-static-block-01.vue b/tests/fixtures/script-indent/ts-static-block-01.vue
index 522271628..5d585d6d0 100644
--- a/tests/fixtures/script-indent/ts-static-block-01.vue
+++ b/tests/fixtures/script-indent/ts-static-block-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/script-indent/ts-type-only-import-export-02.vue b/tests/fixtures/script-indent/ts-type-only-import-export-02.vue
new file mode 100644
index 000000000..b2bad678e
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-type-only-import-export-02.vue
@@ -0,0 +1,23 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-type-only-import-export-03.vue b/tests/fixtures/script-indent/ts-type-only-import-export-03.vue
new file mode 100644
index 000000000..4c9238dc9
--- /dev/null
+++ b/tests/fixtures/script-indent/ts-type-only-import-export-03.vue
@@ -0,0 +1,26 @@
+
+
diff --git a/tests/fixtures/script-indent/ts-type-paramater-01.vue b/tests/fixtures/script-indent/ts-type-paramater-01.vue
index 6b307c740..cbdd01bcf 100644
--- a/tests/fixtures/script-indent/ts-type-paramater-01.vue
+++ b/tests/fixtures/script-indent/ts-type-paramater-01.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/fixtures/typescript/src/test01.ts b/tests/fixtures/typescript/src/test01.ts
new file mode 100644
index 000000000..d14550843
--- /dev/null
+++ b/tests/fixtures/typescript/src/test01.ts
@@ -0,0 +1,20 @@
+export type Props1 = {
+ foo: string
+ bar?: number
+ baz?: boolean
+}
+export type Emits1 = {
+ (e: 'foo' | 'bar', payload: string): void
+ (e: 'baz', payload: number): void
+}
+export type Props2 = {
+ a: string
+ b?: number
+ c?: boolean
+ d?: boolean
+ e?: number | string
+ f?: () => number
+ g?: { foo?: string }
+ h?: string[]
+ i?: readonly string[]
+}
diff --git a/tests/fixtures/typescript/tsconfig.json b/tests/fixtures/typescript/tsconfig.json
new file mode 100644
index 000000000..c13ef64e3
--- /dev/null
+++ b/tests/fixtures/typescript/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "skipLibCheck": true
+ }
+}
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$-with-destructuring/result.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$-with-destructuring/result.js
new file mode 100644
index 000000000..805890a33
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$-with-destructuring/result.js
@@ -0,0 +1,16 @@
+let { a, b: c, d: [,f], g: [...h], ...i } = $(foo)
+let [ x, y = 42 ] = $(bar)
+
+console.log(
+ /*>*/a/*<{"escape":false,"method":"$"}*/,
+ b,
+ /*>*/c/*<{"escape":false,"method":"$"}*/,
+ d,
+ e,
+ /*>*/f/*<{"escape":false,"method":"$"}*/,
+ g,
+ /*>*/h/*<{"escape":false,"method":"$"}*/,
+ /*>*/i/*<{"escape":false,"method":"$"}*/,
+ /*>*/x/*<{"escape":false,"method":"$"}*/,
+ /*>*/y/*<{"escape":false,"method":"$"}*/
+)
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$-with-destructuring/source.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$-with-destructuring/source.js
new file mode 100644
index 000000000..bfb92574f
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$-with-destructuring/source.js
@@ -0,0 +1,16 @@
+let { a, b: c, d: [,f], g: [...h], ...i } = $(foo)
+let [ x, y = 42 ] = $(bar)
+
+console.log(
+ a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i,
+ x,
+ y
+)
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$/result.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$/result.js
new file mode 100644
index 000000000..80d709dee
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$/result.js
@@ -0,0 +1,6 @@
+let v = $(foo)
+let { x, y } = $(bar)
+console.log(/*>*/v/*<{"escape":false,"method":"$"}*/, /*>*/x/*<{"escape":false,"method":"$"}*/, /*>*/y/*<{"escape":false,"method":"$"}*/)
+let a = /*>*/v/*<{"escape":false,"method":"$"}*/
+console.log(a)
+console.log($$({ /*>*/v/*<{"escape":true,"method":"$"}*/, /*>*/x/*<{"escape":true,"method":"$"}*/ }))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$/source.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$/source.js
new file mode 100644
index 000000000..28c98cbc9
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$/source.js
@@ -0,0 +1,6 @@
+let v = $(foo)
+let { x, y } = $(bar)
+console.log(v, x, y)
+let a = v
+console.log(a)
+console.log($$({ v, x }))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$computed/result.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$computed/result.js
new file mode 100644
index 000000000..a45a0ae6f
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$computed/result.js
@@ -0,0 +1,5 @@
+let v = $computed(() => 42)
+console.log(/*>*/v/*<{"escape":false,"method":"$computed"}*/)
+let a = /*>*/v/*<{"escape":false,"method":"$computed"}*/
+console.log(a)
+console.log($$(/*>*/v/*<{"escape":true,"method":"$computed"}*/))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$computed/source.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$computed/source.js
new file mode 100644
index 000000000..ff0f34766
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$computed/source.js
@@ -0,0 +1,5 @@
+let v = $computed(() => 42)
+console.log(v)
+let a = v
+console.log(a)
+console.log($$(v))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$customRef/result.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$customRef/result.js
new file mode 100644
index 000000000..a8a3ae90a
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$customRef/result.js
@@ -0,0 +1,10 @@
+let v = $customRef((track, trigger) => {
+ return {
+ get() { return count },
+ set(newValue) { count = newValue }
+ }
+})
+console.log(/*>*/v/*<{"escape":false,"method":"$customRef"}*/)
+let a = /*>*/v/*<{"escape":false,"method":"$customRef"}*/
+console.log(a)
+console.log($$(/*>*/v/*<{"escape":true,"method":"$customRef"}*/))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$customRef/source.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$customRef/source.js
new file mode 100644
index 000000000..dd62754bc
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$customRef/source.js
@@ -0,0 +1,10 @@
+let v = $customRef((track, trigger) => {
+ return {
+ get() { return count },
+ set(newValue) { count = newValue }
+ }
+})
+console.log(v)
+let a = v
+console.log(a)
+console.log($$(v))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$ref/result.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$ref/result.js
new file mode 100644
index 000000000..518f0c56b
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$ref/result.js
@@ -0,0 +1,5 @@
+let v = $ref(0)
+console.log(/*>*/v/*<{"escape":false,"method":"$ref"}*/)
+let a = /*>*/v/*<{"escape":false,"method":"$ref"}*/
+console.log(a)
+console.log($$(/*>*/v/*<{"escape":true,"method":"$ref"}*/))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$ref/source.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$ref/source.js
new file mode 100644
index 000000000..5c8d7248e
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$ref/source.js
@@ -0,0 +1,5 @@
+let v = $ref(0)
+console.log(v)
+let a = v
+console.log(a)
+console.log($$(v))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$shallowRef/result.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$shallowRef/result.js
new file mode 100644
index 000000000..dbe345a19
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$shallowRef/result.js
@@ -0,0 +1,5 @@
+let v = $shallowRef({ count: 0 })
+console.log(/*>*/v/*<{"escape":false,"method":"$shallowRef"}*/)
+let a = /*>*/v/*<{"escape":false,"method":"$shallowRef"}*/
+console.log(a)
+console.log($$(/*>*/v/*<{"escape":true,"method":"$shallowRef"}*/))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$shallowRef/source.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$shallowRef/source.js
new file mode 100644
index 000000000..da4c19320
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$shallowRef/source.js
@@ -0,0 +1,5 @@
+let v = $shallowRef({ count: 0 })
+console.log(v)
+let a = v
+console.log(a)
+console.log($$(v))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$toRef/result.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$toRef/result.js
new file mode 100644
index 000000000..a7bff1219
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$toRef/result.js
@@ -0,0 +1,5 @@
+let v = $toRef(foo, 'bar')
+console.log(/*>*/v/*<{"escape":false,"method":"$toRef"}*/)
+let a = /*>*/v/*<{"escape":false,"method":"$toRef"}*/
+console.log(a)
+console.log($$(/*>*/v/*<{"escape":true,"method":"$toRef"}*/))
diff --git a/tests/fixtures/utils/ref-object-references/reactive-vars/$toRef/source.js b/tests/fixtures/utils/ref-object-references/reactive-vars/$toRef/source.js
new file mode 100644
index 000000000..b94ddea93
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/reactive-vars/$toRef/source.js
@@ -0,0 +1,5 @@
+let v = $toRef(foo, 'bar')
+console.log(v)
+let a = v
+console.log(a)
+console.log($$(v))
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/computed/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/computed/result.js
new file mode 100644
index 000000000..2217a8182
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/computed/result.js
@@ -0,0 +1,5 @@
+import { computed } from 'vue'
+let v = computed(() => 42)
+console.log(/*>*/v/*<{"type":"expression","method":"computed"}*/.value)
+let a = v
+console.log(/*>*/a/*<{"type":"expression","method":"computed"}*/.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/computed/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/computed/source.js
new file mode 100644
index 000000000..2b4a6412d
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/computed/source.js
@@ -0,0 +1,5 @@
+import { computed } from 'vue'
+let v = computed(() => 42)
+console.log(v.value)
+let a = v
+console.log(a.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/customRef/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/customRef/result.js
new file mode 100644
index 000000000..e7af0774e
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/customRef/result.js
@@ -0,0 +1,10 @@
+import { customRef } from 'vue'
+let v = customRef((track, trigger) => {
+ return {
+ get() { return count },
+ set(newValue) { count = newValue }
+ }
+})
+console.log(/*>*/v/*<{"type":"expression","method":"customRef"}*/.value)
+let a = v
+console.log(/*>*/a/*<{"type":"expression","method":"customRef"}*/.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/customRef/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/customRef/source.js
new file mode 100644
index 000000000..c94db730f
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/customRef/source.js
@@ -0,0 +1,10 @@
+import { customRef } from 'vue'
+let v = customRef((track, trigger) => {
+ return {
+ get() { return count },
+ set(newValue) { count = newValue }
+ }
+})
+console.log(v.value)
+let a = v
+console.log(a.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/result.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/result.vue
new file mode 100644
index 000000000..a5d4293d8
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/result.vue
@@ -0,0 +1,24 @@
+
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/source.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/source.vue
new file mode 100644
index 000000000..7cca9156c
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/source.vue
@@ -0,0 +1,24 @@
+
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/result.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/result.vue
new file mode 100644
index 000000000..c2903d01f
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/result.vue
@@ -0,0 +1,16 @@
+
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/source.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/source.vue
new file mode 100644
index 000000000..012cb9349
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/source.vue
@@ -0,0 +1,16 @@
+
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/ref-to-pattern/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/ref-to-pattern/result.js
new file mode 100644
index 000000000..a0681af4a
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/ref-to-pattern/result.js
@@ -0,0 +1,9 @@
+import { ref } from 'vue'
+let v = ref(0)
+const /*>*/{ value: a }/*<{"type":"pattern","method":"ref"}*/ = v
+const /*>*/{ value = 42 }/*<{"type":"pattern","method":"ref"}*/ = v
+console.log(a)
+console.log(value)
+
+const [x] = v
+console.log(x)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/ref-to-pattern/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/ref-to-pattern/source.js
new file mode 100644
index 000000000..3a2c5c330
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/ref-to-pattern/source.js
@@ -0,0 +1,9 @@
+import { ref } from 'vue'
+let v = ref(0)
+const { value: a } = v
+const { value = 42 } = v
+console.log(a)
+console.log(value)
+
+const [x] = v
+console.log(x)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/ref/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/ref/result.js
new file mode 100644
index 000000000..c356f2c2b
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/ref/result.js
@@ -0,0 +1,5 @@
+import { ref } from 'vue'
+let v = ref(0)
+console.log(/*>*/v/*<{"type":"expression","method":"ref"}*/.value)
+let a = v
+console.log(/*>*/a/*<{"type":"expression","method":"ref"}*/.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/ref/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/ref/source.js
new file mode 100644
index 000000000..836b45777
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/ref/source.js
@@ -0,0 +1,5 @@
+import { ref } from 'vue'
+let v = ref(0)
+console.log(v.value)
+let a = v
+console.log(a.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/shallowRef/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/shallowRef/result.js
new file mode 100644
index 000000000..206f3e88d
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/shallowRef/result.js
@@ -0,0 +1,5 @@
+import { shallowRef } from 'vue'
+let v = shallowRef({ count: 0 })
+console.log(/*>*/v/*<{"type":"expression","method":"shallowRef"}*/.value)
+let a = v
+console.log(/*>*/a/*<{"type":"expression","method":"shallowRef"}*/.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/shallowRef/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/shallowRef/source.js
new file mode 100644
index 000000000..98496ae7f
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/shallowRef/source.js
@@ -0,0 +1,5 @@
+import { shallowRef } from 'vue'
+let v = shallowRef({ count: 0 })
+console.log(v.value)
+let a = v
+console.log(a.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/toRef/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/toRef/result.js
new file mode 100644
index 000000000..28bea16dc
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/toRef/result.js
@@ -0,0 +1,5 @@
+import { toRef } from 'vue'
+let v = toRef(foo, 'bar')
+console.log(/*>*/v/*<{"type":"expression","method":"toRef"}*/.value)
+let a = v
+console.log(/*>*/a/*<{"type":"expression","method":"toRef"}*/.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/toRef/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/toRef/source.js
new file mode 100644
index 000000000..df884f003
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/toRef/source.js
@@ -0,0 +1,5 @@
+import { toRef } from 'vue'
+let v = toRef(foo, 'bar')
+console.log(v.value)
+let a = v
+console.log(a.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/toRefs-to-pattern/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs-to-pattern/result.js
new file mode 100644
index 000000000..527acdc78
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs-to-pattern/result.js
@@ -0,0 +1,5 @@
+import { toRefs } from 'vue'
+let bar = toRefs(foo)
+const { x, y = 42 } = bar
+console.log(/*>*/x/*<{"type":"expression","method":"toRefs"}*/.value)
+console.log(/*>*/y/*<{"type":"expression","method":"toRefs"}*/.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/toRefs-to-pattern/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs-to-pattern/source.js
new file mode 100644
index 000000000..b2c39b4a9
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs-to-pattern/source.js
@@ -0,0 +1,5 @@
+import { toRefs } from 'vue'
+let bar = toRefs(foo)
+const { x, y = 42 } = bar
+console.log(x.value)
+console.log(y.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/toRefs/result.js b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs/result.js
new file mode 100644
index 000000000..809dfc902
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs/result.js
@@ -0,0 +1,18 @@
+import { toRefs } from 'vue'
+let { x, y } = toRefs(foo)
+console.log(/*>*/x/*<{"type":"expression","method":"toRefs"}*/.value)
+let a = y
+console.log(/*>*/a/*<{"type":"expression","method":"toRefs"}*/.value)
+console.log(/*>*/y/*<{"type":"expression","method":"toRefs"}*/.value)
+
+let bar = toRefs(foo)
+console.log(bar)
+console.log(/*>*/bar.x/*<{"type":"expression","method":"toRefs"}*/.value)
+console.log(/*>*/bar.y/*<{"type":"expression","method":"toRefs"}*/.value)
+
+const z = bar.z
+console.log(/*>*/z/*<{"type":"expression","method":"toRefs"}*/.value)
+
+let b;
+/*>*/b/*<{"type":"pattern","method":"toRefs"}*/ = bar.b
+console.log(/*>*/b/*<{"type":"expression","method":"toRefs"}*/.value)
diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/toRefs/source.js b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs/source.js
new file mode 100644
index 000000000..638e69296
--- /dev/null
+++ b/tests/fixtures/utils/ref-object-references/ref-objects/toRefs/source.js
@@ -0,0 +1,18 @@
+import { toRefs } from 'vue'
+let { x, y } = toRefs(foo)
+console.log(x.value)
+let a = y
+console.log(a.value)
+console.log(y.value)
+
+let bar = toRefs(foo)
+console.log(bar)
+console.log(bar.x.value)
+console.log(bar.y.value)
+
+const z = bar.z
+console.log(z.value)
+
+let b;
+b = bar.b
+console.log(b.value)
diff --git a/tests/fixtures/utils/selector/attr-contains/result.json b/tests/fixtures/utils/selector/attr-contains/result.json
new file mode 100644
index 000000000..a2627437e
--- /dev/null
+++ b/tests/fixtures/utils/selector/attr-contains/result.json
@@ -0,0 +1,9 @@
+{
+ "selector": "a[href*=\"example\"]",
+ "matches": [
+ "",
+ "",
+ ""
+ ],
+ "errors": []
+}
\ No newline at end of file
diff --git a/tests/fixtures/utils/selector/attr-contains/source.vue b/tests/fixtures/utils/selector/attr-contains/source.vue
new file mode 100644
index 000000000..6c9a89dde
--- /dev/null
+++ b/tests/fixtures/utils/selector/attr-contains/source.vue
@@ -0,0 +1,10 @@
+
+
+