Skip to content

Commit f7a4dc0

Browse files
authored
Fix #580 - Fix false positives regarding components inside SVGs (#645)
* 580 - don't ignore custom components inside SVGs * 580 - Add more cases * 580 - Detect components inside SVGs and don't treat known SVG elements as components
1 parent 7bb5438 commit f7a4dc0

File tree

6 files changed

+74
-8
lines changed

6 files changed

+74
-8
lines changed

lib/rules/component-name-in-template-casing.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ module.exports = {
6161
return
6262
}
6363

64-
if (!utils.isHtmlElementNode(node) || utils.isHtmlWellKnownElementName(node.rawName)) {
64+
if (
65+
(!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
66+
utils.isHtmlWellKnownElementName(node.rawName) ||
67+
utils.isSvgWellKnownElementName(node.rawName)
68+
) {
6569
return
6670
}
6771

lib/rules/no-unused-components.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,34 @@ module.exports = {
3737
create (context) {
3838
const options = context.options[0] || {}
3939
const ignoreWhenBindingPresent = options.ignoreWhenBindingPresent !== undefined ? options.ignoreWhenBindingPresent : true
40-
const usedComponents = []
40+
const usedComponents = new Set()
4141
let registeredComponents = []
4242
let ignoreReporting = false
4343
let templateLocation
4444

4545
return utils.defineTemplateBodyVisitor(context, {
4646
VElement (node) {
47-
if (utils.isHtmlElementNode(node) && !utils.isHtmlWellKnownElementName(node.rawName)) {
48-
usedComponents.push(node.rawName)
47+
if (
48+
(!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
49+
utils.isHtmlWellKnownElementName(node.rawName) ||
50+
utils.isSvgWellKnownElementName(node.rawName)
51+
) {
52+
return
4953
}
54+
55+
usedComponents.add(node.rawName)
5056
},
5157
"VAttribute[directive=true][key.name='bind'][key.argument='is']" (node) {
5258
if (node.value.type !== 'VExpressionContainer') return
5359

5460
if (node.value.expression.type === 'Literal') {
55-
usedComponents.push(node.value.expression.value)
61+
usedComponents.add(node.value.expression.value)
5662
} else if (ignoreWhenBindingPresent) {
5763
ignoreReporting = true
5864
}
5965
},
6066
"VAttribute[directive=false][key.name='is']" (node) {
61-
usedComponents.push(node.value.value)
67+
usedComponents.add(node.value.value)
6268
},
6369
"VElement[name='template']" (rootNode) {
6470
templateLocation = templateLocation || rootNode.loc.start
@@ -77,13 +83,13 @@ module.exports = {
7783
// like "theComponent", "The-component" etc.
7884
// but except snake_case
7985
if (casing.pascalCase(name) === name || casing.camelCase(name) === name) {
80-
return !usedComponents.some(n => {
86+
return ![...usedComponents].some(n => {
8187
return n.indexOf('_') === -1 && (name === casing.pascalCase(n) || casing.camelCase(n) === name)
8288
})
8389
} else {
8490
// In any other case the used component name must exactly match
8591
// the registered name
86-
return usedComponents.indexOf(name) === -1
92+
return !usedComponents.has(name)
8793
}
8894
})
8995
.forEach(({ node, name }) => context.report({

lib/utils/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// ------------------------------------------------------------------------------
1111

1212
const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json'))
13+
const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json'))
1314
const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json'))
1415
const assert = require('assert')
1516
const vueEslintParser = require('vue-eslint-parser')
@@ -283,6 +284,16 @@ module.exports = {
283284
return HTML_ELEMENT_NAMES.has(name)
284285
},
285286

287+
/**
288+
* Check whether the given name is an well-known SVG element or not.
289+
* @param {string} name The name to check.
290+
* @returns {boolean} `true` if the name is an well-known SVG element name.
291+
*/
292+
isSvgWellKnownElementName (name) {
293+
assert(typeof name === 'string')
294+
return SVG_ELEMENT_NAMES.has(name)
295+
},
296+
286297
/**
287298
* Check whether the given name is a void element name or not.
288299
* @param {string} name The name to check.

lib/utils/svg-elements.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["a","animate","animateMotion","animateTransform","audio","canvas","circle","clipPath","defs","desc","discard","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","filter","foreignObject","g","iframe","image","line","linearGradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialGradient","rect","script","set","stop","style","svg","switch","symbol","text","textPath","title","tspan","unknown","use","video","view"]

tests/lib/rules/component-name-in-template-casing.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ tester.run('component-name-in-template-casing', rule, {
2929
'<template><div><slot></slot></div></template>',
3030
'<template><h1>Title</h1></template>',
3131
'<template><h1 :is="customTitle">Title</h1></template>',
32+
'<template><svg><TheComponent /></svg></template>',
33+
'<template><text /></template>',
34+
'<template><circle cx="0" cy="0" :d="radius"></template>',
3235

3336
// kebab-case
3437
{
@@ -65,6 +68,23 @@ tester.run('component-name-in-template-casing', rule, {
6568
'<template><the-component><!--test</the-component></template>'
6669
],
6770
invalid: [
71+
{
72+
code: `
73+
<template>
74+
<svg>
75+
<the-component />
76+
</svg>
77+
</template>
78+
`,
79+
output: `
80+
<template>
81+
<svg>
82+
<TheComponent />
83+
</svg>
84+
</template>
85+
`,
86+
errors: ['Component name "the-component" is not PascalCase.']
87+
},
6888
{
6989
code: `
7090
<template>

tests/lib/rules/no-unused-components.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,30 @@ tester.run('no-unused-components', rule, {
5252
}
5353
</script>`
5454
},
55+
{
56+
filename: 'test.vue',
57+
code: `<template>
58+
<svg>
59+
<TheCircle />
60+
</svg>
61+
</template>
62+
<script>
63+
export default {
64+
components: {
65+
TheCircle
66+
}
67+
}
68+
</script>`
69+
},
70+
{
71+
filename: 'test.vue',
72+
code: `<template>
73+
<circle cx="0" cy="0" :d="radius" />
74+
</template>
75+
<script>
76+
export default {}
77+
</script>`
78+
},
5579
{
5680
filename: 'test.vue',
5781
code: `<template>

0 commit comments

Comments
 (0)