Skip to content

Commit

Permalink
feat(no-v-text-v-html-on-component): add ignore namespace option (#2610)
Browse files Browse the repository at this point in the history
Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
  • Loading branch information
waynzh and FloEdelmann authored Nov 27, 2024
1 parent 86a8138 commit bea53c0
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 13 deletions.
27 changes: 23 additions & 4 deletions docs/rules/no-v-text-v-html-on-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ If you use v-text / v-html on a component, it will overwrite the component's con
<!-- ✓ GOOD -->
<div v-text="content"></div>
<div v-html="html"></div>
<svg><g v-text="content" /></svg>
<math><mi v-text="content" /></math>
<MyComponent>{{ content }}</MyComponent>
<!-- ✗ BAD -->
<MyComponent v-text="content"></MyComponent>
<MyComponent v-html="html"></MyComponent>
<g v-text="content" />
<mi v-text="content" />
</template>
```

Expand All @@ -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`) ... 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"] }`

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

</eslint-code-block>

### `{ "ignoreElementNamespaces": true }`

<eslint-code-block :rules="{'vue/no-v-text-v-html-on-component': ['error', { ignoreElementNamespaces: true }]}">

```vue
<template>
<!-- ✓ GOOD -->
<g v-text="content" /> <!-- SVG element not inside of <svg> -->
<mi v-text="content" /> <!-- MathML element not inside of <math> -->
</template>
```

</eslint-code-block>

## :rocket: Version

This rule was introduced in eslint-plugin-vue v8.4.0
Expand Down
10 changes: 9 additions & 1 deletion lib/rules/no-v-text-v-html-on-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ module.exports = {
type: 'string'
},
uniqueItems: true
},
ignoreElementNamespaces: {
type: 'boolean'
}
},
additionalProperties: false
Expand All @@ -41,6 +44,8 @@ module.exports = {
const options = context.options[0] || {}
/** @type {Set<string>} */
const allow = new Set(options.allow)
/** @type {boolean} */
const ignoreElementNamespaces = options.ignoreElementNamespaces === true

/**
* Check whether the given node is an allowed component or not.
Expand All @@ -62,7 +67,10 @@ module.exports = {
*/
function verify(node) {
const element = node.parent.parent
if (utils.isCustomComponent(element) && !isAllowedComponent(element)) {
if (
utils.isCustomComponent(element, ignoreElementNamespaces) &&
!isAllowedComponent(element)
) {
context.report({
node,
loc: node.loc,
Expand Down
27 changes: 19 additions & 8 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
},

Expand Down
42 changes: 42 additions & 0 deletions tests/lib/rules/no-v-text-v-html-on-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,26 @@ tester.run('no-v-text-v-html-on-component', rule, {
</template>
`,
options: [{ allow: ['RouterLink', 'nuxt-link'] }]
},
{
filename: 'test.vue',
code: `
<template>
<svg><g v-text="content" /></svg>
<math><mspace v-text="content" /></math>
</template>
`
},
{
filename: 'test.vue',
code: `
<template>
<h1 v-html="content" />
<g v-text="content" />
<mi v-text="content" />
</template>
`,
options: [{ ignoreElementNamespaces: true }]
}
],
invalid: [
Expand Down Expand Up @@ -167,6 +187,28 @@ tester.run('no-v-text-v-html-on-component', rule, {
column: 22
}
]
},
{
filename: 'test.vue',
code: `
<template>
<g v-text="content" />
<mi v-text="content" />
</template>
`,
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
}
]
}
]
})

0 comments on commit bea53c0

Please sign in to comment.