From f3d794aa7ae9e8e6e2f5a7f265d41e9c0a6c6250 Mon Sep 17 00:00:00 2001 From: waynzh Date: Fri, 15 Nov 2024 13:17:27 +0800 Subject: [PATCH 1/5] feat(no-v-text-v-html-on-component): add ignore namespace option --- lib/rules/no-v-text-v-html-on-component.js | 23 +++++++++- .../rules/no-v-text-v-html-on-component.js | 42 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) 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 50ef9c76e..52f0f8fff 100644 --- a/lib/rules/no-v-text-v-html-on-component.js +++ b/lib/rules/no-v-text-v-html-on-component.js @@ -26,6 +26,9 @@ module.exports = { type: 'string' }, uniqueItems: true + }, + ignoreElementNamespaces: { + type: 'boolean' } }, additionalProperties: false @@ -41,6 +44,8 @@ module.exports = { const options = context.options[0] || {} /** @type {Set} */ const allow = new Set(options.allow) + /** @type {boolean} */ + const ignoreElementNamespaces = options.ignoreElementNamespaces === true /** * Check whether the given node is an allowed component or not. @@ -56,13 +61,29 @@ module.exports = { ) } + /** @param {VElement} element */ + function isCustomComponent(element) { + if (ignoreElementNamespaces) { + return ( + (!utils.isHtmlWellKnownElementName(element.rawName) && + !utils.isSvgWellKnownElementName(element.rawName) && + !utils.isMathWellKnownElementName(element.rawName)) || + utils.hasAttribute(element, 'is') || + utils.hasDirective(element, 'bind', 'is') || + utils.hasDirective(element, 'is') + ) + } + + return utils.isCustomComponent(element) + } + /** * Verify for v-text and v-html directive * @param {VDirective} node */ function verify(node) { const element = node.parent.parent - if (utils.isCustomComponent(element) && !isAllowedComponent(element)) { + if (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 ebf2901ba..bb403489e 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 @@ -59,6 +59,26 @@ tester.run('no-v-text-v-html-on-component', rule, { `, options: [{ allow: ['RouterLink', 'nuxt-link'] }] + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ ignoreElementNamespaces: true }] } ], invalid: [ @@ -167,6 +187,28 @@ tester.run('no-v-text-v-html-on-component', rule, { column: 22 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [{ ignoreElementNamespaces: false }], + errors: [ + { + message: "Using v-text on component may break component's content.", + line: 3, + column: 12 + }, + { + message: "Using v-text on component may break component's content.", + line: 4, + column: 13 + } + ] } ] }) From f3a6ad1bd269a7b5520109aeb9df7cddc12e5e4e Mon Sep 17 00:00:00 2001 From: waynzh Date: Fri, 15 Nov 2024 14:06:57 +0800 Subject: [PATCH 2/5] docs: update --- docs/rules/no-v-text-v-html-on-component.md | 27 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) 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 8e504d859..638a1bde2 100644 --- a/docs/rules/no-v-text-v-html-on-component.md +++ b/docs/rules/no-v-text-v-html-on-component.md @@ -25,11 +25,15 @@ If you use v-text / v-html on a component, it will overwrite the component's con
+ + {{ content }} + + ``` @@ -39,14 +43,15 @@ If you use v-text / v-html on a component, it will overwrite the component's con ```json { - "vue/no-v-text-v-html-on-component": [ - "error", - { "allow": ["router-link", "nuxt-link"] } - ] + "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`) ... Specify whether to ignore the namespace restrictions for SVG and MathML elements, so that the rule should not apply. Default is `false`. ### `{ "allow": ["router-link", "nuxt-link"] }` @@ -65,6 +70,20 @@ If you use v-text / v-html on a component, it will overwrite the component's con +### `{ "ignoreElementNamespaces": true }` + + + +```vue + +``` + + + ## :rocket: Version This rule was introduced in eslint-plugin-vue v8.4.0 From dbb8817ba16b9d20bbaecbaed24be8253a816775 Mon Sep 17 00:00:00 2001 From: Wayne Zhang Date: Tue, 19 Nov 2024 12:05:49 +0800 Subject: [PATCH 3/5] Update docs/rules/no-v-text-v-html-on-component.md Co-authored-by: Flo Edelmann --- docs/rules/no-v-text-v-html-on-component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 638a1bde2..f4b38e585 100644 --- a/docs/rules/no-v-text-v-html-on-component.md +++ b/docs/rules/no-v-text-v-html-on-component.md @@ -51,7 +51,7 @@ If you use v-text / v-html on a component, it will overwrite the component's con ``` - `allow` (`string[]`) ... Specify a list of custom components for which the rule should not apply. -- `ignoreElementNamespaces` (`boolean`) ... Specify whether to ignore the namespace restrictions for SVG and MathML elements, so that the rule should not apply. Default is `false`. +- `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"] }` From ed256b9c578f4dc581ee6fb2a22b9335d26b25e1 Mon Sep 17 00:00:00 2001 From: Wayne Zhang Date: Tue, 19 Nov 2024 12:05:59 +0800 Subject: [PATCH 4/5] Update docs/rules/no-v-text-v-html-on-component.md Co-authored-by: Flo Edelmann --- docs/rules/no-v-text-v-html-on-component.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 f4b38e585..7d75eb9c9 100644 --- a/docs/rules/no-v-text-v-html-on-component.md +++ b/docs/rules/no-v-text-v-html-on-component.md @@ -77,8 +77,8 @@ If you use v-text / v-html on a component, it will overwrite the component's con ```vue ``` From ba16740a26be28914aa9609de88974ef5957f051 Mon Sep 17 00:00:00 2001 From: waynzh Date: Tue, 19 Nov 2024 12:10:15 +0800 Subject: [PATCH 5/5] refactor --- lib/rules/no-v-text-v-html-on-component.js | 21 ++++------------- lib/utils/index.js | 27 +++++++++++++++------- 2 files changed, 23 insertions(+), 25 deletions(-) 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 52f0f8fff..e3f1f5409 100644 --- a/lib/rules/no-v-text-v-html-on-component.js +++ b/lib/rules/no-v-text-v-html-on-component.js @@ -61,29 +61,16 @@ module.exports = { ) } - /** @param {VElement} element */ - function isCustomComponent(element) { - if (ignoreElementNamespaces) { - return ( - (!utils.isHtmlWellKnownElementName(element.rawName) && - !utils.isSvgWellKnownElementName(element.rawName) && - !utils.isMathWellKnownElementName(element.rawName)) || - utils.hasAttribute(element, 'is') || - utils.hasDirective(element, 'bind', 'is') || - utils.hasDirective(element, 'is') - ) - } - - return utils.isCustomComponent(element) - } - /** * Verify for v-text and v-html directive * @param {VDirective} node */ function verify(node) { const element = node.parent.parent - if (isCustomComponent(element) && !isAllowedComponent(element)) { + if ( + utils.isCustomComponent(element, ignoreElementNamespaces) && + !isAllowedComponent(element) + ) { context.report({ node, loc: node.loc, diff --git a/lib/utils/index.js b/lib/utils/index.js index 58cd32689..eb84c1279 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -941,19 +941,30 @@ module.exports = { /** * 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)) || - (this.isMathElementNode(node) && - !this.isMathWellKnownElementName(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) ) },