diff --git a/docs/rules/README.md b/docs/rules/README.md index bb8d92e8b..fb952cddb 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -66,6 +66,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | [vue/require-prop-type-constructor](./require-prop-type-constructor.md) | require prop type to be a constructor | :wrench: | | [vue/require-render-return](./require-render-return.md) | enforce render function to always return value | | | [vue/require-v-for-key](./require-v-for-key.md) | require `v-bind:key` with `v-for` directives | | +| [vue/require-v-if-inside-transition](./require-v-if-inside-transition.md) | require control the display of the content inside `` | | | [vue/require-valid-default-prop](./require-valid-default-prop.md) | enforce props default values to be valid | | | [vue/return-in-computed-property](./return-in-computed-property.md) | enforce that a return statement is present in computed property | | | [vue/use-v-on-exact](./use-v-on-exact.md) | enforce usage of `exact` modifier on `v-on` | | diff --git a/docs/rules/require-v-if-inside-transition.md b/docs/rules/require-v-if-inside-transition.md new file mode 100644 index 000000000..5b541d223 --- /dev/null +++ b/docs/rules/require-v-if-inside-transition.md @@ -0,0 +1,42 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/require-v-if-inside-transition +description: require control the display of the content inside `` +--- +# vue/require-v-if-inside-transition +> require control the display of the content inside `` + +- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`. + +## :book: Rule Details + +This rule reports elements inside `` that do not control the display. + + + +```vue + +``` + + + +## :wrench: Options + +Nothing. + +## :books: Further reading + +- [Vue RFCs - 0017-transition-as-root](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0017-transition-as-root.md) + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-v-if-inside-transition.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-v-if-inside-transition.js) diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js index d668791c7..20135152d 100644 --- a/lib/configs/vue3-essential.js +++ b/lib/configs/vue3-essential.js @@ -34,6 +34,7 @@ module.exports = { 'vue/require-prop-type-constructor': 'error', 'vue/require-render-return': 'error', 'vue/require-v-for-key': 'error', + 'vue/require-v-if-inside-transition': 'error', 'vue/require-valid-default-prop': 'error', 'vue/return-in-computed-property': 'error', 'vue/use-v-on-exact': 'error', diff --git a/lib/index.js b/lib/index.js index fec192830..098b5fbd2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -88,6 +88,7 @@ module.exports = { 'require-prop-types': require('./rules/require-prop-types'), 'require-render-return': require('./rules/require-render-return'), 'require-v-for-key': require('./rules/require-v-for-key'), + 'require-v-if-inside-transition': require('./rules/require-v-if-inside-transition'), 'require-valid-default-prop': require('./rules/require-valid-default-prop'), 'return-in-computed-property': require('./rules/return-in-computed-property'), 'script-indent': require('./rules/script-indent'), diff --git a/lib/rules/require-v-if-inside-transition.js b/lib/rules/require-v-if-inside-transition.js new file mode 100644 index 000000000..5288e2e38 --- /dev/null +++ b/lib/rules/require-v-if-inside-transition.js @@ -0,0 +1,82 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +/** + * Check whether the given node is an well-known element or not. + * @param {ASTNode} node The element node to check. + * @returns {boolean} `true` if the name is an well-known element name. + */ +function isWellKnownElement (node) { + if ( + (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) || + utils.isHtmlWellKnownElementName(node.rawName) || + utils.isSvgWellKnownElementName(node.rawName) + ) { + return true + } + return false +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'require control the display of the content inside ``', + categories: ['vue3-essential'], + url: 'https://eslint.vuejs.org/rules/require-v-if-inside-transition.html' + }, + fixable: null, + schema: [], + messages: { + expected: 'The element inside `` is expected to have a `v-if` or `v-show` directive.' + } + }, + + create (context) { + /** + * Check if the given element has display control. + * @param {VElement} element The element node to check. + */ + function verifyInsideElement (element) { + if (utils.isCustomComponent(element)) { + return + } + if (!isWellKnownElement(element)) { + return + } + if (!utils.hasDirective(element, 'if') && !utils.hasDirective(element, 'show')) { + context.report({ + node: element.startTag, + loc: element.startTag.loc, + messageId: 'expected' + }) + } + } + + return utils.defineTemplateBodyVisitor(context, { + "VElement[name='transition'] > VElement" (node) { + if (node.parent.children[0] !== node) { + return + } + verifyInsideElement(node) + } + }) + } +} diff --git a/tests/lib/rules/require-v-if-inside-transition.js b/tests/lib/rules/require-v-if-inside-transition.js new file mode 100644 index 000000000..08e3eddae --- /dev/null +++ b/tests/lib/rules/require-v-if-inside-transition.js @@ -0,0 +1,110 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../lib/rules/require-v-if-inside-transition') + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { ecmaVersion: 2015 } +}) + +tester.run('require-v-if-inside-transition', rule, { + valid: [ + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + } + ], + invalid: [ + { + filename: 'test.vue', + code: '', + errors: [ + { + line: 1, + column: 23, + messageId: 'expected', + endLine: 1, + endColumn: 30 + } + ] + }, + { + filename: 'test.vue', + code: '', + errors: [{ messageId: 'expected' }] + }, + { + filename: 'test.vue', + code: '', + errors: [{ messageId: 'expected' }] + }, + { + filename: 'test.vue', + code: '', + errors: [{ messageId: 'expected' }] + }, + { + filename: 'test.vue', + code: '', + errors: [{ messageId: 'expected' }] + }, + { + filename: 'test.vue', + code: '', + errors: [{ messageId: 'expected' }] + } + ] +})