Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9205cc8

Browse filesBrowse 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
Copy full SHA for 9205cc8

File tree

Expand file treeCollapse file tree

3 files changed

+107
-21
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+107
-21
lines changed

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

Copy file name to clipboardExpand all lines: docs/rules/no-reserved-component-names.md
+31-1Lines 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

Copy file name to clipboardExpand all lines: lib/rules/no-reserved-component-names.js
+38-20Lines 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

Copy file name to clipboardExpand all lines: tests/lib/rules/no-reserved-component-names.js
+38Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.