Skip to content

Commit

Permalink
Add disallowVueBuiltInComponents and `disallowVue3BuiltInComponents…
Browse files Browse the repository at this point in the history
…` option that reports Vue built-in component names to the `vue/no-reserved-component-names` rule. (#1116)
  • Loading branch information
ota-meshi committed May 9, 2020
1 parent b940cb9 commit 0849a27
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 25 deletions.
45 changes: 43 additions & 2 deletions docs/rules/no-reserved-component-names.md
Expand Up @@ -9,7 +9,7 @@ description: disallow the use of reserved names in component definitions
## :book: Rule Details

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

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

Expand All @@ -26,14 +26,55 @@ export default {

## :wrench: Options

Nothing.
```json
{
"vue/no-reserved-component-names": ["error", {
"disallowVueBuiltInComponents": false,
"disallowVue3BuiltInComponents": 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`.

### `"disallowVueBuiltInComponents": true`

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

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

</eslint-code-block>

### `"disallowVue3BuiltInComponents": true`

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

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

</eslint-code-block>

## :books: Further reading

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

## :mag: Implementation

Expand Down
85 changes: 69 additions & 16 deletions lib/rules/no-reserved-component-names.js
Expand Up @@ -22,20 +22,44 @@ const kebabCaseElements = [
'missing-glyph'
]

// https://vuejs.org/v2/api/index.html#Built-In-Components
const vueBuiltInComponents = [
'component',
'transition',
'transition-group',
'keep-alive',
'slot'
]

const vue3BuiltInComponents = [
'teleport',
'suspense'
]

const isLowercase = (word) => /^[a-z]*$/.test(word)
const capitalizeFirstLetter = (word) => word[0].toUpperCase() + word.substring(1, word.length)

const RESERVED_NAMES = new Set(
[
...kebabCaseElements,
...kebabCaseElements.map(casing.pascalCase),
...htmlElements,
...htmlElements.map(capitalizeFirstLetter),
...deprecatedHtmlElements,
...deprecatedHtmlElements.map(capitalizeFirstLetter),
...svgElements,
...svgElements.filter(isLowercase).map(capitalizeFirstLetter)
])
const RESERVED_NAMES_IN_HTML = new Set([
...htmlElements,
...htmlElements.map(capitalizeFirstLetter)
])
const RESERVED_NAMES_IN_VUE = new Set([
...vueBuiltInComponents,
...vueBuiltInComponents.map(casing.pascalCase)
])
const RESERVED_NAMES_IN_VUE3 = new Set([
...RESERVED_NAMES_IN_VUE,
...vue3BuiltInComponents,
...vue3BuiltInComponents.map(casing.pascalCase)
])
const RESERVED_NAMES_IN_OTHERS = new Set([
...deprecatedHtmlElements,
...deprecatedHtmlElements.map(capitalizeFirstLetter),
...kebabCaseElements,
...kebabCaseElements.map(casing.pascalCase),
...svgElements,
...svgElements.filter(isLowercase).map(capitalizeFirstLetter)
])

// ------------------------------------------------------------------------------
// Rule Definition
Expand All @@ -46,14 +70,41 @@ module.exports = {
type: 'suggestion',
docs: {
description: 'disallow the use of reserved names in component definitions',
categories: undefined, // 'essential'
categories: undefined,
url: 'https://eslint.vuejs.org/rules/no-reserved-component-names.html'
},
fixable: null,
schema: []
schema: [{
type: 'object',
properties: {
disallowVueBuiltInComponents: {
type: 'boolean'
},
disallowVue3BuiltInComponents: {
type: 'boolean'
}
}
}],
messages: {
reserved: 'Name "{{name}}" is reserved.',
reservedInHtml: 'Name "{{name}}" is reserved in HTML.',
reservedInVue: 'Name "{{name}}" is reserved in Vue.js.',
reservedInVue3: 'Name "{{name}}" is reserved in Vue.js 3.x.'
}
},

create (context) {
const options = context.options[0] || {}
const disallowVueBuiltInComponents = options.disallowVueBuiltInComponents === true
const disallowVue3BuiltInComponents = options.disallowVue3BuiltInComponents === true

const reservedNames = new Set([
...RESERVED_NAMES_IN_HTML,
...(disallowVueBuiltInComponents ? RESERVED_NAMES_IN_VUE : []),
...(disallowVue3BuiltInComponents ? RESERVED_NAMES_IN_VUE3 : []),
...RESERVED_NAMES_IN_OTHERS
])

function canVerify (node) {
return node.type === 'Literal' || (
node.type === 'TemplateLiteral' &&
Expand All @@ -70,15 +121,17 @@ module.exports = {
} else {
name = node.value
}
if (RESERVED_NAMES.has(name)) {
if (reservedNames.has(name)) {
report(node, name)
}
}

function report (node, name) {
context.report({
node: node,
message: 'Name "{{name}}" is reserved.',
messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml'
: RESERVED_NAMES_IN_VUE.has(name) ? 'reservedInVue'
: RESERVED_NAMES_IN_VUE3.has(name) ? 'reservedInVue3' : 'reserved',
data: {
name: name
}
Expand All @@ -98,7 +151,7 @@ module.exports = {
utils.executeOnVue(context, (obj) => {
// Report if a component has been registered locally with a reserved name.
utils.getRegisteredComponents(obj)
.filter(({ name }) => RESERVED_NAMES.has(name))
.filter(({ name }) => reservedNames.has(name))
.forEach(({ node, name }) => report(node, name))

const node = obj.properties
Expand Down

0 comments on commit 0849a27

Please sign in to comment.