Skip to content

Commit 9205cc8

Browse files
waynzhFloEdelmann
andauthored
feat(no-reserved-component-names): add case sensitive option (#2594)
Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
1 parent 4729a3b commit 9205cc8

File tree

3 files changed

+107
-21
lines changed

3 files changed

+107
-21
lines changed

docs/rules/no-reserved-component-names.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ export default {
3535
{
3636
"vue/no-reserved-component-names": ["error", {
3737
"disallowVueBuiltInComponents": false,
38-
"disallowVue3BuiltInComponents": false
38+
"disallowVue3BuiltInComponents": false,
39+
"htmlElementCaseSensitive": false,
3940
}]
4041
}
4142
```
4243

4344
- `disallowVueBuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 2.x built-in component names. Default is `false`.
4445
- `disallowVue3BuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 3.x built-in component names. Default is `false`.
46+
- `htmlElementCaseSensitive` (`boolean`) ... If `true`, component names must exactly match the case of an HTML element to be considered conflicting. Default is `false` (i.e. case-insensitve comparison).
4547

4648
### `"disallowVueBuiltInComponents": true`
4749

@@ -73,6 +75,34 @@ export default {
7375

7476
</eslint-code-block>
7577

78+
### `"htmlElementCaseSensitive": true`
79+
80+
<eslint-code-block :rules="{'vue/no-reserved-component-names': ['error', {htmlElementCaseSensitive: true}]}">
81+
82+
```vue
83+
<script>
84+
/* ✓ GOOD */
85+
export default {
86+
name: 'Button'
87+
}
88+
</script>
89+
```
90+
91+
</eslint-code-block>
92+
93+
<eslint-code-block :rules="{'vue/no-reserved-component-names': ['error', {htmlElementCaseSensitive: true}]}">
94+
95+
```vue
96+
<script>
97+
/* ✗ BAD */
98+
export default {
99+
name: 'button'
100+
}
101+
</script>
102+
```
103+
104+
</eslint-code-block>
105+
76106
## :couple: Related Rules
77107

78108
- [vue/multi-word-component-names](./multi-word-component-names.md)

lib/rules/no-reserved-component-names.js

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,6 @@ function isLowercase(word) {
3434
return /^[a-z]*$/.test(word)
3535
}
3636

37-
const RESERVED_NAMES_IN_HTML = new Set([
38-
...htmlElements,
39-
...htmlElements.map(casing.capitalize)
40-
])
41-
const RESERVED_NAMES_IN_OTHERS = new Set([
42-
...deprecatedHtmlElements,
43-
...deprecatedHtmlElements.map(casing.capitalize),
44-
...kebabCaseElements,
45-
...kebabCaseElements.map(casing.pascalCase),
46-
...svgElements,
47-
...svgElements.filter(isLowercase).map(casing.capitalize)
48-
])
49-
5037
/**
5138
* @param {Expression | SpreadElement} node
5239
* @returns {node is (Literal | TemplateLiteral)}
@@ -61,14 +48,14 @@ function canVerify(node) {
6148
}
6249

6350
/**
64-
* @param {string} name
65-
* @returns {string}
51+
* @template T
52+
* @param {Set<T>} set
53+
* @param {Iterable<T>} iterable
6654
*/
67-
function getMessageId(name) {
68-
if (RESERVED_NAMES_IN_HTML.has(name)) return 'reservedInHtml'
69-
if (RESERVED_NAMES_IN_VUE.has(name)) return 'reservedInVue'
70-
if (RESERVED_NAMES_IN_VUE3.has(name)) return 'reservedInVue3'
71-
return 'reserved'
55+
function addAll(set, iterable) {
56+
for (const element of iterable) {
57+
set.add(element)
58+
}
7259
}
7360

7461
module.exports = {
@@ -90,6 +77,9 @@ module.exports = {
9077
},
9178
disallowVue3BuiltInComponents: {
9279
type: 'boolean'
80+
},
81+
htmlElementCaseSensitive: {
82+
type: 'boolean'
9383
}
9484
},
9585
additionalProperties: false
@@ -109,6 +99,23 @@ module.exports = {
10999
options.disallowVueBuiltInComponents === true
110100
const disallowVue3BuiltInComponents =
111101
options.disallowVue3BuiltInComponents === true
102+
const htmlElementCaseSensitive = options.htmlElementCaseSensitive === true
103+
104+
const RESERVED_NAMES_IN_HTML = new Set(htmlElements)
105+
const RESERVED_NAMES_IN_OTHERS = new Set([
106+
...deprecatedHtmlElements,
107+
...kebabCaseElements,
108+
...svgElements
109+
])
110+
111+
if (!htmlElementCaseSensitive) {
112+
addAll(RESERVED_NAMES_IN_HTML, htmlElements.map(casing.capitalize))
113+
addAll(RESERVED_NAMES_IN_OTHERS, [
114+
...deprecatedHtmlElements.map(casing.capitalize),
115+
...kebabCaseElements.map(casing.pascalCase),
116+
...svgElements.filter(isLowercase).map(casing.capitalize)
117+
])
118+
}
112119

113120
const reservedNames = new Set([
114121
...RESERVED_NAMES_IN_HTML,
@@ -117,6 +124,17 @@ module.exports = {
117124
...RESERVED_NAMES_IN_OTHERS
118125
])
119126

127+
/**
128+
* @param {string} name
129+
* @returns {string}
130+
*/
131+
function getMessageId(name) {
132+
if (RESERVED_NAMES_IN_HTML.has(name)) return 'reservedInHtml'
133+
if (RESERVED_NAMES_IN_VUE.has(name)) return 'reservedInVue'
134+
if (RESERVED_NAMES_IN_VUE3.has(name)) return 'reservedInVue3'
135+
return 'reserved'
136+
}
137+
120138
/**
121139
* @param {Literal | TemplateLiteral} node
122140
*/

tests/lib/rules/no-reserved-component-names.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,16 @@ const invalidElements = [
410410
'xmp',
411411
'Xmp'
412412
]
413+
const invalidLowerCaseElements = []
414+
const invalidUpperCaseElements = []
415+
416+
for (const element of invalidElements) {
417+
if (element[0] === element[0].toLowerCase()) {
418+
invalidLowerCaseElements.push(element)
419+
} else {
420+
invalidUpperCaseElements.push(element)
421+
}
422+
}
413423

414424
const vue2BuiltInComponents = [
415425
'component',
@@ -559,6 +569,16 @@ ruleTester.run('no-reserved-component-names', rule, {
559569
languageOptions,
560570
options: [{ disallowVueBuiltInComponents: true }]
561571
})),
572+
...invalidUpperCaseElements.map((name) => ({
573+
filename: `${name}.vue`,
574+
code: `
575+
export default {
576+
name: '${name}'
577+
}
578+
`,
579+
languageOptions,
580+
options: [{ htmlElementCaseSensitive: true }]
581+
})),
562582
{
563583
filename: 'test.vue',
564584
code: `<script setup> defineOptions({}) </script>`,
@@ -701,6 +721,24 @@ ruleTester.run('no-reserved-component-names', rule, {
701721
}
702722
]
703723
})),
724+
...invalidLowerCaseElements.map((name) => ({
725+
filename: `${name}.vue`,
726+
code: `<script setup> defineOptions({name: '${name}'}) </script>`,
727+
languageOptions: {
728+
parser: require('vue-eslint-parser'),
729+
...languageOptions
730+
},
731+
options: [{ htmlElementCaseSensitive: true }],
732+
errors: [
733+
{
734+
messageId: RESERVED_NAMES_IN_HTML.has(name)
735+
? 'reservedInHtml'
736+
: 'reserved',
737+
data: { name },
738+
line: 1
739+
}
740+
]
741+
})),
704742
...vue2BuiltInComponents.map((name) => ({
705743
filename: `${name}.vue`,
706744
code: `

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy