Skip to content

Commit bea53c0

Browse files
waynzhFloEdelmann
andauthored
feat(no-v-text-v-html-on-component): add ignore namespace option (#2610)
Co-authored-by: Flo Edelmann <[email protected]>
1 parent 86a8138 commit bea53c0

File tree

4 files changed

+93
-13
lines changed

4 files changed

+93
-13
lines changed

docs/rules/no-v-text-v-html-on-component.md

+23-4
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@ If you use v-text / v-html on a component, it will overwrite the component's con
2525
<!-- ✓ GOOD -->
2626
<div v-text="content"></div>
2727
<div v-html="html"></div>
28+
<svg><g v-text="content" /></svg>
29+
<math><mi v-text="content" /></math>
2830
<MyComponent>{{ content }}</MyComponent>
2931
3032
<!-- ✗ BAD -->
3133
<MyComponent v-text="content"></MyComponent>
3234
<MyComponent v-html="html"></MyComponent>
35+
<g v-text="content" />
36+
<mi v-text="content" />
3337
</template>
3438
```
3539

@@ -39,14 +43,15 @@ If you use v-text / v-html on a component, it will overwrite the component's con
3943

4044
```json
4145
{
42-
"vue/no-v-text-v-html-on-component": [
43-
"error",
44-
{ "allow": ["router-link", "nuxt-link"] }
45-
]
46+
"vue/no-v-text-v-html-on-component": ["error", {
47+
"allow": ["router-link", "nuxt-link"],
48+
"ignoreElementNamespaces": false
49+
}]
4650
}
4751
```
4852

4953
- `allow` (`string[]`) ... Specify a list of custom components for which the rule should not apply.
54+
- `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`.
5055

5156
### `{ "allow": ["router-link", "nuxt-link"] }`
5257

@@ -65,6 +70,20 @@ If you use v-text / v-html on a component, it will overwrite the component's con
6570

6671
</eslint-code-block>
6772

73+
### `{ "ignoreElementNamespaces": true }`
74+
75+
<eslint-code-block :rules="{'vue/no-v-text-v-html-on-component': ['error', { ignoreElementNamespaces: true }]}">
76+
77+
```vue
78+
<template>
79+
<!-- ✓ GOOD -->
80+
<g v-text="content" /> <!-- SVG element not inside of <svg> -->
81+
<mi v-text="content" /> <!-- MathML element not inside of <math> -->
82+
</template>
83+
```
84+
85+
</eslint-code-block>
86+
6887
## :rocket: Version
6988

7089
This rule was introduced in eslint-plugin-vue v8.4.0

lib/rules/no-v-text-v-html-on-component.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ module.exports = {
2626
type: 'string'
2727
},
2828
uniqueItems: true
29+
},
30+
ignoreElementNamespaces: {
31+
type: 'boolean'
2932
}
3033
},
3134
additionalProperties: false
@@ -41,6 +44,8 @@ module.exports = {
4144
const options = context.options[0] || {}
4245
/** @type {Set<string>} */
4346
const allow = new Set(options.allow)
47+
/** @type {boolean} */
48+
const ignoreElementNamespaces = options.ignoreElementNamespaces === true
4449

4550
/**
4651
* Check whether the given node is an allowed component or not.
@@ -62,7 +67,10 @@ module.exports = {
6267
*/
6368
function verify(node) {
6469
const element = node.parent.parent
65-
if (utils.isCustomComponent(element) && !isAllowedComponent(element)) {
70+
if (
71+
utils.isCustomComponent(element, ignoreElementNamespaces) &&
72+
!isAllowedComponent(element)
73+
) {
6674
context.report({
6775
node,
6876
loc: node.loc,

lib/utils/index.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -941,19 +941,30 @@ module.exports = {
941941
/**
942942
* Check whether the given node is a custom component or not.
943943
* @param {VElement} node The start tag node to check.
944+
* @param {boolean} [ignoreElementNamespaces=false] If `true`, ignore element namespaces.
944945
* @returns {boolean} `true` if the node is a custom component.
945946
*/
946-
isCustomComponent(node) {
947-
return (
948-
(this.isHtmlElementNode(node) &&
949-
!this.isHtmlWellKnownElementName(node.rawName)) ||
950-
(this.isSvgElementNode(node) &&
951-
!this.isSvgWellKnownElementName(node.rawName)) ||
952-
(this.isMathElementNode(node) &&
953-
!this.isMathWellKnownElementName(node.rawName)) ||
947+
isCustomComponent(node, ignoreElementNamespaces = false) {
948+
if (
954949
hasAttribute(node, 'is') ||
955950
hasDirective(node, 'bind', 'is') ||
956951
hasDirective(node, 'is')
952+
) {
953+
return true
954+
}
955+
956+
const isHtmlName = this.isHtmlWellKnownElementName(node.rawName)
957+
const isSvgName = this.isSvgWellKnownElementName(node.rawName)
958+
const isMathName = this.isMathWellKnownElementName(node.rawName)
959+
960+
if (ignoreElementNamespaces) {
961+
return !isHtmlName && !isSvgName && !isMathName
962+
}
963+
964+
return (
965+
(this.isHtmlElementNode(node) && !isHtmlName) ||
966+
(this.isSvgElementNode(node) && !isSvgName) ||
967+
(this.isMathElementNode(node) && !isMathName)
957968
)
958969
},
959970

tests/lib/rules/no-v-text-v-html-on-component.js

+42
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ tester.run('no-v-text-v-html-on-component', rule, {
5959
</template>
6060
`,
6161
options: [{ allow: ['RouterLink', 'nuxt-link'] }]
62+
},
63+
{
64+
filename: 'test.vue',
65+
code: `
66+
<template>
67+
<svg><g v-text="content" /></svg>
68+
<math><mspace v-text="content" /></math>
69+
</template>
70+
`
71+
},
72+
{
73+
filename: 'test.vue',
74+
code: `
75+
<template>
76+
<h1 v-html="content" />
77+
<g v-text="content" />
78+
<mi v-text="content" />
79+
</template>
80+
`,
81+
options: [{ ignoreElementNamespaces: true }]
6282
}
6383
],
6484
invalid: [
@@ -167,6 +187,28 @@ tester.run('no-v-text-v-html-on-component', rule, {
167187
column: 22
168188
}
169189
]
190+
},
191+
{
192+
filename: 'test.vue',
193+
code: `
194+
<template>
195+
<g v-text="content" />
196+
<mi v-text="content" />
197+
</template>
198+
`,
199+
options: [{ ignoreElementNamespaces: false }],
200+
errors: [
201+
{
202+
message: "Using v-text on component may break component's content.",
203+
line: 3,
204+
column: 12
205+
},
206+
{
207+
message: "Using v-text on component may break component's content.",
208+
line: 4,
209+
column: 13
210+
}
211+
]
170212
}
171213
]
172214
})

0 commit comments

Comments
 (0)