Skip to content

Commit

Permalink
⭐️ New: Add rule no-reserved-component-names (#757)
Browse files Browse the repository at this point in the history
* ⭐ add rule no-reserved-component-names

* Increase test coverage

* Checking elements against element lists

* 🔨 Update PR with ota-meshi suggestions

* 🔨 Lint locally registered components

* 👌 Adding tests to validate slot and template

* 🔨 Linting for deprecated elements
  • Loading branch information
shadskii authored and ota-meshi committed Dec 26, 2019
1 parent 06dc4a2 commit b19843c
Show file tree
Hide file tree
Showing 4 changed files with 529 additions and 0 deletions.
28 changes: 28 additions & 0 deletions docs/rules/no-reserved-component-names.md
@@ -0,0 +1,28 @@
# vue/no-reserved-component-names
> disallow the use of reserved names in component definitions
- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/recommended"`, and `"plugin:vue/strongly-recommended"`.

## :book: Rule Details

This rule prevents name collisions between vue components and standard html elements.

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

```vue
<script>
/* ✗ BAD */
export default {
name: 'div'
}
</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)
125 changes: 125 additions & 0 deletions lib/rules/no-reserved-component-names.js
@@ -0,0 +1,125 @@
/**
* @fileoverview disallow the use of reserved names in component definitions
* @author Jake Hassel <https://github.com/shadskii>
*/
'use strict'

const utils = require('../utils')
const casing = require('../utils/casing')

const htmlElements = require('../utils/html-elements.json')
const deprecatedHtmlElements = require('../utils/deprecated-html-elements.json')
const svgElements = require('../utils/svg-elements.json')

const kebabCaseElements = [
'annotation-xml',
'color-profile',
'font-face',
'font-face-src',
'font-face-uri',
'font-face-format',
'font-face-name',
'missing-glyph'
]

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)
])

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow the use of reserved names in component definitions',
category: undefined, // 'essential'
url: 'https://eslint.vuejs.org/rules/no-reserved-component-names.html'
},
fixable: null,
schema: []
},

create (context) {
function canVerify (node) {
return node.type === 'Literal' || (
node.type === 'TemplateLiteral' &&
node.expressions.length === 0 &&
node.quasis.length === 1
)
}

function reportIfInvalid (node) {
let name
if (node.type === 'TemplateLiteral') {
const quasis = node.quasis[0]
name = quasis.value.cooked
} else {
name = node.value
}
if (RESERVED_NAMES.has(name)) {
report(node, name)
}
}

function report (node, name) {
context.report({
node: node,
message: 'Name "{{name}}" is reserved.',
data: {
name: name
}
})
}

return Object.assign({},
{
"CallExpression > MemberExpression > Identifier[name='component']" (node) {
const parent = node.parent.parent
const calleeObject = utils.unwrapTypes(parent.callee.object)

if (calleeObject.type === 'Identifier' &&
calleeObject.name === 'Vue' &&
parent.arguments &&
parent.arguments.length === 2
) {
const argument = parent.arguments[0]

if (canVerify(argument)) {
reportIfInvalid(argument)
}
}
}
},
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))
.forEach(({ node, name }) => report(node, name))

const node = obj.properties
.find(item => (
item.type === 'Property' &&
item.key.name === 'name' &&
canVerify(item.value)
))

if (!node) return
reportIfInvalid(node.value)
})
)
}
}
1 change: 1 addition & 0 deletions lib/utils/deprecated-html-elements.json
@@ -0,0 +1 @@
["acronym","applet","basefont","bgsound","big","blink","center","command","content","dir","element","font","frame","frameset","image","isindex","keygen","listing","marquee","menuitem","multicol","nextid","nobr","noembed","noframes","plaintext","shadow","spacer","strike","tt","xmp"]

0 comments on commit b19843c

Please sign in to comment.