From 13cacf8fbc5b8805cbe8c0e355e57d7db4b69762 Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Mon, 25 Mar 2024 11:40:00 +0900 Subject: [PATCH 1/8] Change flat config to an array to accept `*.vue` --- lib/configs/flat/base.js | 38 +++- lib/configs/flat/vue2-essential.js | 136 +++++------ lib/configs/flat/vue2-recommended.js | 26 ++- lib/configs/flat/vue2-strongly-recommended.js | 58 ++--- lib/configs/flat/vue3-essential.js | 172 +++++++------- lib/configs/flat/vue3-recommended.js | 26 ++- lib/configs/flat/vue3-strongly-recommended.js | 72 +++--- lib/index.js | 74 ++---- lib/utils/config-helpers.js | 19 -- package.json | 1 + tests/eslint-compat.js | 69 ++++-- tests/integrations/flat-config.js | 43 ++++ tests/integrations/flat-config/.npmrc | 1 + tests/integrations/flat-config/a.vue | 12 + .../integrations/flat-config/eslint.config.js | 9 + tests/integrations/flat-config/package.json | 13 ++ tests/lib/configs/flat.js | 214 ++++++++++++++++-- tools/update-lib-flat-configs.js | 47 ++-- tools/update-lib-index.js | 50 ++-- 19 files changed, 676 insertions(+), 404 deletions(-) delete mode 100644 lib/utils/config-helpers.js create mode 100644 tests/integrations/flat-config.js create mode 100644 tests/integrations/flat-config/.npmrc create mode 100644 tests/integrations/flat-config/a.vue create mode 100644 tests/integrations/flat-config/eslint.config.js create mode 100644 tests/integrations/flat-config/package.json diff --git a/lib/configs/flat/base.js b/lib/configs/flat/base.js index e36ab4b77..6c379c272 100644 --- a/lib/configs/flat/base.js +++ b/lib/configs/flat/base.js @@ -4,14 +4,34 @@ * in order to update its content execute "npm run update" */ const globals = require('globals') -module.exports = { - languageOptions: { - parser: require('vue-eslint-parser'), - sourceType: 'module', - globals: globals.browser +module.exports = [ + { + plugins: { + get vue() { + return require('../../index') + } + }, + languageOptions: { + sourceType: 'module', + globals: globals.browser + } }, - rules: { - 'vue/comment-directive': 'error', - 'vue/jsx-uses-vars': 'error' + { + files: ['*.vue', '**/*.vue'], + plugins: { + get vue() { + return require('../../index') + } + }, + languageOptions: { + parser: require('vue-eslint-parser'), + sourceType: 'module', + globals: globals.browser + }, + rules: { + 'vue/comment-directive': 'error', + 'vue/jsx-uses-vars': 'error' + }, + processor: 'vue/vue' } -} +] diff --git a/lib/configs/flat/vue2-essential.js b/lib/configs/flat/vue2-essential.js index ce253352b..3b8c99243 100644 --- a/lib/configs/flat/vue2-essential.js +++ b/lib/configs/flat/vue2-essential.js @@ -5,72 +5,74 @@ */ 'use strict' const config = require('./base.js') -const { extendRules } = require('../../utils/config-helpers.js') -const rules = { - 'vue/multi-word-component-names': 'error', - 'vue/no-arrow-functions-in-watch': 'error', - 'vue/no-async-in-computed-properties': 'error', - 'vue/no-child-content': 'error', - 'vue/no-computed-properties-in-data': 'error', - 'vue/no-custom-modifiers-on-v-model': 'error', - 'vue/no-dupe-keys': 'error', - 'vue/no-dupe-v-else-if': 'error', - 'vue/no-duplicate-attributes': 'error', - 'vue/no-export-in-script-setup': 'error', - 'vue/no-multiple-template-root': 'error', - 'vue/no-mutating-props': 'error', - 'vue/no-parsing-error': 'error', - 'vue/no-ref-as-operand': 'error', - 'vue/no-reserved-component-names': 'error', - 'vue/no-reserved-keys': 'error', - 'vue/no-reserved-props': [ - 'error', - { - vueVersion: 2 +module.exports = [ + ...config, + { + rules: { + 'vue/multi-word-component-names': 'error', + 'vue/no-arrow-functions-in-watch': 'error', + 'vue/no-async-in-computed-properties': 'error', + 'vue/no-child-content': 'error', + 'vue/no-computed-properties-in-data': 'error', + 'vue/no-custom-modifiers-on-v-model': 'error', + 'vue/no-dupe-keys': 'error', + 'vue/no-dupe-v-else-if': 'error', + 'vue/no-duplicate-attributes': 'error', + 'vue/no-export-in-script-setup': 'error', + 'vue/no-multiple-template-root': 'error', + 'vue/no-mutating-props': 'error', + 'vue/no-parsing-error': 'error', + 'vue/no-ref-as-operand': 'error', + 'vue/no-reserved-component-names': 'error', + 'vue/no-reserved-keys': 'error', + 'vue/no-reserved-props': [ + 'error', + { + vueVersion: 2 + } + ], + 'vue/no-shared-component-data': 'error', + 'vue/no-side-effects-in-computed-properties': 'error', + 'vue/no-template-key': 'error', + 'vue/no-textarea-mustache': 'error', + 'vue/no-unused-components': 'error', + 'vue/no-unused-vars': 'error', + 'vue/no-use-computed-property-like-method': 'error', + 'vue/no-use-v-if-with-v-for': 'error', + 'vue/no-useless-template-attributes': 'error', + 'vue/no-v-for-template-key': 'error', + 'vue/no-v-model-argument': 'error', + 'vue/no-v-text-v-html-on-component': 'error', + 'vue/require-component-is': 'error', + 'vue/require-prop-type-constructor': 'error', + 'vue/require-render-return': 'error', + 'vue/require-v-for-key': 'error', + 'vue/require-valid-default-prop': 'error', + 'vue/return-in-computed-property': 'error', + 'vue/return-in-emits-validator': 'error', + 'vue/use-v-on-exact': 'error', + 'vue/valid-attribute-name': 'error', + 'vue/valid-define-emits': 'error', + 'vue/valid-define-props': 'error', + 'vue/valid-model-definition': 'error', + 'vue/valid-next-tick': 'error', + 'vue/valid-template-root': 'error', + 'vue/valid-v-bind-sync': 'error', + 'vue/valid-v-bind': 'error', + 'vue/valid-v-cloak': 'error', + 'vue/valid-v-else-if': 'error', + 'vue/valid-v-else': 'error', + 'vue/valid-v-for': 'error', + 'vue/valid-v-html': 'error', + 'vue/valid-v-if': 'error', + 'vue/valid-v-model': 'error', + 'vue/valid-v-on': 'error', + 'vue/valid-v-once': 'error', + 'vue/valid-v-pre': 'error', + 'vue/valid-v-show': 'error', + 'vue/valid-v-slot': 'error', + 'vue/valid-v-text': 'error' } - ], - 'vue/no-shared-component-data': 'error', - 'vue/no-side-effects-in-computed-properties': 'error', - 'vue/no-template-key': 'error', - 'vue/no-textarea-mustache': 'error', - 'vue/no-unused-components': 'error', - 'vue/no-unused-vars': 'error', - 'vue/no-use-computed-property-like-method': 'error', - 'vue/no-use-v-if-with-v-for': 'error', - 'vue/no-useless-template-attributes': 'error', - 'vue/no-v-for-template-key': 'error', - 'vue/no-v-model-argument': 'error', - 'vue/no-v-text-v-html-on-component': 'error', - 'vue/require-component-is': 'error', - 'vue/require-prop-type-constructor': 'error', - 'vue/require-render-return': 'error', - 'vue/require-v-for-key': 'error', - 'vue/require-valid-default-prop': 'error', - 'vue/return-in-computed-property': 'error', - 'vue/return-in-emits-validator': 'error', - 'vue/use-v-on-exact': 'error', - 'vue/valid-attribute-name': 'error', - 'vue/valid-define-emits': 'error', - 'vue/valid-define-props': 'error', - 'vue/valid-model-definition': 'error', - 'vue/valid-next-tick': 'error', - 'vue/valid-template-root': 'error', - 'vue/valid-v-bind-sync': 'error', - 'vue/valid-v-bind': 'error', - 'vue/valid-v-cloak': 'error', - 'vue/valid-v-else-if': 'error', - 'vue/valid-v-else': 'error', - 'vue/valid-v-for': 'error', - 'vue/valid-v-html': 'error', - 'vue/valid-v-if': 'error', - 'vue/valid-v-model': 'error', - 'vue/valid-v-on': 'error', - 'vue/valid-v-once': 'error', - 'vue/valid-v-pre': 'error', - 'vue/valid-v-show': 'error', - 'vue/valid-v-slot': 'error', - 'vue/valid-v-text': 'error' -} - -module.exports = extendRules(config, rules) + } +] diff --git a/lib/configs/flat/vue2-recommended.js b/lib/configs/flat/vue2-recommended.js index e15b02075..9bd029dee 100644 --- a/lib/configs/flat/vue2-recommended.js +++ b/lib/configs/flat/vue2-recommended.js @@ -5,16 +5,18 @@ */ 'use strict' const config = require('./vue2-strongly-recommended.js') -const { extendRules } = require('../../utils/config-helpers.js') -const rules = { - 'vue/attributes-order': 'warn', - 'vue/component-tags-order': 'warn', - 'vue/no-lone-template': 'warn', - 'vue/no-multiple-slot-args': 'warn', - 'vue/no-v-html': 'warn', - 'vue/order-in-components': 'warn', - 'vue/this-in-template': 'warn' -} - -module.exports = extendRules(config, rules) +module.exports = [ + ...config, + { + rules: { + 'vue/attributes-order': 'warn', + 'vue/component-tags-order': 'warn', + 'vue/no-lone-template': 'warn', + 'vue/no-multiple-slot-args': 'warn', + 'vue/no-v-html': 'warn', + 'vue/order-in-components': 'warn', + 'vue/this-in-template': 'warn' + } + } +] diff --git a/lib/configs/flat/vue2-strongly-recommended.js b/lib/configs/flat/vue2-strongly-recommended.js index a1c870b92..e8d437b4c 100644 --- a/lib/configs/flat/vue2-strongly-recommended.js +++ b/lib/configs/flat/vue2-strongly-recommended.js @@ -5,32 +5,34 @@ */ 'use strict' const config = require('./vue2-essential.js') -const { extendRules } = require('../../utils/config-helpers.js') -const rules = { - 'vue/attribute-hyphenation': 'warn', - 'vue/component-definition-name-casing': 'warn', - 'vue/first-attribute-linebreak': 'warn', - 'vue/html-closing-bracket-newline': 'warn', - 'vue/html-closing-bracket-spacing': 'warn', - 'vue/html-end-tags': 'warn', - 'vue/html-indent': 'warn', - 'vue/html-quotes': 'warn', - 'vue/html-self-closing': 'warn', - 'vue/max-attributes-per-line': 'warn', - 'vue/multiline-html-element-content-newline': 'warn', - 'vue/mustache-interpolation-spacing': 'warn', - 'vue/no-multi-spaces': 'warn', - 'vue/no-spaces-around-equal-signs-in-attribute': 'warn', - 'vue/no-template-shadow': 'warn', - 'vue/one-component-per-file': 'warn', - 'vue/prop-name-casing': 'warn', - 'vue/require-default-prop': 'warn', - 'vue/require-prop-types': 'warn', - 'vue/singleline-html-element-content-newline': 'warn', - 'vue/v-bind-style': 'warn', - 'vue/v-on-style': 'warn', - 'vue/v-slot-style': 'warn' -} - -module.exports = extendRules(config, rules) +module.exports = [ + ...config, + { + rules: { + 'vue/attribute-hyphenation': 'warn', + 'vue/component-definition-name-casing': 'warn', + 'vue/first-attribute-linebreak': 'warn', + 'vue/html-closing-bracket-newline': 'warn', + 'vue/html-closing-bracket-spacing': 'warn', + 'vue/html-end-tags': 'warn', + 'vue/html-indent': 'warn', + 'vue/html-quotes': 'warn', + 'vue/html-self-closing': 'warn', + 'vue/max-attributes-per-line': 'warn', + 'vue/multiline-html-element-content-newline': 'warn', + 'vue/mustache-interpolation-spacing': 'warn', + 'vue/no-multi-spaces': 'warn', + 'vue/no-spaces-around-equal-signs-in-attribute': 'warn', + 'vue/no-template-shadow': 'warn', + 'vue/one-component-per-file': 'warn', + 'vue/prop-name-casing': 'warn', + 'vue/require-default-prop': 'warn', + 'vue/require-prop-types': 'warn', + 'vue/singleline-html-element-content-newline': 'warn', + 'vue/v-bind-style': 'warn', + 'vue/v-on-style': 'warn', + 'vue/v-slot-style': 'warn' + } + } +] diff --git a/lib/configs/flat/vue3-essential.js b/lib/configs/flat/vue3-essential.js index e8450635d..b6338c910 100644 --- a/lib/configs/flat/vue3-essential.js +++ b/lib/configs/flat/vue3-essential.js @@ -5,89 +5,91 @@ */ 'use strict' const config = require('./base.js') -const { extendRules } = require('../../utils/config-helpers.js') -const rules = { - 'vue/multi-word-component-names': 'error', - 'vue/no-arrow-functions-in-watch': 'error', - 'vue/no-async-in-computed-properties': 'error', - 'vue/no-child-content': 'error', - 'vue/no-computed-properties-in-data': 'error', - 'vue/no-deprecated-data-object-declaration': 'error', - 'vue/no-deprecated-destroyed-lifecycle': 'error', - 'vue/no-deprecated-dollar-listeners-api': 'error', - 'vue/no-deprecated-dollar-scopedslots-api': 'error', - 'vue/no-deprecated-events-api': 'error', - 'vue/no-deprecated-filter': 'error', - 'vue/no-deprecated-functional-template': 'error', - 'vue/no-deprecated-html-element-is': 'error', - 'vue/no-deprecated-inline-template': 'error', - 'vue/no-deprecated-props-default-this': 'error', - 'vue/no-deprecated-router-link-tag-prop': 'error', - 'vue/no-deprecated-scope-attribute': 'error', - 'vue/no-deprecated-slot-attribute': 'error', - 'vue/no-deprecated-slot-scope-attribute': 'error', - 'vue/no-deprecated-v-bind-sync': 'error', - 'vue/no-deprecated-v-is': 'error', - 'vue/no-deprecated-v-on-native-modifier': 'error', - 'vue/no-deprecated-v-on-number-modifiers': 'error', - 'vue/no-deprecated-vue-config-keycodes': 'error', - 'vue/no-dupe-keys': 'error', - 'vue/no-dupe-v-else-if': 'error', - 'vue/no-duplicate-attributes': 'error', - 'vue/no-export-in-script-setup': 'error', - 'vue/no-expose-after-await': 'error', - 'vue/no-lifecycle-after-await': 'error', - 'vue/no-mutating-props': 'error', - 'vue/no-parsing-error': 'error', - 'vue/no-ref-as-operand': 'error', - 'vue/no-reserved-component-names': 'error', - 'vue/no-reserved-keys': 'error', - 'vue/no-reserved-props': 'error', - 'vue/no-shared-component-data': 'error', - 'vue/no-side-effects-in-computed-properties': 'error', - 'vue/no-template-key': 'error', - 'vue/no-textarea-mustache': 'error', - 'vue/no-unused-components': 'error', - 'vue/no-unused-vars': 'error', - 'vue/no-use-computed-property-like-method': 'error', - 'vue/no-use-v-if-with-v-for': 'error', - 'vue/no-useless-template-attributes': 'error', - 'vue/no-v-for-template-key-on-child': 'error', - 'vue/no-v-text-v-html-on-component': 'error', - 'vue/no-watch-after-await': 'error', - 'vue/prefer-import-from-vue': 'error', - 'vue/require-component-is': 'error', - 'vue/require-prop-type-constructor': 'error', - 'vue/require-render-return': 'error', - 'vue/require-slots-as-functions': 'error', - 'vue/require-toggle-inside-transition': 'error', - 'vue/require-v-for-key': 'error', - 'vue/require-valid-default-prop': 'error', - 'vue/return-in-computed-property': 'error', - 'vue/return-in-emits-validator': 'error', - 'vue/use-v-on-exact': 'error', - 'vue/valid-attribute-name': 'error', - 'vue/valid-define-emits': 'error', - 'vue/valid-define-props': 'error', - 'vue/valid-next-tick': 'error', - 'vue/valid-template-root': 'error', - 'vue/valid-v-bind': 'error', - 'vue/valid-v-cloak': 'error', - 'vue/valid-v-else-if': 'error', - 'vue/valid-v-else': 'error', - 'vue/valid-v-for': 'error', - 'vue/valid-v-html': 'error', - 'vue/valid-v-if': 'error', - 'vue/valid-v-is': 'error', - 'vue/valid-v-memo': 'error', - 'vue/valid-v-model': 'error', - 'vue/valid-v-on': 'error', - 'vue/valid-v-once': 'error', - 'vue/valid-v-pre': 'error', - 'vue/valid-v-show': 'error', - 'vue/valid-v-slot': 'error', - 'vue/valid-v-text': 'error' -} - -module.exports = extendRules(config, rules) +module.exports = [ + ...config, + { + rules: { + 'vue/multi-word-component-names': 'error', + 'vue/no-arrow-functions-in-watch': 'error', + 'vue/no-async-in-computed-properties': 'error', + 'vue/no-child-content': 'error', + 'vue/no-computed-properties-in-data': 'error', + 'vue/no-deprecated-data-object-declaration': 'error', + 'vue/no-deprecated-destroyed-lifecycle': 'error', + 'vue/no-deprecated-dollar-listeners-api': 'error', + 'vue/no-deprecated-dollar-scopedslots-api': 'error', + 'vue/no-deprecated-events-api': 'error', + 'vue/no-deprecated-filter': 'error', + 'vue/no-deprecated-functional-template': 'error', + 'vue/no-deprecated-html-element-is': 'error', + 'vue/no-deprecated-inline-template': 'error', + 'vue/no-deprecated-props-default-this': 'error', + 'vue/no-deprecated-router-link-tag-prop': 'error', + 'vue/no-deprecated-scope-attribute': 'error', + 'vue/no-deprecated-slot-attribute': 'error', + 'vue/no-deprecated-slot-scope-attribute': 'error', + 'vue/no-deprecated-v-bind-sync': 'error', + 'vue/no-deprecated-v-is': 'error', + 'vue/no-deprecated-v-on-native-modifier': 'error', + 'vue/no-deprecated-v-on-number-modifiers': 'error', + 'vue/no-deprecated-vue-config-keycodes': 'error', + 'vue/no-dupe-keys': 'error', + 'vue/no-dupe-v-else-if': 'error', + 'vue/no-duplicate-attributes': 'error', + 'vue/no-export-in-script-setup': 'error', + 'vue/no-expose-after-await': 'error', + 'vue/no-lifecycle-after-await': 'error', + 'vue/no-mutating-props': 'error', + 'vue/no-parsing-error': 'error', + 'vue/no-ref-as-operand': 'error', + 'vue/no-reserved-component-names': 'error', + 'vue/no-reserved-keys': 'error', + 'vue/no-reserved-props': 'error', + 'vue/no-shared-component-data': 'error', + 'vue/no-side-effects-in-computed-properties': 'error', + 'vue/no-template-key': 'error', + 'vue/no-textarea-mustache': 'error', + 'vue/no-unused-components': 'error', + 'vue/no-unused-vars': 'error', + 'vue/no-use-computed-property-like-method': 'error', + 'vue/no-use-v-if-with-v-for': 'error', + 'vue/no-useless-template-attributes': 'error', + 'vue/no-v-for-template-key-on-child': 'error', + 'vue/no-v-text-v-html-on-component': 'error', + 'vue/no-watch-after-await': 'error', + 'vue/prefer-import-from-vue': 'error', + 'vue/require-component-is': 'error', + 'vue/require-prop-type-constructor': 'error', + 'vue/require-render-return': 'error', + 'vue/require-slots-as-functions': 'error', + 'vue/require-toggle-inside-transition': 'error', + 'vue/require-v-for-key': 'error', + 'vue/require-valid-default-prop': 'error', + 'vue/return-in-computed-property': 'error', + 'vue/return-in-emits-validator': 'error', + 'vue/use-v-on-exact': 'error', + 'vue/valid-attribute-name': 'error', + 'vue/valid-define-emits': 'error', + 'vue/valid-define-props': 'error', + 'vue/valid-next-tick': 'error', + 'vue/valid-template-root': 'error', + 'vue/valid-v-bind': 'error', + 'vue/valid-v-cloak': 'error', + 'vue/valid-v-else-if': 'error', + 'vue/valid-v-else': 'error', + 'vue/valid-v-for': 'error', + 'vue/valid-v-html': 'error', + 'vue/valid-v-if': 'error', + 'vue/valid-v-is': 'error', + 'vue/valid-v-memo': 'error', + 'vue/valid-v-model': 'error', + 'vue/valid-v-on': 'error', + 'vue/valid-v-once': 'error', + 'vue/valid-v-pre': 'error', + 'vue/valid-v-show': 'error', + 'vue/valid-v-slot': 'error', + 'vue/valid-v-text': 'error' + } + } +] diff --git a/lib/configs/flat/vue3-recommended.js b/lib/configs/flat/vue3-recommended.js index e39b86bc8..eff60c5d6 100644 --- a/lib/configs/flat/vue3-recommended.js +++ b/lib/configs/flat/vue3-recommended.js @@ -5,16 +5,18 @@ */ 'use strict' const config = require('./vue3-strongly-recommended.js') -const { extendRules } = require('../../utils/config-helpers.js') -const rules = { - 'vue/attributes-order': 'warn', - 'vue/component-tags-order': 'warn', - 'vue/no-lone-template': 'warn', - 'vue/no-multiple-slot-args': 'warn', - 'vue/no-v-html': 'warn', - 'vue/order-in-components': 'warn', - 'vue/this-in-template': 'warn' -} - -module.exports = extendRules(config, rules) +module.exports = [ + ...config, + { + rules: { + 'vue/attributes-order': 'warn', + 'vue/component-tags-order': 'warn', + 'vue/no-lone-template': 'warn', + 'vue/no-multiple-slot-args': 'warn', + 'vue/no-v-html': 'warn', + 'vue/order-in-components': 'warn', + 'vue/this-in-template': 'warn' + } + } +] diff --git a/lib/configs/flat/vue3-strongly-recommended.js b/lib/configs/flat/vue3-strongly-recommended.js index ec71ac468..2d3bb2b6e 100644 --- a/lib/configs/flat/vue3-strongly-recommended.js +++ b/lib/configs/flat/vue3-strongly-recommended.js @@ -5,40 +5,42 @@ */ 'use strict' const config = require('./vue3-essential.js') -const { extendRules } = require('../../utils/config-helpers.js') -const rules = { - 'vue/attribute-hyphenation': 'warn', - 'vue/component-definition-name-casing': 'warn', - 'vue/first-attribute-linebreak': 'warn', - 'vue/html-closing-bracket-newline': 'warn', - 'vue/html-closing-bracket-spacing': 'warn', - 'vue/html-end-tags': 'warn', - 'vue/html-indent': 'warn', - 'vue/html-quotes': 'warn', - 'vue/html-self-closing': 'warn', - 'vue/max-attributes-per-line': 'warn', - 'vue/multiline-html-element-content-newline': 'warn', - 'vue/mustache-interpolation-spacing': 'warn', - 'vue/no-multi-spaces': 'warn', - 'vue/no-spaces-around-equal-signs-in-attribute': 'warn', - 'vue/no-template-shadow': 'warn', - 'vue/one-component-per-file': 'warn', - 'vue/prop-name-casing': 'warn', - 'vue/require-default-prop': 'warn', - 'vue/require-explicit-emits': 'warn', - 'vue/require-prop-types': 'warn', - 'vue/singleline-html-element-content-newline': 'warn', - 'vue/v-bind-style': 'warn', - 'vue/v-on-event-hyphenation': [ - 'warn', - 'always', - { - autofix: true +module.exports = [ + ...config, + { + rules: { + 'vue/attribute-hyphenation': 'warn', + 'vue/component-definition-name-casing': 'warn', + 'vue/first-attribute-linebreak': 'warn', + 'vue/html-closing-bracket-newline': 'warn', + 'vue/html-closing-bracket-spacing': 'warn', + 'vue/html-end-tags': 'warn', + 'vue/html-indent': 'warn', + 'vue/html-quotes': 'warn', + 'vue/html-self-closing': 'warn', + 'vue/max-attributes-per-line': 'warn', + 'vue/multiline-html-element-content-newline': 'warn', + 'vue/mustache-interpolation-spacing': 'warn', + 'vue/no-multi-spaces': 'warn', + 'vue/no-spaces-around-equal-signs-in-attribute': 'warn', + 'vue/no-template-shadow': 'warn', + 'vue/one-component-per-file': 'warn', + 'vue/prop-name-casing': 'warn', + 'vue/require-default-prop': 'warn', + 'vue/require-explicit-emits': 'warn', + 'vue/require-prop-types': 'warn', + 'vue/singleline-html-element-content-newline': 'warn', + 'vue/v-bind-style': 'warn', + 'vue/v-on-event-hyphenation': [ + 'warn', + 'always', + { + autofix: true + } + ], + 'vue/v-on-style': 'warn', + 'vue/v-slot-style': 'warn' } - ], - 'vue/v-on-style': 'warn', - 'vue/v-slot-style': 'warn' -} - -module.exports = extendRules(config, rules) + } +] diff --git a/lib/index.js b/lib/index.js index bc6c9a463..956eeaa4b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,6 +7,28 @@ const plugin = { meta: require('./meta'), + configs: { + // eslintrc configs + base: require('./configs/base'), + essential: require('./configs/vue2-essential'), + 'no-layout-rules': require('./configs/no-layout-rules'), + recommended: require('./configs/vue2-recommended'), + 'strongly-recommended': require('./configs/vue2-strongly-recommended'), + 'vue3-essential': require('./configs/vue3-essential'), + 'vue3-recommended': require('./configs/vue3-recommended'), + 'vue3-strongly-recommended': require('./configs/vue3-strongly-recommended'), + + // flat configs + 'flat/base': require('./configs/flat/base.js'), + 'flat/vue2-essential': require('./configs/flat/vue2-essential.js'), + 'flat/vue2-recommended': require('./configs/flat/vue2-recommended.js'), + 'flat/vue2-strongly-recommended': require('./configs/flat/vue2-strongly-recommended.js'), + + // in flat configs, non-prefixed config is for Vue 3 (unlike eslintrc configs) + 'flat/essential': require('./configs/flat/vue3-essential.js'), + 'flat/recommended': require('./configs/flat/vue3-recommended.js'), + 'flat/strongly-recommended': require('./configs/flat/vue3-strongly-recommended.js') + }, rules: { 'array-bracket-newline': require('./rules/array-bracket-newline'), 'array-bracket-spacing': require('./rules/array-bracket-spacing'), @@ -251,7 +273,8 @@ const plugin = { 'valid-v-text': require('./rules/valid-v-text') }, processors: { - '.vue': require('./processor') + '.vue': require('./processor'), + vue: require('./processor') }, environments: { // TODO Remove in the next major version @@ -267,53 +290,4 @@ const plugin = { } } -const baseConfig = { plugins: { vue: plugin } } - -plugin.configs = { - // eslintrc configs - base: require('./configs/base'), - essential: require('./configs/vue2-essential'), - 'no-layout-rules': require('./configs/no-layout-rules'), - recommended: require('./configs/vue2-recommended'), - 'strongly-recommended': require('./configs/vue2-strongly-recommended'), - 'vue3-essential': require('./configs/vue3-essential'), - 'vue3-recommended': require('./configs/vue3-recommended'), - 'vue3-strongly-recommended': require('./configs/vue3-strongly-recommended'), - - // flat configs - 'flat/base': Object.assign({}, baseConfig, require('./configs/flat/base.js')), - 'flat/vue2-essential': Object.assign( - {}, - baseConfig, - require('./configs/flat/vue2-essential.js') - ), - 'flat/vue2-recommended': Object.assign( - {}, - baseConfig, - require('./configs/flat/vue2-recommended.js') - ), - 'flat/vue2-strongly-recommended': Object.assign( - {}, - baseConfig, - require('./configs/flat/vue2-strongly-recommended.js') - ), - - // in flat configs, non-prefixed config is for Vue 3 (unlike eslintrc configs) - 'flat/essential': Object.assign( - {}, - baseConfig, - require('./configs/flat/vue3-essential.js') - ), - 'flat/recommended': Object.assign( - {}, - baseConfig, - require('./configs/flat/vue3-recommended.js') - ), - 'flat/strongly-recommended': Object.assign( - {}, - baseConfig, - require('./configs/flat/vue3-strongly-recommended.js') - ) -} - module.exports = plugin diff --git a/lib/utils/config-helpers.js b/lib/utils/config-helpers.js deleted file mode 100644 index cfee23386..000000000 --- a/lib/utils/config-helpers.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @fileoverview eslint config helpers - * @author 唯然 - */ - -function extendRules(config, rules) { - const { rules: extendedRules = {}, ...rest } = config - return { - ...rest, - rules: { - ...extendedRules, - ...rules - } - } -} - -module.exports = { - extendRules -} diff --git a/package.json b/package.json index 23ca9bb71..8c6dff8c4 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start": "npm run test:base -- --watch --growl", "test:base": "mocha \"tests/lib/**/*.js\" --reporter dot", "test": "nyc npm run test:base -- \"tests/integrations/*.js\" --timeout 60000", + "test:integrations": "mocha \"tests/integrations/*.js\" --timeout 60000", "debug": "mocha --inspect \"tests/lib/**/*.js\" --reporter dot --timeout 60000", "cover": "npm run cover:test && npm run cover:report", "cover:test": "nyc npm run test:base -- --timeout 60000", diff --git a/tests/eslint-compat.js b/tests/eslint-compat.js index fe77994ad..f553204cb 100644 --- a/tests/eslint-compat.js +++ b/tests/eslint-compat.js @@ -28,35 +28,57 @@ function getESLintClassForV8(BaseESLintClass = eslint.ESLint) { } } - // eslint-disable-next-line unicorn/consistent-function-scoping function adjustOptions(options) { - const newOptions = { - ...options - } - if (newOptions.overrideConfigFile === true) { - newOptions.useEslintrc = false - delete newOptions.overrideConfigFile - } - if (newOptions.overrideConfig) { - newOptions.overrideConfig = { ...newOptions.overrideConfig } - let plugins - if (newOptions.overrideConfig.plugins) { - plugins = newOptions.overrideConfig.plugins - delete newOptions.overrideConfig.plugins + const { + overrideConfig: originalOverrideConfig, + overrideConfigFile, + ...newOptions + } = options || {} + + if (overrideConfigFile) { + if (overrideConfigFile === true) { + newOptions.useEslintrc = false + } else { + newOptions.overrideConfigFile = overrideConfigFile } - newOptions.overrideConfig = processCompatibleConfig( - newOptions.overrideConfig + } + + if (originalOverrideConfig) { + const [overrideConfig, plugins] = convertFlatConfigToV8OverridesConfig( + originalOverrideConfig ) - if (plugins) { - newOptions.overrideConfig.plugins = Object.keys(plugins) - newOptions.plugins = plugins + newOptions.overrideConfig = overrideConfig + newOptions.plugins = plugins + } + return newOptions + } + + // eslint-disable-next-line unicorn/consistent-function-scoping + function convertFlatConfigToV8OverridesConfig(config) { + const pluginDefs = {} + const newConfigs = [] + for (const configItem of Array.isArray(config) ? config : [config]) { + const { plugins, ...otherConfig } = configItem + + if (typeof otherConfig.processor !== 'string') { + // Remove unsupported object processor option + // (I don't know how to successfully convert the processors at now.) + delete otherConfig.processor + } + + const newConfig = { + files: ['*'], + ...processCompatibleConfig(otherConfig) } - // adjust - delete newOptions.overrideConfig.files - delete newOptions.overrideConfig.processor + if (plugins) { + newConfig.plugins = Object.keys(plugins) + } + Object.assign(pluginDefs, plugins) + newConfigs.push(newConfig) } - return newOptions + + return [{ overrides: newConfigs }, pluginDefs] } } /** @returns {typeof eslint.ESLint} */ @@ -167,7 +189,6 @@ function getRuleTesterClassForV8() { return processCompatibleConfig(test) } } - function processCompatibleConfig(config, linter) { const newConfig = { ...config } if (newConfig.languageOptions) { diff --git a/tests/integrations/flat-config.js b/tests/integrations/flat-config.js new file mode 100644 index 000000000..85feb882f --- /dev/null +++ b/tests/integrations/flat-config.js @@ -0,0 +1,43 @@ +'use strict' + +const { strict: assert } = require('assert') +const cp = require('child_process') +const path = require('path') +const semver = require('semver') + +const ESLINT = `.${path.sep}node_modules${path.sep}.bin${path.sep}eslint` + +describe('Integration with flat config', () => { + let originalCwd + + before(() => { + originalCwd = process.cwd() + process.chdir(path.join(__dirname, 'flat-config')) + cp.execSync('npm i -f', { stdio: 'inherit' }) + }) + after(() => { + process.chdir(originalCwd) + }) + + it('should lint without errors', () => { + if ( + !semver.satisfies( + process.version, + require( + path.join(__dirname, 'flat-config/node_modules/eslint/package.json') + ).engines.node + ) + ) { + return + } + + const result = JSON.parse( + cp.execSync(`${ESLINT} a.vue --format=json`, { + encoding: 'utf8' + }) + ) + // console.log(JSON.stringify(result, null, 2)) + assert.strictEqual(result.length, 1) + assert.deepStrictEqual(result[0].messages, []) + }) +}) diff --git a/tests/integrations/flat-config/.npmrc b/tests/integrations/flat-config/.npmrc new file mode 100644 index 000000000..43c97e719 --- /dev/null +++ b/tests/integrations/flat-config/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/tests/integrations/flat-config/a.vue b/tests/integrations/flat-config/a.vue new file mode 100644 index 000000000..0470d17d3 --- /dev/null +++ b/tests/integrations/flat-config/a.vue @@ -0,0 +1,12 @@ + + + + diff --git a/tests/integrations/flat-config/eslint.config.js b/tests/integrations/flat-config/eslint.config.js new file mode 100644 index 000000000..9fed1e61d --- /dev/null +++ b/tests/integrations/flat-config/eslint.config.js @@ -0,0 +1,9 @@ +const plugin = require('eslint-plugin-vue') +module.exports = [ + ...plugin.configs['flat/base'], + { + rules: { + 'vue/no-duplicate-attributes': 'error' + } + } +] diff --git a/tests/integrations/flat-config/package.json b/tests/integrations/flat-config/package.json new file mode 100644 index 000000000..e46b5e1b6 --- /dev/null +++ b/tests/integrations/flat-config/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "name": "integration-test-for-flat-config", + "version": "1.0.0", + "description": "Integration test for flat config", + "scripts": {}, + "keywords": [], + "license": "MIT", + "dependencies": { + "eslint": "^9.0.0-0", + "eslint-plugin-vue": "file:../../.." + } +} diff --git a/tests/lib/configs/flat.js b/tests/lib/configs/flat.js index 7d9a97d26..f1b2d4101 100644 --- a/tests/lib/configs/flat.js +++ b/tests/lib/configs/flat.js @@ -7,24 +7,74 @@ const plugin = require('../../../lib/index') const { strict: assert } = require('assert') // node v14 does not support 'assert/strict' +const { ESLint } = require('../../eslint-compat') + +function mergeConfig(configs) { + let config = { rules: {}, plugins: {} } + for (const item of configs) { + config = { + ...config, + ...item, + plugins: { + ...config.plugins, + ...item?.plugins + }, + rules: { + ...config.rules, + ...item?.rules + } + } + } + return config +} describe('flat configs', () => { it('should export base config', () => { const base = plugin.configs['flat/base'] assert.ok(base) assert.equal(typeof base, 'object') - assert.strictEqual(base.plugins.vue, plugin) - assert.strictEqual(base.rules['vue/comment-directive'], 'error') + + const forVue = mergeConfig( + base.filter((config) => config.files?.includes('*.vue') || !config.files) + ) + assert.strictEqual(forVue.plugins.vue, plugin) + assert.strictEqual(forVue.processor, 'vue/vue') + assert.strictEqual(forVue.rules['vue/comment-directive'], 'error') + + const forOtherThanVue = mergeConfig( + base.filter((config) => !config.files?.includes('*.vue')) + ) + assert.strictEqual(forOtherThanVue.plugins.vue, plugin) + assert.strictEqual( + forOtherThanVue.rules['vue/comment-directive'], + undefined + ) }) it('should export essential config', () => { const essential = plugin.configs['flat/essential'] assert.ok(essential) assert.equal(typeof essential, 'object') - assert.strictEqual(essential.plugins.vue, plugin) - assert.strictEqual(essential.rules['vue/comment-directive'], 'error') + + const forVue = mergeConfig( + essential.filter( + (config) => config.files?.includes('*.vue') || !config.files + ) + ) + assert.strictEqual(forVue.plugins.vue, plugin) + assert.strictEqual(forVue.rules['vue/comment-directive'], 'error') + assert.strictEqual(forVue.rules['vue/multi-word-component-names'], 'error') + + const forOtherThanVue = mergeConfig( + essential.filter((config) => !config.files?.includes('*.vue')) + ) + assert.strictEqual(forOtherThanVue.plugins.vue, plugin) + assert.strictEqual( + forOtherThanVue.rules['vue/comment-directive'], + undefined + ) assert.strictEqual( - essential.rules['vue/multi-word-component-names'], + forOtherThanVue.rules['vue/multi-word-component-names'], 'error' ) }) @@ -33,13 +83,26 @@ describe('flat configs', () => { const stronglyRecommended = plugin.configs['flat/vue2-strongly-recommended'] assert.ok(stronglyRecommended) assert.equal(typeof stronglyRecommended, 'object') - assert.strictEqual(stronglyRecommended.plugins.vue, plugin) + + const forVue = mergeConfig( + stronglyRecommended.filter( + (config) => config.files?.includes('*.vue') || !config.files + ) + ) + assert.strictEqual(forVue.plugins.vue, plugin) + assert.strictEqual(forVue.rules['vue/comment-directive'], 'error') + assert.strictEqual(forVue.rules['vue/multi-word-component-names'], 'error') + + const forOtherThanVue = mergeConfig( + stronglyRecommended.filter((config) => !config.files?.includes('*.vue')) + ) + assert.strictEqual(forOtherThanVue.plugins.vue, plugin) assert.strictEqual( - stronglyRecommended.rules['vue/comment-directive'], - 'error' + forOtherThanVue.rules['vue/comment-directive'], + undefined ) assert.strictEqual( - stronglyRecommended.rules['vue/multi-word-component-names'], + forOtherThanVue.rules['vue/multi-word-component-names'], 'error' ) }) @@ -48,23 +111,56 @@ describe('flat configs', () => { const recommended = plugin.configs['flat/recommended'] assert.ok(recommended) assert.equal(typeof recommended, 'object') - assert.strictEqual(recommended.plugins.vue, plugin) - assert.strictEqual(recommended.rules['vue/comment-directive'], 'error') + + const forVue = mergeConfig( + recommended.filter( + (config) => config.files?.includes('*.vue') || !config.files + ) + ) + assert.strictEqual(forVue.plugins.vue, plugin) + assert.strictEqual(forVue.rules['vue/comment-directive'], 'error') + assert.strictEqual(forVue.rules['vue/multi-word-component-names'], 'error') + assert.strictEqual(forVue.rules['vue/attributes-order'], 'warn') + + const forOtherThanVue = mergeConfig( + recommended.filter((config) => !config.files?.includes('*.vue')) + ) + assert.strictEqual(forOtherThanVue.plugins.vue, plugin) + assert.strictEqual( + forOtherThanVue.rules['vue/comment-directive'], + undefined + ) assert.strictEqual( - recommended.rules['vue/multi-word-component-names'], + forOtherThanVue.rules['vue/multi-word-component-names'], 'error' ) - assert.strictEqual(recommended.rules['vue/attributes-order'], 'warn') + assert.strictEqual(forOtherThanVue.rules['vue/attributes-order'], 'warn') }) it('should export vue2-essential config', () => { const essential = plugin.configs['flat/vue2-essential'] assert.ok(essential) assert.equal(typeof essential, 'object') - assert.strictEqual(essential.plugins.vue, plugin) - assert.strictEqual(essential.rules['vue/comment-directive'], 'error') + + const forVue = mergeConfig( + essential.filter( + (config) => config.files?.includes('*.vue') || !config.files + ) + ) + assert.strictEqual(forVue.plugins.vue, plugin) + assert.strictEqual(forVue.rules['vue/comment-directive'], 'error') + assert.strictEqual(forVue.rules['vue/multi-word-component-names'], 'error') + + const forOtherThanVue = mergeConfig( + essential.filter((config) => !config.files?.includes('*.vue')) + ) + assert.strictEqual(forOtherThanVue.plugins.vue, plugin) + assert.strictEqual( + forOtherThanVue.rules['vue/comment-directive'], + undefined + ) assert.strictEqual( - essential.rules['vue/multi-word-component-names'], + forOtherThanVue.rules['vue/multi-word-component-names'], 'error' ) }) @@ -73,13 +169,26 @@ describe('flat configs', () => { const stronglyRecommended = plugin.configs['flat/vue2-strongly-recommended'] assert.ok(stronglyRecommended) assert.equal(typeof stronglyRecommended, 'object') - assert.strictEqual(stronglyRecommended.plugins.vue, plugin) + + const forVue = mergeConfig( + stronglyRecommended.filter( + (config) => config.files?.includes('*.vue') || !config.files + ) + ) + assert.strictEqual(forVue.plugins.vue, plugin) + assert.strictEqual(forVue.rules['vue/comment-directive'], 'error') + assert.strictEqual(forVue.rules['vue/multi-word-component-names'], 'error') + + const forOtherThanVue = mergeConfig( + stronglyRecommended.filter((config) => !config.files?.includes('*.vue')) + ) + assert.strictEqual(forOtherThanVue.plugins.vue, plugin) assert.strictEqual( - stronglyRecommended.rules['vue/comment-directive'], - 'error' + forOtherThanVue.rules['vue/comment-directive'], + undefined ) assert.strictEqual( - stronglyRecommended.rules['vue/multi-word-component-names'], + forOtherThanVue.rules['vue/multi-word-component-names'], 'error' ) }) @@ -88,12 +197,69 @@ describe('flat configs', () => { const recommended = plugin.configs['flat/vue2-recommended'] assert.ok(recommended) assert.equal(typeof recommended, 'object') - assert.strictEqual(recommended.plugins.vue, plugin) - assert.strictEqual(recommended.rules['vue/comment-directive'], 'error') + + const forVue = mergeConfig( + recommended.filter( + (config) => config.files?.includes('*.vue') || !config.files + ) + ) + assert.strictEqual(forVue.plugins.vue, plugin) + assert.strictEqual(forVue.rules['vue/comment-directive'], 'error') + assert.strictEqual(forVue.rules['vue/multi-word-component-names'], 'error') + assert.strictEqual(forVue.rules['vue/attributes-order'], 'warn') + + const forOtherThanVue = mergeConfig( + recommended.filter((config) => !config.files?.includes('*.vue')) + ) + assert.strictEqual(forOtherThanVue.plugins.vue, plugin) assert.strictEqual( - recommended.rules['vue/multi-word-component-names'], + forOtherThanVue.rules['vue/comment-directive'], + undefined + ) + assert.strictEqual( + forOtherThanVue.rules['vue/multi-word-component-names'], 'error' ) - assert.strictEqual(recommended.rules['vue/attributes-order'], 'warn') + assert.strictEqual(forOtherThanVue.rules['vue/attributes-order'], 'warn') + }) + + it('should work the suppress comments with base config', async () => { + const base = plugin.configs['flat/base'] + const eslint = new ESLint({ + overrideConfigFile: true, + overrideConfig: [ + ...base, + { + rules: { + 'vue/no-duplicate-attributes': 'error' + } + } + ] + }) + const code = ` + + ` + const result = await eslint.lintText(code, { filePath: 'test.vue' }) + + assert.deepStrictEqual(result[0].messages, []) + }) + it('should work the suppress comments with recommended config', async () => { + const recommended = plugin.configs['flat/recommended'] + const eslint = new ESLint({ + overrideConfigFile: true, + overrideConfig: recommended + }) + const code = ` + +` + const result = await eslint.lintText(code, { filePath: 'MyComponent.vue' }) + + assert.deepStrictEqual(result[0].messages, []) }) }) diff --git a/tools/update-lib-flat-configs.js b/tools/update-lib-flat-configs.js index 1266b7873..1ff5b9c16 100644 --- a/tools/update-lib-flat-configs.js +++ b/tools/update-lib-flat-configs.js @@ -49,22 +49,41 @@ function formatRules(rules, categoryId) { function formatCategory(category) { const extendsCategoryId = extendsCategories[category.categoryId] - if (extendsCategoryId == null) { + if (category.categoryId === 'base') { return `/* * IMPORTANT! * This file has been automatically generated, * in order to update its content execute "npm run update" */ const globals = require('globals') -module.exports = { - languageOptions: { - parser: require('vue-eslint-parser'), - sourceType: 'module', - globals: globals.browser +module.exports = [ + { + plugins: { + get vue() { + return require('../../index') + } + }, + languageOptions: { + sourceType: 'module', + globals: globals.browser + } }, - rules: ${formatRules(category.rules, category.categoryId)} -} - + { + files: ['*.vue', '**/*.vue'], + plugins: { + get vue() { + return require('../../index') + } + }, + languageOptions: { + parser: require('vue-eslint-parser'), + sourceType: 'module', + globals: globals.browser + }, + rules: ${formatRules(category.rules, category.categoryId)}, + processor: 'vue/vue' + } +] ` } return `/* @@ -74,11 +93,13 @@ module.exports = { */ 'use strict' const config = require('./${extendsCategoryId}.js') -const { extendRules } = require('../../utils/config-helpers.js') - -const rules = ${formatRules(category.rules, category.categoryId)} -module.exports = extendRules(config, rules) +module.exports = [ + ...config, + { + rules: ${formatRules(category.rules, category.categoryId)}, + } +] ` } diff --git a/tools/update-lib-index.js b/tools/update-lib-index.js index 399da68fd..427fd197e 100644 --- a/tools/update-lib-index.js +++ b/tools/update-lib-index.js @@ -25,13 +25,36 @@ const content = `/* const plugin = { meta: require('./meta'), + configs: { + // eslintrc configs + base: require('./configs/base'), + essential: require('./configs/vue2-essential'), + 'no-layout-rules': require('./configs/no-layout-rules'), + recommended: require('./configs/vue2-recommended'), + 'strongly-recommended': require('./configs/vue2-strongly-recommended'), + 'vue3-essential': require('./configs/vue3-essential'), + 'vue3-recommended': require('./configs/vue3-recommended'), + 'vue3-strongly-recommended': require('./configs/vue3-strongly-recommended'), + + // flat configs + 'flat/base': require('./configs/flat/base.js'), + 'flat/vue2-essential': require('./configs/flat/vue2-essential.js'), + 'flat/vue2-recommended': require('./configs/flat/vue2-recommended.js'), + 'flat/vue2-strongly-recommended': require('./configs/flat/vue2-strongly-recommended.js'), + + // in flat configs, non-prefixed config is for Vue 3 (unlike eslintrc configs) + 'flat/essential': require('./configs/flat/vue3-essential.js'), + 'flat/recommended': require('./configs/flat/vue3-recommended.js'), + 'flat/strongly-recommended': require('./configs/flat/vue3-strongly-recommended.js'), + }, rules: { ${rules .map((rule) => `'${rule.name}': require('./rules/${rule.name}')`) .join(',\n')} }, processors: { - '.vue': require('./processor') + '.vue': require('./processor'), + 'vue': require('./processor') }, environments: { // TODO Remove in the next major version @@ -47,31 +70,6 @@ const plugin = { } } -const baseConfig = {plugins: {vue: plugin}} - -plugin.configs = { - // eslintrc configs - base: require('./configs/base'), - essential: require('./configs/vue2-essential'), - 'no-layout-rules': require('./configs/no-layout-rules'), - recommended: require('./configs/vue2-recommended'), - 'strongly-recommended': require('./configs/vue2-strongly-recommended'), - 'vue3-essential': require('./configs/vue3-essential'), - 'vue3-recommended': require('./configs/vue3-recommended'), - 'vue3-strongly-recommended': require('./configs/vue3-strongly-recommended'), - - // flat configs - 'flat/base': Object.assign({}, baseConfig, require('./configs/flat/base.js')), - 'flat/vue2-essential': Object.assign({}, baseConfig, require('./configs/flat/vue2-essential.js')), - 'flat/vue2-recommended': Object.assign({}, baseConfig, require('./configs/flat/vue2-recommended.js')), - 'flat/vue2-strongly-recommended': Object.assign({}, baseConfig, require('./configs/flat/vue2-strongly-recommended.js')), - - // in flat configs, non-prefixed config is for Vue 3 (unlike eslintrc configs) - 'flat/essential': Object.assign({}, baseConfig, require('./configs/flat/vue3-essential.js')), - 'flat/recommended': Object.assign({}, baseConfig, require('./configs/flat/vue3-recommended.js')), - 'flat/strongly-recommended': Object.assign({}, baseConfig, require('./configs/flat/vue3-strongly-recommended.js')), -} - module.exports = plugin ` fs.writeFileSync(filePath, content) From ca8125f4b4fff1bb8dd3565e2e6efb6e01bf8539 Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Mon, 25 Mar 2024 11:53:33 +0900 Subject: [PATCH 2/8] fix test case --- tests/eslint-compat.js | 77 ++++++++++++++++----------------------- tests/lib/configs/flat.js | 8 ++-- 2 files changed, 37 insertions(+), 48 deletions(-) diff --git a/tests/eslint-compat.js b/tests/eslint-compat.js index f553204cb..e3f15fbc4 100644 --- a/tests/eslint-compat.js +++ b/tests/eslint-compat.js @@ -3,16 +3,25 @@ const eslint = require('eslint') const semver = require('semver') let ESLint = eslint.ESLint +/** @type {typeof eslint.ESLint | null} */ +let FlatESLint = eslint.ESLint let Linter = eslint.Linter let RuleTester = eslint.RuleTester if (semver.lt(eslint.Linter.version, '9.0.0-0')) { ESLint = eslint.ESLint ? getESLintClassForV8() : getESLintClassForV6() Linter = getLinterClassForV8() RuleTester = getRuleTesterClassForV8() + try { + // @ts-ignore + FlatESLint = require('eslint/use-at-your-own-risk').FlatESLint + } catch { + FlatESLint = null + } } module.exports = { ESLint, + FlatESLint, RuleTester, Linter } @@ -28,57 +37,35 @@ function getESLintClassForV8(BaseESLintClass = eslint.ESLint) { } } + // eslint-disable-next-line unicorn/consistent-function-scoping function adjustOptions(options) { - const { - overrideConfig: originalOverrideConfig, - overrideConfigFile, - ...newOptions - } = options || {} - - if (overrideConfigFile) { - if (overrideConfigFile === true) { - newOptions.useEslintrc = false - } else { - newOptions.overrideConfigFile = overrideConfigFile + const newOptions = { + ...options + } + if (newOptions.overrideConfigFile === true) { + newOptions.useEslintrc = false + delete newOptions.overrideConfigFile + } + if (newOptions.overrideConfig) { + newOptions.overrideConfig = { ...newOptions.overrideConfig } + let plugins + if (newOptions.overrideConfig.plugins) { + plugins = newOptions.overrideConfig.plugins + delete newOptions.overrideConfig.plugins } - } - - if (originalOverrideConfig) { - const [overrideConfig, plugins] = convertFlatConfigToV8OverridesConfig( - originalOverrideConfig + newOptions.overrideConfig = processCompatibleConfig( + newOptions.overrideConfig ) - newOptions.overrideConfig = overrideConfig - newOptions.plugins = plugins - } - return newOptions - } - - // eslint-disable-next-line unicorn/consistent-function-scoping - function convertFlatConfigToV8OverridesConfig(config) { - const pluginDefs = {} - const newConfigs = [] - for (const configItem of Array.isArray(config) ? config : [config]) { - const { plugins, ...otherConfig } = configItem - - if (typeof otherConfig.processor !== 'string') { - // Remove unsupported object processor option - // (I don't know how to successfully convert the processors at now.) - delete otherConfig.processor - } - - const newConfig = { - files: ['*'], - ...processCompatibleConfig(otherConfig) - } - if (plugins) { - newConfig.plugins = Object.keys(plugins) + newOptions.overrideConfig.plugins = Object.keys(plugins) + newOptions.plugins = plugins } - Object.assign(pluginDefs, plugins) - newConfigs.push(newConfig) - } - return [{ overrides: newConfigs }, pluginDefs] + // adjust + delete newOptions.overrideConfig.files + delete newOptions.overrideConfig.processor + } + return newOptions } } /** @returns {typeof eslint.ESLint} */ diff --git a/tests/lib/configs/flat.js b/tests/lib/configs/flat.js index f1b2d4101..2ba1a6aa2 100644 --- a/tests/lib/configs/flat.js +++ b/tests/lib/configs/flat.js @@ -7,7 +7,7 @@ const plugin = require('../../../lib/index') const { strict: assert } = require('assert') // node v14 does not support 'assert/strict' -const { ESLint } = require('../../eslint-compat') +const { FlatESLint } = require('../../eslint-compat') function mergeConfig(configs) { let config = { rules: {}, plugins: {} } @@ -224,8 +224,9 @@ describe('flat configs', () => { }) it('should work the suppress comments with base config', async () => { + if (!FlatESLint) return const base = plugin.configs['flat/base'] - const eslint = new ESLint({ + const eslint = new FlatESLint({ overrideConfigFile: true, overrideConfig: [ ...base, @@ -247,8 +248,9 @@ describe('flat configs', () => { assert.deepStrictEqual(result[0].messages, []) }) it('should work the suppress comments with recommended config', async () => { + if (!FlatESLint) return const recommended = plugin.configs['flat/recommended'] - const eslint = new ESLint({ + const eslint = new FlatESLint({ overrideConfigFile: true, overrideConfig: recommended }) From e95d20b8850eb17dd1076a5af4b714d5540f55b5 Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Mon, 25 Mar 2024 12:04:22 +0900 Subject: [PATCH 3/8] fix site --- docs/.vitepress/vite-plugin.mts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/.vitepress/vite-plugin.mts b/docs/.vitepress/vite-plugin.mts index fd0644ed5..ba1ca7db0 100644 --- a/docs/.vitepress/vite-plugin.mts +++ b/docs/.vitepress/vite-plugin.mts @@ -65,7 +65,7 @@ function transformRequire(code: string) { id += Math.random().toString(32).substring(2) } modules.set(id, moduleString) - return id + return id + '()' } ) @@ -73,7 +73,7 @@ function transformRequire(code: string) { [...modules] .map(([id, moduleString]) => { return `import * as __temp_${id} from ${moduleString}; -const ${id} = __temp_${id}.default || __temp_${id}; +const ${id} = () => __temp_${id}.default || __temp_${id}; ` }) .join('') + From 1438a8dfcb591df64e7a58503c7b959dc9b2fddd Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Mon, 25 Mar 2024 12:33:35 +0900 Subject: [PATCH 4/8] add test --- tests/lib/configs/flat.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/lib/configs/flat.js b/tests/lib/configs/flat.js index 2ba1a6aa2..c7c93d4ce 100644 --- a/tests/lib/configs/flat.js +++ b/tests/lib/configs/flat.js @@ -264,4 +264,29 @@ describe('flat configs', () => { assert.deepStrictEqual(result[0].messages, []) }) + it('should error with recommended config', async () => { + if (!FlatESLint) return + const recommended = plugin.configs['flat/recommended'] + const eslint = new FlatESLint({ + overrideConfigFile: true, + overrideConfig: recommended + }) + const code = ` + +` + const result = await eslint.lintText(code, { filePath: 'MyComponent.vue' }) + + assert.deepStrictEqual( + result[0].messages.map((message) => message.ruleId), + [ + 'vue/no-parsing-error', + 'vue/max-attributes-per-line', + 'vue/no-duplicate-attributes', + 'vue/singleline-html-element-content-newline', + 'vue/singleline-html-element-content-newline' + ] + ) + }) }) From ac273f7ac5d84b8808aa5471e84edaeb8d4bfa2d Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Mon, 25 Mar 2024 15:48:51 +0900 Subject: [PATCH 5/8] update docs and remove unnecessary comment --- docs/rules/attribute-hyphenation.md | 2 +- docs/rules/attributes-order.md | 2 +- docs/rules/comment-directive.md | 2 +- .../rules/component-definition-name-casing.md | 2 +- docs/rules/component-tags-order.md | 2 +- docs/rules/first-attribute-linebreak.md | 2 +- docs/rules/html-closing-bracket-newline.md | 2 +- docs/rules/html-closing-bracket-spacing.md | 2 +- docs/rules/html-end-tags.md | 2 +- docs/rules/html-indent.md | 2 +- docs/rules/html-quotes.md | 2 +- docs/rules/html-self-closing.md | 2 +- docs/rules/index.md | 16 +++---- docs/rules/jsx-uses-vars.md | 2 +- docs/rules/max-attributes-per-line.md | 2 +- docs/rules/multi-word-component-names.md | 2 +- .../multiline-html-element-content-newline.md | 2 +- docs/rules/mustache-interpolation-spacing.md | 2 +- docs/rules/no-arrow-functions-in-watch.md | 2 +- docs/rules/no-async-in-computed-properties.md | 2 +- docs/rules/no-child-content.md | 2 +- docs/rules/no-computed-properties-in-data.md | 2 +- docs/rules/no-custom-modifiers-on-v-model.md | 2 +- .../no-deprecated-data-object-declaration.md | 2 +- .../no-deprecated-destroyed-lifecycle.md | 2 +- .../no-deprecated-dollar-listeners-api.md | 2 +- .../no-deprecated-dollar-scopedslots-api.md | 2 +- docs/rules/no-deprecated-events-api.md | 2 +- docs/rules/no-deprecated-filter.md | 2 +- .../no-deprecated-functional-template.md | 2 +- docs/rules/no-deprecated-html-element-is.md | 2 +- docs/rules/no-deprecated-inline-template.md | 2 +- .../rules/no-deprecated-props-default-this.md | 2 +- .../no-deprecated-router-link-tag-prop.md | 2 +- docs/rules/no-deprecated-scope-attribute.md | 2 +- docs/rules/no-deprecated-slot-attribute.md | 2 +- .../no-deprecated-slot-scope-attribute.md | 2 +- docs/rules/no-deprecated-v-bind-sync.md | 2 +- docs/rules/no-deprecated-v-is.md | 2 +- .../no-deprecated-v-on-native-modifier.md | 2 +- .../no-deprecated-v-on-number-modifiers.md | 2 +- .../no-deprecated-vue-config-keycodes.md | 2 +- docs/rules/no-dupe-keys.md | 2 +- docs/rules/no-dupe-v-else-if.md | 2 +- docs/rules/no-duplicate-attributes.md | 2 +- docs/rules/no-export-in-script-setup.md | 2 +- docs/rules/no-expose-after-await.md | 2 +- docs/rules/no-lifecycle-after-await.md | 2 +- docs/rules/no-lone-template.md | 2 +- docs/rules/no-multi-spaces.md | 2 +- docs/rules/no-multiple-slot-args.md | 2 +- docs/rules/no-multiple-template-root.md | 2 +- docs/rules/no-mutating-props.md | 2 +- docs/rules/no-parsing-error.md | 2 +- docs/rules/no-ref-as-operand.md | 2 +- docs/rules/no-reserved-component-names.md | 2 +- docs/rules/no-reserved-keys.md | 2 +- docs/rules/no-reserved-props.md | 2 +- docs/rules/no-shared-component-data.md | 2 +- .../no-side-effects-in-computed-properties.md | 2 +- ...-spaces-around-equal-signs-in-attribute.md | 2 +- docs/rules/no-template-key.md | 2 +- docs/rules/no-template-shadow.md | 2 +- docs/rules/no-textarea-mustache.md | 2 +- docs/rules/no-unused-components.md | 2 +- docs/rules/no-unused-vars.md | 2 +- .../no-use-computed-property-like-method.md | 2 +- docs/rules/no-use-v-if-with-v-for.md | 2 +- docs/rules/no-useless-template-attributes.md | 2 +- docs/rules/no-v-for-template-key-on-child.md | 2 +- docs/rules/no-v-for-template-key.md | 2 +- docs/rules/no-v-html.md | 2 +- docs/rules/no-v-model-argument.md | 2 +- docs/rules/no-v-text-v-html-on-component.md | 2 +- docs/rules/no-watch-after-await.md | 2 +- docs/rules/one-component-per-file.md | 2 +- docs/rules/order-in-components.md | 2 +- docs/rules/prefer-import-from-vue.md | 2 +- docs/rules/prop-name-casing.md | 2 +- docs/rules/require-component-is.md | 2 +- docs/rules/require-default-prop.md | 2 +- docs/rules/require-explicit-emits.md | 2 +- docs/rules/require-prop-type-constructor.md | 2 +- docs/rules/require-prop-types.md | 2 +- docs/rules/require-render-return.md | 2 +- docs/rules/require-slots-as-functions.md | 2 +- .../rules/require-toggle-inside-transition.md | 2 +- docs/rules/require-v-for-key.md | 2 +- docs/rules/require-valid-default-prop.md | 2 +- docs/rules/return-in-computed-property.md | 2 +- docs/rules/return-in-emits-validator.md | 2 +- ...singleline-html-element-content-newline.md | 2 +- docs/rules/this-in-template.md | 2 +- docs/rules/use-v-on-exact.md | 2 +- docs/rules/v-bind-style.md | 2 +- docs/rules/v-on-event-hyphenation.md | 2 +- docs/rules/v-on-style.md | 2 +- docs/rules/v-slot-style.md | 2 +- docs/rules/valid-attribute-name.md | 2 +- docs/rules/valid-define-emits.md | 2 +- docs/rules/valid-define-props.md | 2 +- docs/rules/valid-model-definition.md | 2 +- docs/rules/valid-next-tick.md | 2 +- docs/rules/valid-template-root.md | 2 +- docs/rules/valid-v-bind-sync.md | 2 +- docs/rules/valid-v-bind.md | 2 +- docs/rules/valid-v-cloak.md | 2 +- docs/rules/valid-v-else-if.md | 2 +- docs/rules/valid-v-else.md | 2 +- docs/rules/valid-v-for.md | 2 +- docs/rules/valid-v-html.md | 2 +- docs/rules/valid-v-if.md | 2 +- docs/rules/valid-v-is.md | 2 +- docs/rules/valid-v-memo.md | 2 +- docs/rules/valid-v-model.md | 2 +- docs/rules/valid-v-on.md | 2 +- docs/rules/valid-v-once.md | 2 +- docs/rules/valid-v-pre.md | 2 +- docs/rules/valid-v-show.md | 2 +- docs/rules/valid-v-slot.md | 2 +- docs/rules/valid-v-text.md | 2 +- docs/user-guide/index.md | 46 ++++++++++++++++++- tests/integrations/flat-config.js | 1 - tools/lib/categories.js | 45 +++++++++++++++--- tools/update-docs-rules-index.js | 7 +-- tools/update-docs.js | 7 ++- tools/update-lib-configs.js | 2 +- tools/update-lib-flat-configs.js | 2 +- 128 files changed, 220 insertions(+), 146 deletions(-) diff --git a/docs/rules/attribute-hyphenation.md b/docs/rules/attribute-hyphenation.md index 5c2de0027..d5fba2e31 100644 --- a/docs/rules/attribute-hyphenation.md +++ b/docs/rules/attribute-hyphenation.md @@ -10,7 +10,7 @@ since: v3.9.0 > enforce attribute naming style on custom components in template -- :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/vue3-strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue3-recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/vue2-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. ## :book: Rule Details diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md index a9cf47288..366f134b8 100644 --- a/docs/rules/attributes-order.md +++ b/docs/rules/attributes-order.md @@ -10,7 +10,7 @@ since: v4.3.0 > enforce order of attributes -- :gear: This rule is included in `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`. +- :gear: This rule is included in all of `"plugin:vue/vue3-recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/vue2-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. ## :book: Rule Details diff --git a/docs/rules/comment-directive.md b/docs/rules/comment-directive.md index b95f31668..9bc227a6f 100644 --- a/docs/rules/comment-directive.md +++ b/docs/rules/comment-directive.md @@ -10,7 +10,7 @@ since: v4.1.0 > support comment-directives in `