diff --git a/.changeset/brown-eels-care.md b/.changeset/brown-eels-care.md new file mode 100644 index 000000000..6d26027f0 --- /dev/null +++ b/.changeset/brown-eels-care.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-vue": minor +--- + +Use `error` instead of `warn` in configs when `VUE_ESLINT_ALWAYS_ERROR` env variable is `"true"` diff --git a/docs/user-guide/index.md b/docs/user-guide/index.md index 103471f61..85acc40b9 100644 --- a/docs/user-guide/index.md +++ b/docs/user-guide/index.md @@ -187,14 +187,20 @@ You can use the following configs by adding them to `extends`. - 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/strongly-recommended-error"` ... Above, except with all rules configured to error instead of warn. - `"plugin:vue/recommended"` ... Above, plus rules to enforce subjective community defaults to ensure consistency. + - `"plugin:vue/recommended-error"` ... Above, except with all rules configured to error instead of warn. - 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-strongly-recommended-error"` ... Above, except with all rules configured to error instead of warn. - `"plugin:vue/vue2-recommended"` ... Above, plus rules to enforce subjective community defaults to ensure consistency. + - `"plugin:vue/vue2-recommended-error"` ... Above, except with all rules configured to error instead of warn. :::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). + +Alternatively, there are versions of the configs with all rules set to error suffixed with `-error` that you can use. ::: :::warning Status of Vue.js 3.x supports diff --git a/lib/configs/flat/vue2-recommended-error.js b/lib/configs/flat/vue2-recommended-error.js new file mode 100644 index 000000000..f9bc69c0a --- /dev/null +++ b/lib/configs/flat/vue2-recommended-error.js @@ -0,0 +1,24 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +'use strict' +const config = require('./vue2-strongly-recommended-error.js') + +module.exports = [ + ...config, + { + name: 'vue/vue2-recommended/rules', + rules: { + 'vue/attributes-order': 'error', + 'vue/block-order': 'error', + 'vue/no-lone-template': 'error', + 'vue/no-multiple-slot-args': 'error', + 'vue/no-required-prop-with-default': 'error', + 'vue/no-v-html': 'error', + 'vue/order-in-components': 'error', + 'vue/this-in-template': 'error' + } + } +] diff --git a/lib/configs/flat/vue2-strongly-recommended-error.js b/lib/configs/flat/vue2-strongly-recommended-error.js new file mode 100644 index 000000000..176afa681 --- /dev/null +++ b/lib/configs/flat/vue2-strongly-recommended-error.js @@ -0,0 +1,39 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +'use strict' +const config = require('./vue2-essential.js') + +module.exports = [ + ...config, + { + name: 'vue/vue2-strongly-recommended/rules', + rules: { + 'vue/attribute-hyphenation': 'error', + 'vue/component-definition-name-casing': 'error', + 'vue/first-attribute-linebreak': 'error', + 'vue/html-closing-bracket-newline': 'error', + 'vue/html-closing-bracket-spacing': 'error', + 'vue/html-end-tags': 'error', + 'vue/html-indent': 'error', + 'vue/html-quotes': 'error', + 'vue/html-self-closing': 'error', + 'vue/max-attributes-per-line': 'error', + 'vue/multiline-html-element-content-newline': 'error', + 'vue/mustache-interpolation-spacing': 'error', + 'vue/no-multi-spaces': 'error', + 'vue/no-spaces-around-equal-signs-in-attribute': 'error', + 'vue/no-template-shadow': 'error', + 'vue/one-component-per-file': 'error', + 'vue/prop-name-casing': 'error', + 'vue/require-default-prop': 'error', + 'vue/require-prop-types': 'error', + 'vue/singleline-html-element-content-newline': 'error', + 'vue/v-bind-style': 'error', + 'vue/v-on-style': 'error', + 'vue/v-slot-style': 'error' + } + } +] diff --git a/lib/configs/flat/vue3-recommended-error.js b/lib/configs/flat/vue3-recommended-error.js new file mode 100644 index 000000000..232e6b838 --- /dev/null +++ b/lib/configs/flat/vue3-recommended-error.js @@ -0,0 +1,24 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +'use strict' +const config = require('./vue3-strongly-recommended-error.js') + +module.exports = [ + ...config, + { + name: 'vue/recommended/rules', + rules: { + 'vue/attributes-order': 'error', + 'vue/block-order': 'error', + 'vue/no-lone-template': 'error', + 'vue/no-multiple-slot-args': 'error', + 'vue/no-required-prop-with-default': 'error', + 'vue/no-v-html': 'error', + 'vue/order-in-components': 'error', + 'vue/this-in-template': 'error' + } + } +] diff --git a/lib/configs/flat/vue3-strongly-recommended-error.js b/lib/configs/flat/vue3-strongly-recommended-error.js new file mode 100644 index 000000000..883fc5501 --- /dev/null +++ b/lib/configs/flat/vue3-strongly-recommended-error.js @@ -0,0 +1,47 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +'use strict' +const config = require('./vue3-essential.js') + +module.exports = [ + ...config, + { + name: 'vue/strongly-recommended/rules', + rules: { + 'vue/attribute-hyphenation': 'error', + 'vue/component-definition-name-casing': 'error', + 'vue/first-attribute-linebreak': 'error', + 'vue/html-closing-bracket-newline': 'error', + 'vue/html-closing-bracket-spacing': 'error', + 'vue/html-end-tags': 'error', + 'vue/html-indent': 'error', + 'vue/html-quotes': 'error', + 'vue/html-self-closing': 'error', + 'vue/max-attributes-per-line': 'error', + 'vue/multiline-html-element-content-newline': 'error', + 'vue/mustache-interpolation-spacing': 'error', + 'vue/no-multi-spaces': 'error', + 'vue/no-spaces-around-equal-signs-in-attribute': 'error', + 'vue/no-template-shadow': 'error', + 'vue/one-component-per-file': 'error', + 'vue/prop-name-casing': 'error', + 'vue/require-default-prop': 'error', + 'vue/require-explicit-emits': 'error', + 'vue/require-prop-types': 'error', + 'vue/singleline-html-element-content-newline': 'error', + 'vue/v-bind-style': 'error', + 'vue/v-on-event-hyphenation': [ + 'error', + 'always', + { + autofix: true + } + ], + 'vue/v-on-style': 'error', + 'vue/v-slot-style': 'error' + } + } +] diff --git a/lib/configs/vue2-recommended-error.js b/lib/configs/vue2-recommended-error.js new file mode 100644 index 000000000..eb7c624fd --- /dev/null +++ b/lib/configs/vue2-recommended-error.js @@ -0,0 +1,18 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +module.exports = { + extends: require.resolve('./vue2-strongly-recommended-error'), + rules: { + 'vue/attributes-order': 'error', + 'vue/block-order': 'error', + 'vue/no-lone-template': 'error', + 'vue/no-multiple-slot-args': 'error', + 'vue/no-required-prop-with-default': 'error', + 'vue/no-v-html': 'error', + 'vue/order-in-components': 'error', + 'vue/this-in-template': 'error' + } +} diff --git a/lib/configs/vue2-strongly-recommended-error.js b/lib/configs/vue2-strongly-recommended-error.js new file mode 100644 index 000000000..482914c8c --- /dev/null +++ b/lib/configs/vue2-strongly-recommended-error.js @@ -0,0 +1,33 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +module.exports = { + extends: require.resolve('./vue2-essential'), + rules: { + 'vue/attribute-hyphenation': 'error', + 'vue/component-definition-name-casing': 'error', + 'vue/first-attribute-linebreak': 'error', + 'vue/html-closing-bracket-newline': 'error', + 'vue/html-closing-bracket-spacing': 'error', + 'vue/html-end-tags': 'error', + 'vue/html-indent': 'error', + 'vue/html-quotes': 'error', + 'vue/html-self-closing': 'error', + 'vue/max-attributes-per-line': 'error', + 'vue/multiline-html-element-content-newline': 'error', + 'vue/mustache-interpolation-spacing': 'error', + 'vue/no-multi-spaces': 'error', + 'vue/no-spaces-around-equal-signs-in-attribute': 'error', + 'vue/no-template-shadow': 'error', + 'vue/one-component-per-file': 'error', + 'vue/prop-name-casing': 'error', + 'vue/require-default-prop': 'error', + 'vue/require-prop-types': 'error', + 'vue/singleline-html-element-content-newline': 'error', + 'vue/v-bind-style': 'error', + 'vue/v-on-style': 'error', + 'vue/v-slot-style': 'error' + } +} diff --git a/lib/configs/vue3-recommended-error.js b/lib/configs/vue3-recommended-error.js new file mode 100644 index 000000000..23a3c21a2 --- /dev/null +++ b/lib/configs/vue3-recommended-error.js @@ -0,0 +1,18 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +module.exports = { + extends: require.resolve('./vue3-strongly-recommended-error'), + rules: { + 'vue/attributes-order': 'error', + 'vue/block-order': 'error', + 'vue/no-lone-template': 'error', + 'vue/no-multiple-slot-args': 'error', + 'vue/no-required-prop-with-default': 'error', + 'vue/no-v-html': 'error', + 'vue/order-in-components': 'error', + 'vue/this-in-template': 'error' + } +} diff --git a/lib/configs/vue3-strongly-recommended-error.js b/lib/configs/vue3-strongly-recommended-error.js new file mode 100644 index 000000000..b48e0deec --- /dev/null +++ b/lib/configs/vue3-strongly-recommended-error.js @@ -0,0 +1,41 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update its content execute "npm run update" + */ +module.exports = { + extends: require.resolve('./vue3-essential'), + rules: { + 'vue/attribute-hyphenation': 'error', + 'vue/component-definition-name-casing': 'error', + 'vue/first-attribute-linebreak': 'error', + 'vue/html-closing-bracket-newline': 'error', + 'vue/html-closing-bracket-spacing': 'error', + 'vue/html-end-tags': 'error', + 'vue/html-indent': 'error', + 'vue/html-quotes': 'error', + 'vue/html-self-closing': 'error', + 'vue/max-attributes-per-line': 'error', + 'vue/multiline-html-element-content-newline': 'error', + 'vue/mustache-interpolation-spacing': 'error', + 'vue/no-multi-spaces': 'error', + 'vue/no-spaces-around-equal-signs-in-attribute': 'error', + 'vue/no-template-shadow': 'error', + 'vue/one-component-per-file': 'error', + 'vue/prop-name-casing': 'error', + 'vue/require-default-prop': 'error', + 'vue/require-explicit-emits': 'error', + 'vue/require-prop-types': 'error', + 'vue/singleline-html-element-content-newline': 'error', + 'vue/v-bind-style': 'error', + 'vue/v-on-event-hyphenation': [ + 'error', + 'always', + { + autofix: true + } + ], + 'vue/v-on-style': 'error', + 'vue/v-slot-style': 'error' + } +} diff --git a/tools/update-lib-configs.js b/tools/update-lib-configs.js index 0c2845e45..a97f19cd2 100644 --- a/tools/update-lib-configs.js +++ b/tools/update-lib-configs.js @@ -28,10 +28,11 @@ const extendsCategories = { 'vue3-use-with-caution': 'vue3-recommended' } -function formatRules(rules, categoryId) { +function formatRules(rules, categoryId, alwaysError) { const obj = Object.fromEntries( rules.map((rule) => { - let options = errorCategories.has(categoryId) ? 'error' : 'warn' + let options = + alwaysError || errorCategories.has(categoryId) ? 'error' : 'warn' const defaultOptions = rule.meta && rule.meta.docs && rule.meta.docs.defaultOptions if (defaultOptions) { @@ -47,8 +48,16 @@ function formatRules(rules, categoryId) { return JSON.stringify(obj, null, 2) } -function formatCategory(category) { - const extendsCategoryId = extendsCategories[category.categoryId] +function hasWarningRules(categoryId) { + return ( + categoryId !== 'base' && + categoryId !== 'vue3-essential' && + categoryId !== 'vue2-essential' + ) +} + +function formatCategory(category, alwaysError = false) { + let extendsCategoryId = extendsCategories[category.categoryId] if (extendsCategoryId == null) { return `/* * IMPORTANT! @@ -63,7 +72,7 @@ module.exports = { plugins: [ 'vue' ], - rules: ${formatRules(category.rules, category.categoryId)}, + rules: ${formatRules(category.rules, category.categoryId, alwaysError)}, overrides: [ { files: '*.vue', @@ -73,6 +82,10 @@ module.exports = { } ` } + if (alwaysError && hasWarningRules(extendsCategoryId)) { + extendsCategoryId += '-error' + } + return `/* * IMPORTANT! * This file has been automatically generated, @@ -80,7 +93,7 @@ module.exports = { */ module.exports = { extends: require.resolve('./${extendsCategoryId}'), - rules: ${formatRules(category.rules, category.categoryId)} + rules: ${formatRules(category.rules, category.categoryId, alwaysError)} } ` } @@ -92,6 +105,13 @@ for (const category of categories) { const content = formatCategory(category) fs.writeFileSync(filePath, content) + + if (hasWarningRules(category.categoryId)) { + fs.writeFileSync( + path.join(ROOT, `${category.categoryId}-error.js`), + formatCategory(category, true) + ) + } } // Format files. diff --git a/tools/update-lib-flat-configs.js b/tools/update-lib-flat-configs.js index e55fb2d01..619f72653 100644 --- a/tools/update-lib-flat-configs.js +++ b/tools/update-lib-flat-configs.js @@ -28,10 +28,11 @@ const extendsCategories = { 'vue3-use-with-caution': 'vue3-recommended' } -function formatRules(rules, categoryId) { +function formatRules(rules, categoryId, alwaysError) { const obj = Object.fromEntries( rules.map((rule) => { - let options = errorCategories.has(categoryId) ? 'error' : 'warn' + let options = + alwaysError || errorCategories.has(categoryId) ? 'error' : 'warn' const defaultOptions = rule.meta && rule.meta.docs && rule.meta.docs.defaultOptions if (defaultOptions) { @@ -47,8 +48,17 @@ function formatRules(rules, categoryId) { return JSON.stringify(obj, null, 2) } -function formatCategory(category) { - const extendsCategoryId = extendsCategories[category.categoryId] +function hasWarningRules(categoryId) { + return ( + categoryId !== 'base' && + categoryId !== 'vue3-essential' && + categoryId !== 'vue2-essential' + ) +} + +function formatCategory(category, alwaysError = false) { + let extendsCategoryId = extendsCategories[category.categoryId] + if (category.categoryId === 'base') { return `/* * IMPORTANT! @@ -79,12 +89,17 @@ module.exports = [ parser: require('vue-eslint-parser'), sourceType: 'module', }, - rules: ${formatRules(category.rules, category.categoryId)}, + rules: ${formatRules(category.rules, category.categoryId, alwaysError)}, processor: 'vue/vue' } ] ` } + + if (alwaysError && hasWarningRules(extendsCategoryId)) { + extendsCategoryId += '-error' + } + return `/* * IMPORTANT! * This file has been automatically generated, @@ -97,7 +112,7 @@ module.exports = [ ...config, { name: 'vue/${category.categoryId.replace(/^vue3-/u, '')}/rules', - rules: ${formatRules(category.rules, category.categoryId)}, + rules: ${formatRules(category.rules, category.categoryId, alwaysError)}, } ] ` @@ -110,6 +125,13 @@ for (const category of categories) { const content = formatCategory(category) fs.writeFileSync(filePath, content) + + if (hasWarningRules(category.categoryId)) { + fs.writeFileSync( + path.join(ROOT, `${category.categoryId}-error.js`), + formatCategory(category, true) + ) + } } // Format files.