Skip to content

Commit

Permalink
feat(no-reserved-component-names): add case sensitive option (#2594)
Browse files Browse the repository at this point in the history
Co-authored-by: Flo Edelmann <[email protected]>
  • Loading branch information
waynzh and FloEdelmann authored Nov 11, 2024
1 parent 4729a3b commit 9205cc8
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 21 deletions.
32 changes: 31 additions & 1 deletion docs/rules/no-reserved-component-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down Expand Up @@ -73,6 +75,34 @@ export default {

</eslint-code-block>

### `"htmlElementCaseSensitive": true`

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

```vue
<script>
/* ✓ GOOD */
export default {
name: 'Button'
}
</script>
```

</eslint-code-block>

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

```vue
<script>
/* ✗ BAD */
export default {
name: 'button'
}
</script>
```

</eslint-code-block>

## :couple: Related Rules

- [vue/multi-word-component-names](./multi-word-component-names.md)
Expand Down
58 changes: 38 additions & 20 deletions lib/rules/no-reserved-component-names.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
Expand All @@ -61,14 +48,14 @@ function canVerify(node) {
}

/**
* @param {string} name
* @returns {string}
* @template T
* @param {Set<T>} set
* @param {Iterable<T>} 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 = {
Expand All @@ -90,6 +77,9 @@ module.exports = {
},
disallowVue3BuiltInComponents: {
type: 'boolean'
},
htmlElementCaseSensitive: {
type: 'boolean'
}
},
additionalProperties: false
Expand All @@ -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,
Expand All @@ -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
*/
Expand Down
38 changes: 38 additions & 0 deletions tests/lib/rules/no-reserved-component-names.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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: `<script setup> defineOptions({}) </script>`,
Expand Down Expand Up @@ -701,6 +721,24 @@ ruleTester.run('no-reserved-component-names', rule, {
}
]
})),
...invalidLowerCaseElements.map((name) => ({
filename: `${name}.vue`,
code: `<script setup> defineOptions({name: '${name}'}) </script>`,
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: `
Expand Down

0 comments on commit 9205cc8

Please sign in to comment.