Skip to content

Commit 55d388c

Browse files
armano2mysticatea
authored andcommitted
New: add rule attribute-hyphenation. (fixes #92)(#95)
* Update vue-eslint-parser to version 2.0.0-beta.3 * Add rule `html-attributes-casing`.
1 parent da311e9 commit 55d388c

File tree

5 files changed

+249
-4
lines changed

5 files changed

+249
-4
lines changed

docs/rules/attribute-hyphenation.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Define if attributes on cusom components can be hyphened. (attribute-hyphenation)
2+
3+
## :wrench: Options
4+
5+
Default casing is set to `always`
6+
7+
```
8+
'vue/attribute-hyphenation': [2, 'always'|'never']
9+
```
10+
11+
### `"always"` - Use hyphenated name. (It errors on upper case letters.)
12+
13+
:+1: Examples of **correct** code`:
14+
15+
```html
16+
<template>
17+
<foo my-prop="prop">
18+
<a onClick="return false"></a>
19+
</foo>
20+
</template>
21+
```
22+
23+
:-1: Examples of **incorrect** code`:
24+
25+
```html
26+
<template>
27+
<foo myProp="prop">
28+
<a onClick="return false"></a>
29+
</foo>
30+
</template>
31+
```
32+
33+
### `"never"` - Don't use hyphenated name. (It errors on hyphens except `data-` and `aria-`.)
34+
35+
:+1: Examples of **correct** code`:
36+
37+
```html
38+
<template>
39+
<foo myProp="prop">
40+
<a onClick="return false"></a>
41+
</foo>
42+
</template>
43+
```
44+
45+
:-1: Examples of **incorrect** code`:
46+
47+
```html
48+
<template>
49+
<foo my-prop="prop">
50+
<a onClick="return false"></a>
51+
</foo>
52+
</template>
53+
```

lib/rules/attribute-hyphenation.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @fileoverview Define a style for the props casing in templates.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
const casing = require('../utils/casing')
9+
10+
// ------------------------------------------------------------------------------
11+
// Rule Definition
12+
// ------------------------------------------------------------------------------
13+
14+
function create (context) {
15+
const sourceCode = context.getSourceCode()
16+
const options = context.options[0]
17+
const useHyphenated = options !== 'never'
18+
19+
const caseConverter = casing.getConverter(useHyphenated ? 'kebab-case' : 'camelCase')
20+
21+
function reportIssue (node, name) {
22+
const text = sourceCode.getText(node.key)
23+
24+
context.report({
25+
node: node.key,
26+
loc: node.loc,
27+
message: useHyphenated ? "Attribute '{{text}}' must be hyphenated." : "Attribute '{{text}}' cann't be hyphenated.",
28+
data: {
29+
text
30+
},
31+
fix: fixer => fixer.replaceText(node.key, text.replace(name, caseConverter(name)))
32+
})
33+
}
34+
35+
function isIgnoredAttribute (value) {
36+
if (value.indexOf('data-') !== -1 || value.indexOf('aria-') !== -1) {
37+
return true
38+
}
39+
return useHyphenated ? value.toLowerCase() === value : !/-/.test(value)
40+
}
41+
42+
// ----------------------------------------------------------------------
43+
// Public
44+
// ----------------------------------------------------------------------
45+
46+
utils.registerTemplateBodyVisitor(context, {
47+
VAttribute (node) {
48+
if (!utils.isCustomComponent(node.parent.parent)) return
49+
50+
const name = !node.directive ? node.key.rawName : node.key.name === 'bind' ? node.key.raw.argument : false
51+
if (!name || isIgnoredAttribute(name)) return
52+
53+
reportIssue(node, name)
54+
}
55+
})
56+
57+
return {}
58+
}
59+
60+
module.exports = {
61+
meta: {
62+
docs: {
63+
description: 'Define a style for the props casing in templates.',
64+
category: 'Stylistic Issues',
65+
recommended: false
66+
},
67+
fixable: 'code',
68+
schema: [
69+
{
70+
enum: ['always', 'never']
71+
}
72+
]
73+
},
74+
75+
create
76+
}

package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
},
4646
"dependencies": {
4747
"requireindex": "^1.1.0",
48-
"vue-eslint-parser": "^2.0.0-beta.2"
48+
"vue-eslint-parser": "^2.0.0-beta.3"
4949
},
5050
"devDependencies": {
5151
"@types/node": "^4.2.16",
+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @fileoverview Define a style for the props casing in templates.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/attribute-hyphenation')
12+
13+
const RuleTester = require('eslint').RuleTester
14+
15+
// ------------------------------------------------------------------------------
16+
// Tests
17+
// ------------------------------------------------------------------------------
18+
19+
const ruleTester = new RuleTester({
20+
parser: 'vue-eslint-parser',
21+
parserOptions: { ecmaVersion: 2015 }
22+
})
23+
24+
ruleTester.run('attribute-hyphenation', rule, {
25+
26+
valid: [
27+
{
28+
filename: 'test.vue',
29+
code: ''
30+
},
31+
{
32+
filename: 'test.vue',
33+
code: '<template><div><custom data-id="foo" aria-test="bar" my-prop="prop"></custom></div></template>',
34+
options: ['always']
35+
},
36+
{
37+
filename: 'test.vue',
38+
code: '<template><div><custom data-id="foo" aria-test="bar" myProp="prop"></custom></div></template>',
39+
options: ['never']
40+
},
41+
{
42+
filename: 'test.vue',
43+
code: '<template><div data-id="foo" aria-test="bar"><a onClick="" my-prop="prop"></a></div></template>',
44+
options: ['never']
45+
}
46+
],
47+
48+
invalid: [
49+
{
50+
filename: 'test.vue',
51+
code: '<template><div><custom my-prop="foo"></custom></div></template>',
52+
output: '<template><div><custom myProp="foo"></custom></div></template>',
53+
options: ['never'],
54+
errors: [{
55+
message: "Attribute 'my-prop' cann't be hyphenated.",
56+
type: 'VIdentifier',
57+
line: 1
58+
}]
59+
},
60+
{
61+
filename: 'test.vue',
62+
code: '<template><div><custom MyProp="Bar"></custom></div></template>',
63+
output: '<template><div><custom my-prop="Bar"></custom></div></template>',
64+
options: ['always'],
65+
errors: [{
66+
message: "Attribute 'MyProp' must be hyphenated.",
67+
type: 'VIdentifier',
68+
line: 1
69+
}]
70+
},
71+
{
72+
filename: 'test.vue',
73+
code: '<template><div><custom :my-prop="prop"></custom></div></template>',
74+
output: '<template><div><custom :myProp="prop"></custom></div></template>',
75+
options: ['never'],
76+
errors: [{
77+
message: "Attribute ':my-prop' cann't be hyphenated.",
78+
type: 'VDirectiveKey',
79+
line: 1
80+
}]
81+
},
82+
{
83+
filename: 'test.vue',
84+
code: '<template><div><custom :MyProp="prop"></custom></div></template>',
85+
output: '<template><div><custom :my-prop="prop"></custom></div></template>',
86+
options: ['always'],
87+
errors: [{
88+
message: "Attribute ':MyProp' must be hyphenated.",
89+
type: 'VDirectiveKey',
90+
line: 1
91+
}]
92+
},
93+
{
94+
filename: 'test.vue',
95+
code: '<template><div><custom v-bind:my-prop="prop"></custom></div></template>',
96+
output: '<template><div><custom v-bind:myProp="prop"></custom></div></template>',
97+
options: ['never'],
98+
errors: [{
99+
message: "Attribute 'v-bind:my-prop' cann't be hyphenated.",
100+
type: 'VDirectiveKey',
101+
line: 1
102+
}]
103+
},
104+
{
105+
filename: 'test.vue',
106+
code: '<template><div><custom v-bind:MyProp="prop"></custom></div></template>',
107+
output: '<template><div><custom v-bind:my-prop="prop"></custom></div></template>',
108+
options: ['always'],
109+
errors: [{
110+
message: "Attribute 'v-bind:MyProp' must be hyphenated.",
111+
type: 'VDirectiveKey',
112+
line: 1
113+
}]
114+
}
115+
]
116+
})

0 commit comments

Comments
 (0)