Skip to content

Commit c49a2e2

Browse files
ota-meshimichalsnik
authored andcommitted
⭐️New: Add vue/component-name-in-template-casing (#397)
* Add Vue.extend support, add missing info about Vue.mixin check in readme * Docs: fixes wording in docs (#372) * Fix: fix script-indent to prevent removing <script> tag (fixes #367) (#374) * [Update] Make `vue/max-attributes-per-line` fixable (#380) * [Update] Make `vue/max-attributes-per-line` fixable * [fix] bug and style * [fix] Switch indent calculation method with node and attribute * [fix] don't handle indentation * [add] autofix test max-attributes-per-line.js * Update: make `vue/order-in-components` fixable (#381) * [Update] Make `vue/order-in-components` fixable This Commit makes `vue/order-in-components` fixable. In case of `The "A" property should be above the "B" property` error, autofix will move A before B * [fix] fail test at [email protected] * [fix] If there is a possibility of a side effect, don't autofix * [fix] failed test at node v4 * [update] use Traverser * [fix] failed test [email protected] * [fix] I used `output: null` to specify "not fix" * [New] Add `vue/component-name-in-template-casing` * [update] documents * [fix] require-meta-docs-url * [fix] failed tests * [fix] review contents * Default case to constant. and used it. `const defaultCase = 'PascalCase'` * `'category'` to `undefined` * Add empty line for clarity * I used object shorthand. `caseType: caseType` to `caseType` * [fix] No deletes space and attributes of endTag * [fix] Remove test unnecessary option * [fix] lint error caused by merging the master for conflict resolution * Add ignores option. * Fixed that extra differences. * update docs link * Update formatting
1 parent 766b637 commit c49a2e2

6 files changed

+505
-0
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
190190
| | Rule ID | Description |
191191
|:---|:--------|:------------|
192192
| :wrench: | [vue/attribute-hyphenation](./docs/rules/attribute-hyphenation.md) | enforce attribute naming style on custom components in template |
193+
| :wrench: | [vue/component-name-in-template-casing](./docs/rules/component-name-in-template-casing.md) | enforce specific casing for the component naming style in template |
193194
| :wrench: | [vue/html-closing-bracket-newline](./docs/rules/html-closing-bracket-newline.md) | require or disallow a line break before tag's closing brackets |
194195
| :wrench: | [vue/html-closing-bracket-spacing](./docs/rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets |
195196
| :wrench: | [vue/html-end-tags](./docs/rules/html-end-tags.md) | enforce end tag style |

Diff for: docs/rules/component-name-in-template-casing.md

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# enforce specific casing for the component naming style in template (vue/component-name-in-template-casing)
2+
3+
- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
4+
- :wrench: The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.
5+
6+
Define a style for the component name in template casing for consistency purposes.
7+
8+
## :book: Rule Details
9+
10+
:+1: Examples of **correct** code for `PascalCase`:
11+
12+
```html
13+
<template>
14+
<TheComponent />
15+
</template>
16+
```
17+
18+
:-1: Examples of **incorrect** code for `PascalCase`:
19+
20+
```html
21+
<template>
22+
<the-component />
23+
<theComponent />
24+
<The-component />
25+
</template>
26+
```
27+
28+
:+1: Examples of **correct** code for `kebab-case`:
29+
30+
```html
31+
<template>
32+
<the-component />
33+
</template>
34+
```
35+
36+
:-1: Examples of **incorrect** code for `kebab-case`:
37+
38+
```html
39+
<template>
40+
<TheComponent />
41+
<theComponent />
42+
<Thecomponent />
43+
<The-component />
44+
</template>
45+
```
46+
47+
## :wrench: Options
48+
49+
Default casing is set to `PascalCase`.
50+
51+
```json
52+
"vue/component-name-in-template-casing": ["error",
53+
"PascalCase|kebab-case",
54+
{
55+
"ignores": []
56+
}
57+
]
58+
```
59+
60+
- `ignores` (`string[]`) ... The element name to ignore. Sets the element name to allow. For example, a custom element or a non-Vue component.
61+
62+
63+
:+1: Examples of **correct** code for `{ignores: ["custom-element"]}`:
64+
65+
```html
66+
<template>
67+
<custom-element></custom-element>
68+
<TheComponent/>
69+
</template>
70+
```
71+
72+
## Related links
73+
74+
- [Style guide - Component name casing in templates](https://vuejs.org/v2/style-guide/#Component-name-casing-in-templates-strongly-recommended)

Diff for: lib/configs/strongly-recommended.js

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports = {
77
extends: require.resolve('./essential'),
88
rules: {
99
'vue/attribute-hyphenation': 'error',
10+
'vue/component-name-in-template-casing': 'error',
1011
'vue/html-closing-bracket-newline': 'error',
1112
'vue/html-closing-bracket-spacing': 'error',
1213
'vue/html-end-tags': 'error',

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'attribute-hyphenation': require('./rules/attribute-hyphenation'),
1111
'attributes-order': require('./rules/attributes-order'),
1212
'comment-directive': require('./rules/comment-directive'),
13+
'component-name-in-template-casing': require('./rules/component-name-in-template-casing'),
1314
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),
1415
'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),
1516
'html-end-tags': require('./rules/html-end-tags'),

Diff for: lib/rules/component-name-in-template-casing.js

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* @author Yosuke Ota
3+
* issue https://github.com/vuejs/eslint-plugin-vue/issues/250
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
const casing = require('../utils/casing')
13+
14+
const allowedCaseOptions = ['PascalCase', 'kebab-case']
15+
const defaultCase = 'PascalCase'
16+
17+
// ------------------------------------------------------------------------------
18+
// Rule Definition
19+
// ------------------------------------------------------------------------------
20+
21+
module.exports = {
22+
meta: {
23+
docs: {
24+
description: 'enforce specific casing for the component naming style in template',
25+
category: undefined, // strongly-recommended
26+
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.1/docs/rules/component-name-in-template-casing.md'
27+
},
28+
fixable: 'code',
29+
schema: [
30+
{
31+
enum: allowedCaseOptions
32+
},
33+
{
34+
type: 'object',
35+
properties: {
36+
ignores: {
37+
type: 'array',
38+
items: { type: 'string' },
39+
uniqueItems: true,
40+
additionalItems: false
41+
}
42+
},
43+
additionalProperties: false
44+
}
45+
]
46+
},
47+
48+
create (context) {
49+
const caseOption = context.options[0]
50+
const options = context.options[1] || {}
51+
const caseType = allowedCaseOptions.indexOf(caseOption) !== -1 ? caseOption : defaultCase
52+
const ignores = options.ignores || []
53+
const tokens = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
54+
const sourceCode = context.getSourceCode()
55+
56+
let hasInvalidEOF = false
57+
58+
return utils.defineTemplateBodyVisitor(context, {
59+
'VElement' (node) {
60+
if (hasInvalidEOF) {
61+
return
62+
}
63+
64+
if (!utils.isCustomComponent(node)) {
65+
return
66+
}
67+
68+
const name = node.rawName
69+
if (ignores.indexOf(name) >= 0) {
70+
return
71+
}
72+
const casingName = casing.getConverter(caseType)(name)
73+
if (casingName !== name) {
74+
const startTag = node.startTag
75+
const open = tokens.getFirstToken(startTag)
76+
77+
context.report({
78+
node: open,
79+
loc: open.loc,
80+
message: 'Component name "{{name}}" is not {{caseType}}.',
81+
data: {
82+
name,
83+
caseType
84+
},
85+
fix: fixer => {
86+
const endTag = node.endTag
87+
if (!endTag) {
88+
return fixer.replaceText(open, `<${casingName}`)
89+
}
90+
const endTagOpen = tokens.getFirstToken(endTag)
91+
// If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
92+
// return [
93+
// fixer.replaceText(open, `<${casingName}`),
94+
// fixer.replaceText(endTagOpen, `</${casingName}`)
95+
// ]
96+
const code = `<${casingName}${sourceCode.text.slice(open.range[1], endTagOpen.range[0])}</${casingName}`
97+
return fixer.replaceTextRange([open.range[0], endTagOpen.range[1]], code)
98+
}
99+
})
100+
}
101+
}
102+
}, {
103+
Program (node) {
104+
hasInvalidEOF = utils.hasInvalidEOF(node)
105+
}
106+
})
107+
}
108+
}

0 commit comments

Comments
 (0)