Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
multi-word-component-names
rule (#1661)
* Add `multi-word-component-names` rule * Fix review comments for `multi-word-component-names` rule
- Loading branch information
1 parent
928e0c6
commit a74dd59
Showing
5 changed files
with
454 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
--- | ||
pageClass: rule-details | ||
sidebarDepth: 0 | ||
title: vue/multi-word-component-names | ||
description: require component names to be always multi-word | ||
--- | ||
# vue/multi-word-component-names | ||
|
||
> require component names to be always multi-word | ||
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge> | ||
|
||
## :book: Rule Details | ||
|
||
This rule require component names to be always multi-word, except for root `App` | ||
components, and built-in components provided by Vue, such as `<transition>` or | ||
`<component>`. This prevents conflicts with existing and future HTML elements, | ||
since all HTML elements are a single word. | ||
|
||
<eslint-code-block filename="src/TodoItem.js" language="javascript" :rules="{'vue/multi-word-component-names': ['error']}"> | ||
|
||
```js | ||
/* ✓ GOOD */ | ||
Vue.component('todo-item', { | ||
// ... | ||
}) | ||
|
||
/* ✗ BAD */ | ||
Vue.component('Todo', { | ||
// ... | ||
}) | ||
``` | ||
</eslint-code-block> | ||
|
||
<eslint-code-block filename="src/TodoItem.js" :rules="{'vue/multi-word-component-names': ['error']}"> | ||
|
||
```vue | ||
<script> | ||
/* ✓ GOOD */ | ||
export default { | ||
name: 'TodoItem', | ||
// ... | ||
} | ||
</script> | ||
``` | ||
</eslint-code-block> | ||
|
||
<eslint-code-block filename="src/Todo.vue" :rules="{'vue/multi-word-component-names': ['error']}"> | ||
|
||
```vue | ||
<script> | ||
/* ✗ BAD */ | ||
export default { | ||
name: 'Todo', | ||
// ... | ||
} | ||
</script> | ||
``` | ||
</eslint-code-block> | ||
|
||
<eslint-code-block filename="src/Todo.vue" :rules="{'vue/multi-word-component-names': ['error']}"> | ||
|
||
```vue | ||
<script> | ||
/* ✗ BAD */ | ||
export default { | ||
// ... | ||
} | ||
</script> | ||
``` | ||
</eslint-code-block> | ||
|
||
## :wrench: Options | ||
|
||
Nothing. | ||
|
||
## :books: Further Reading | ||
|
||
- [Style guide - Multi-word component names](https://vuejs.org/v2/style-guide/#Multi-word-component-names-essential) | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/multi-word-component-names.js) | ||
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/multi-word-component-names.js) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/** | ||
* @author Marton Csordas | ||
* See LICENSE file in root directory for full license. | ||
*/ | ||
'use strict' | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Requirements | ||
// ------------------------------------------------------------------------------ | ||
|
||
const casing = require('../utils/casing') | ||
const utils = require('../utils') | ||
|
||
const RESERVED_NAMES_IN_VUE3 = new Set( | ||
require('../utils/vue3-builtin-components') | ||
) | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Helpers | ||
// ------------------------------------------------------------------------------ | ||
|
||
/** | ||
* Returns true if the given component name is valid, otherwise false. | ||
* @param {string} name | ||
* */ | ||
function isValidComponentName(name) { | ||
if (name.toLowerCase() === 'app' || RESERVED_NAMES_IN_VUE3.has(name)) { | ||
return true | ||
} else { | ||
const elements = casing.kebabCase(name).split('-') | ||
return elements.length > 1 | ||
} | ||
} | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: 'require component names to be always multi-word', | ||
categories: undefined, | ||
url: 'https://eslint.vuejs.org/rules/multi-word-component-names.html' | ||
}, | ||
schema: [], | ||
messages: { | ||
unexpected: 'Component name "{{value}}" should always be multi-word.' | ||
} | ||
}, | ||
/** @param {RuleContext} context */ | ||
create(context) { | ||
const fileName = context.getFilename() | ||
let componentName = fileName.replace(/\.[^/.]+$/, '') | ||
|
||
return utils.compositingVisitors( | ||
{ | ||
/** @param {Program} node */ | ||
Program(node) { | ||
if ( | ||
!node.body.length && | ||
utils.isVueFile(fileName) && | ||
!isValidComponentName(componentName) | ||
) { | ||
context.report({ | ||
messageId: 'unexpected', | ||
data: { | ||
value: componentName | ||
}, | ||
loc: { line: 1, column: 0 } | ||
}) | ||
} | ||
} | ||
}, | ||
|
||
utils.executeOnVue(context, (obj) => { | ||
const node = utils.findProperty(obj, 'name') | ||
|
||
/** @type {SourceLocation | null} */ | ||
let loc = null | ||
|
||
// Check if the component has a name property. | ||
if (node) { | ||
const valueNode = node.value | ||
if (valueNode.type !== 'Literal') return | ||
|
||
componentName = `${valueNode.value}` | ||
loc = node.loc | ||
} else if ( | ||
obj.parent.type === 'CallExpression' && | ||
obj.parent.arguments.length === 2 | ||
) { | ||
// The component is registered globally with 'Vue.component', where | ||
// the first paremter is the component name. | ||
const argument = obj.parent.arguments[0] | ||
if (argument.type !== 'Literal') return | ||
|
||
componentName = `${argument.value}` | ||
loc = argument.loc | ||
} | ||
|
||
if (!isValidComponentName(componentName)) { | ||
context.report({ | ||
messageId: 'unexpected', | ||
data: { | ||
value: componentName | ||
}, | ||
loc: loc || { line: 1, column: 0 } | ||
}) | ||
} | ||
}) | ||
) | ||
} | ||
} |
Oops, something went wrong.