Skip to content

Commit 0849a27

Browse files
authored
Add disallowVueBuiltInComponents and disallowVue3BuiltInComponents option that reports Vue built-in component names to the vue/no-reserved-component-names rule. (#1116)
1 parent b940cb9 commit 0849a27

File tree

3 files changed

+241
-25
lines changed

3 files changed

+241
-25
lines changed

Diff for: docs/rules/no-reserved-component-names.md

+43-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ description: disallow the use of reserved names in component definitions
99
1010
## :book: Rule Details
1111

12-
This rule prevents name collisions between vue components and standard html elements.
12+
This rule prevents name collisions between Vue components and standard HTML elements and built-in components.
1313

1414
<eslint-code-block :rules="{'vue/no-reserved-component-names': ['error']}">
1515

@@ -26,14 +26,55 @@ export default {
2626

2727
## :wrench: Options
2828

29-
Nothing.
29+
```json
30+
{
31+
"vue/no-reserved-component-names": ["error", {
32+
"disallowVueBuiltInComponents": false,
33+
"disallowVue3BuiltInComponents": false
34+
}]
35+
}
36+
```
37+
38+
- `disallowVueBuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 2.x built-in component names. Default is `false`.
39+
- `disallowVue3BuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 3.x built-in component names. Default is `false`.
40+
41+
### `"disallowVueBuiltInComponents": true`
42+
43+
<eslint-code-block :rules="{'vue/no-reserved-component-names': ['error', {disallowVueBuiltInComponents: true}]}">
44+
45+
```vue
46+
<script>
47+
/* ✗ BAD */
48+
export default {
49+
name: 'transition-group'
50+
}
51+
</script>
52+
```
53+
54+
</eslint-code-block>
55+
56+
### `"disallowVue3BuiltInComponents": true`
57+
58+
<eslint-code-block :rules="{'vue/no-reserved-component-names': ['error', {disallowVue3BuiltInComponents: true}]}">
59+
60+
```vue
61+
<script>
62+
/* ✗ BAD */
63+
export default {
64+
name: 'teleport'
65+
}
66+
</script>
67+
```
68+
69+
</eslint-code-block>
3070

3171
## :books: Further reading
3272

3373
- [List of html elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element)
3474
- [List of SVG elements](https://developer.mozilla.org/en-US/docs/Web/SVG/Element)
3575
- [Kebab case elements](https://stackoverflow.com/questions/22545621/do-custom-elements-require-a-dash-in-their-name/22545622#22545622)
3676
- [Valid custom element name](https://w3c.github.io/webcomponents/spec/custom/#valid-custom-element-name)
77+
- [API - Built-In Components](https://vuejs.org/v2/api/index.html#Built-In-Components)
3778

3879
## :mag: Implementation
3980

Diff for: lib/rules/no-reserved-component-names.js

+69-16
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,44 @@ const kebabCaseElements = [
2222
'missing-glyph'
2323
]
2424

25+
// https://vuejs.org/v2/api/index.html#Built-In-Components
26+
const vueBuiltInComponents = [
27+
'component',
28+
'transition',
29+
'transition-group',
30+
'keep-alive',
31+
'slot'
32+
]
33+
34+
const vue3BuiltInComponents = [
35+
'teleport',
36+
'suspense'
37+
]
38+
2539
const isLowercase = (word) => /^[a-z]*$/.test(word)
2640
const capitalizeFirstLetter = (word) => word[0].toUpperCase() + word.substring(1, word.length)
2741

28-
const RESERVED_NAMES = new Set(
29-
[
30-
...kebabCaseElements,
31-
...kebabCaseElements.map(casing.pascalCase),
32-
...htmlElements,
33-
...htmlElements.map(capitalizeFirstLetter),
34-
...deprecatedHtmlElements,
35-
...deprecatedHtmlElements.map(capitalizeFirstLetter),
36-
...svgElements,
37-
...svgElements.filter(isLowercase).map(capitalizeFirstLetter)
38-
])
42+
const RESERVED_NAMES_IN_HTML = new Set([
43+
...htmlElements,
44+
...htmlElements.map(capitalizeFirstLetter)
45+
])
46+
const RESERVED_NAMES_IN_VUE = new Set([
47+
...vueBuiltInComponents,
48+
...vueBuiltInComponents.map(casing.pascalCase)
49+
])
50+
const RESERVED_NAMES_IN_VUE3 = new Set([
51+
...RESERVED_NAMES_IN_VUE,
52+
...vue3BuiltInComponents,
53+
...vue3BuiltInComponents.map(casing.pascalCase)
54+
])
55+
const RESERVED_NAMES_IN_OTHERS = new Set([
56+
...deprecatedHtmlElements,
57+
...deprecatedHtmlElements.map(capitalizeFirstLetter),
58+
...kebabCaseElements,
59+
...kebabCaseElements.map(casing.pascalCase),
60+
...svgElements,
61+
...svgElements.filter(isLowercase).map(capitalizeFirstLetter)
62+
])
3963

4064
// ------------------------------------------------------------------------------
4165
// Rule Definition
@@ -46,14 +70,41 @@ module.exports = {
4670
type: 'suggestion',
4771
docs: {
4872
description: 'disallow the use of reserved names in component definitions',
49-
categories: undefined, // 'essential'
73+
categories: undefined,
5074
url: 'https://eslint.vuejs.org/rules/no-reserved-component-names.html'
5175
},
5276
fixable: null,
53-
schema: []
77+
schema: [{
78+
type: 'object',
79+
properties: {
80+
disallowVueBuiltInComponents: {
81+
type: 'boolean'
82+
},
83+
disallowVue3BuiltInComponents: {
84+
type: 'boolean'
85+
}
86+
}
87+
}],
88+
messages: {
89+
reserved: 'Name "{{name}}" is reserved.',
90+
reservedInHtml: 'Name "{{name}}" is reserved in HTML.',
91+
reservedInVue: 'Name "{{name}}" is reserved in Vue.js.',
92+
reservedInVue3: 'Name "{{name}}" is reserved in Vue.js 3.x.'
93+
}
5494
},
5595

5696
create (context) {
97+
const options = context.options[0] || {}
98+
const disallowVueBuiltInComponents = options.disallowVueBuiltInComponents === true
99+
const disallowVue3BuiltInComponents = options.disallowVue3BuiltInComponents === true
100+
101+
const reservedNames = new Set([
102+
...RESERVED_NAMES_IN_HTML,
103+
...(disallowVueBuiltInComponents ? RESERVED_NAMES_IN_VUE : []),
104+
...(disallowVue3BuiltInComponents ? RESERVED_NAMES_IN_VUE3 : []),
105+
...RESERVED_NAMES_IN_OTHERS
106+
])
107+
57108
function canVerify (node) {
58109
return node.type === 'Literal' || (
59110
node.type === 'TemplateLiteral' &&
@@ -70,15 +121,17 @@ module.exports = {
70121
} else {
71122
name = node.value
72123
}
73-
if (RESERVED_NAMES.has(name)) {
124+
if (reservedNames.has(name)) {
74125
report(node, name)
75126
}
76127
}
77128

78129
function report (node, name) {
79130
context.report({
80131
node: node,
81-
message: 'Name "{{name}}" is reserved.',
132+
messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml'
133+
: RESERVED_NAMES_IN_VUE.has(name) ? 'reservedInVue'
134+
: RESERVED_NAMES_IN_VUE3.has(name) ? 'reservedInVue3' : 'reserved',
82135
data: {
83136
name: name
84137
}
@@ -98,7 +151,7 @@ module.exports = {
98151
utils.executeOnVue(context, (obj) => {
99152
// Report if a component has been registered locally with a reserved name.
100153
utils.getRegisteredComponents(obj)
101-
.filter(({ name }) => RESERVED_NAMES.has(name))
154+
.filter(({ name }) => reservedNames.has(name))
102155
.forEach(({ node, name }) => report(node, name))
103156

104157
const node = obj.properties

0 commit comments

Comments
 (0)