diff --git a/docs/rules/no-reserved-component-names.md b/docs/rules/no-reserved-component-names.md
index a5d85cde8..31b74ab06 100644
--- a/docs/rules/no-reserved-component-names.md
+++ b/docs/rules/no-reserved-component-names.md
@@ -35,13 +35,15 @@ export default {
{
"vue/no-reserved-component-names": ["error", {
"disallowVueBuiltInComponents": false,
- "disallowVue3BuiltInComponents": false
+ "disallowVue3BuiltInComponents": false,
+ "htmlElementCaseSensitive": false,
}]
}
```
- `disallowVueBuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 2.x built-in component names. Default is `false`.
- `disallowVue3BuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 3.x built-in component names. Default is `false`.
+- `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).
### `"disallowVueBuiltInComponents": true`
@@ -73,6 +75,34 @@ export default {
+### `"htmlElementCaseSensitive": true`
+
+
+
+```vue
+
+```
+
+
+
+
+
+```vue
+
+```
+
+
+
## :couple: Related Rules
- [vue/multi-word-component-names](./multi-word-component-names.md)
diff --git a/lib/rules/no-reserved-component-names.js b/lib/rules/no-reserved-component-names.js
index 90e53604f..56621b424 100644
--- a/lib/rules/no-reserved-component-names.js
+++ b/lib/rules/no-reserved-component-names.js
@@ -34,19 +34,6 @@ function isLowercase(word) {
return /^[a-z]*$/.test(word)
}
-const RESERVED_NAMES_IN_HTML = new Set([
- ...htmlElements,
- ...htmlElements.map(casing.capitalize)
-])
-const RESERVED_NAMES_IN_OTHERS = new Set([
- ...deprecatedHtmlElements,
- ...deprecatedHtmlElements.map(casing.capitalize),
- ...kebabCaseElements,
- ...kebabCaseElements.map(casing.pascalCase),
- ...svgElements,
- ...svgElements.filter(isLowercase).map(casing.capitalize)
-])
-
/**
* @param {Expression | SpreadElement} node
* @returns {node is (Literal | TemplateLiteral)}
@@ -61,14 +48,14 @@ function canVerify(node) {
}
/**
- * @param {string} name
- * @returns {string}
+ * @template T
+ * @param {Set} set
+ * @param {Iterable} iterable
*/
-function getMessageId(name) {
- if (RESERVED_NAMES_IN_HTML.has(name)) return 'reservedInHtml'
- if (RESERVED_NAMES_IN_VUE.has(name)) return 'reservedInVue'
- if (RESERVED_NAMES_IN_VUE3.has(name)) return 'reservedInVue3'
- return 'reserved'
+function addAll(set, iterable) {
+ for (const element of iterable) {
+ set.add(element)
+ }
}
module.exports = {
@@ -90,6 +77,9 @@ module.exports = {
},
disallowVue3BuiltInComponents: {
type: 'boolean'
+ },
+ htmlElementCaseSensitive: {
+ type: 'boolean'
}
},
additionalProperties: false
@@ -109,6 +99,23 @@ module.exports = {
options.disallowVueBuiltInComponents === true
const disallowVue3BuiltInComponents =
options.disallowVue3BuiltInComponents === true
+ const htmlElementCaseSensitive = options.htmlElementCaseSensitive === true
+
+ const RESERVED_NAMES_IN_HTML = new Set(htmlElements)
+ const RESERVED_NAMES_IN_OTHERS = new Set([
+ ...deprecatedHtmlElements,
+ ...kebabCaseElements,
+ ...svgElements
+ ])
+
+ if (!htmlElementCaseSensitive) {
+ addAll(RESERVED_NAMES_IN_HTML, htmlElements.map(casing.capitalize))
+ addAll(RESERVED_NAMES_IN_OTHERS, [
+ ...deprecatedHtmlElements.map(casing.capitalize),
+ ...kebabCaseElements.map(casing.pascalCase),
+ ...svgElements.filter(isLowercase).map(casing.capitalize)
+ ])
+ }
const reservedNames = new Set([
...RESERVED_NAMES_IN_HTML,
@@ -117,6 +124,17 @@ module.exports = {
...RESERVED_NAMES_IN_OTHERS
])
+ /**
+ * @param {string} name
+ * @returns {string}
+ */
+ function getMessageId(name) {
+ if (RESERVED_NAMES_IN_HTML.has(name)) return 'reservedInHtml'
+ if (RESERVED_NAMES_IN_VUE.has(name)) return 'reservedInVue'
+ if (RESERVED_NAMES_IN_VUE3.has(name)) return 'reservedInVue3'
+ return 'reserved'
+ }
+
/**
* @param {Literal | TemplateLiteral} node
*/
diff --git a/tests/lib/rules/no-reserved-component-names.js b/tests/lib/rules/no-reserved-component-names.js
index 41cddcfd5..231e1f98d 100644
--- a/tests/lib/rules/no-reserved-component-names.js
+++ b/tests/lib/rules/no-reserved-component-names.js
@@ -410,6 +410,16 @@ const invalidElements = [
'xmp',
'Xmp'
]
+const invalidLowerCaseElements = []
+const invalidUpperCaseElements = []
+
+for (const element of invalidElements) {
+ if (element[0] === element[0].toLowerCase()) {
+ invalidLowerCaseElements.push(element)
+ } else {
+ invalidUpperCaseElements.push(element)
+ }
+}
const vue2BuiltInComponents = [
'component',
@@ -559,6 +569,16 @@ ruleTester.run('no-reserved-component-names', rule, {
languageOptions,
options: [{ disallowVueBuiltInComponents: true }]
})),
+ ...invalidUpperCaseElements.map((name) => ({
+ filename: `${name}.vue`,
+ code: `
+ export default {
+ name: '${name}'
+ }
+ `,
+ languageOptions,
+ options: [{ htmlElementCaseSensitive: true }]
+ })),
{
filename: 'test.vue',
code: ``,
@@ -701,6 +721,24 @@ ruleTester.run('no-reserved-component-names', rule, {
}
]
})),
+ ...invalidLowerCaseElements.map((name) => ({
+ filename: `${name}.vue`,
+ code: ``,
+ languageOptions: {
+ parser: require('vue-eslint-parser'),
+ ...languageOptions
+ },
+ options: [{ htmlElementCaseSensitive: true }],
+ errors: [
+ {
+ messageId: RESERVED_NAMES_IN_HTML.has(name)
+ ? 'reservedInHtml'
+ : 'reserved',
+ data: { name },
+ line: 1
+ }
+ ]
+ })),
...vue2BuiltInComponents.map((name) => ({
filename: `${name}.vue`,
code: `