diff --git a/docs/rules/no-v-text-v-html-on-component.md b/docs/rules/no-v-text-v-html-on-component.md index 420eefda6..376974b00 100644 --- a/docs/rules/no-v-text-v-html-on-component.md +++ b/docs/rules/no-v-text-v-html-on-component.md @@ -36,7 +36,33 @@ If you use v-text / v-html on a component, it will overwrite the component's con ## :wrench: Options -Nothing. +```json +{ + "vue/no-v-text-v-html-on-component": [ + "error", + { "allow": ["router-link", "nuxt-link"] } + ] +} +``` + +- `allow` (`string[]`) ... Specify a list of custom components for which the rule should not apply. + +### `{ "allow": ["router-link", "nuxt-link"] }` + + + +```vue + +``` + + ## :rocket: Version diff --git a/lib/rules/no-v-text-v-html-on-component.js b/lib/rules/no-v-text-v-html-on-component.js index c517167f1..92289f6bb 100644 --- a/lib/rules/no-v-text-v-html-on-component.js +++ b/lib/rules/no-v-text-v-html-on-component.js @@ -5,6 +5,7 @@ 'use strict' const utils = require('../utils') +const casing = require('../utils/casing') module.exports = { meta: { @@ -15,7 +16,21 @@ module.exports = { url: 'https://eslint.vuejs.org/rules/no-v-text-v-html-on-component.html' }, fixable: null, - schema: [], + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + uniqueItems: true + } + }, + additionalProperties: false + } + ], messages: { disallow: "Using {{directiveName}} on component may break component's content." @@ -23,13 +38,31 @@ module.exports = { }, /** @param {RuleContext} context */ create(context) { + const options = context.options[0] || {} + /** @type {Set} */ + const allow = new Set(options.allow) + + /** + * Check whether the given node is an allowed component or not. + * @param {VElement} node The start tag node to check. + * @returns {boolean} `true` if the node is an allowed component. + */ + function isAllowedComponent(node) { + const componentName = node.rawName + return ( + allow.has(componentName) || + allow.has(casing.pascalCase(componentName)) || + allow.has(casing.kebabCase(componentName)) + ) + } + /** * Verify for v-text and v-html directive * @param {VDirective} node */ function verify(node) { const element = node.parent.parent - if (utils.isCustomComponent(element)) { + if (utils.isCustomComponent(element) && !isAllowedComponent(element)) { context.report({ node, loc: node.loc, diff --git a/tests/lib/rules/no-v-text-v-html-on-component.js b/tests/lib/rules/no-v-text-v-html-on-component.js index c50071346..1dafc17d8 100644 --- a/tests/lib/rules/no-v-text-v-html-on-component.js +++ b/tests/lib/rules/no-v-text-v-html-on-component.js @@ -40,6 +40,25 @@ tester.run('no-v-text-v-html-on-component', rule, { ` + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ allow: ['router-link'] }] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ allow: ['RouterLink', 'nuxt-link'] }] } ], invalid: [ @@ -132,6 +151,22 @@ tester.run('no-v-text-v-html-on-component', rule, { column: 30 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ allow: ['nuxt-link'] }], + errors: [ + { + message: "Using v-html on component may break component's content.", + line: 3, + column: 22 + } + ] } ] })