Skip to content

Commit 8d8e3d7

Browse files
committed
Add rule html-attributes-casing.
1 parent 39c9df5 commit 8d8e3d7

File tree

6 files changed

+460
-4
lines changed

6 files changed

+460
-4
lines changed

docs/rules/attribute-hyphenation.md

Lines changed: 53 additions & 0 deletions
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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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 kebabCase = casing.getConverter('kebab-case')
20+
const caseConverter = casing.getConverter(useHyphenated ? 'kebab-case' : 'camelCase')
21+
22+
function reportIssue (node, name) {
23+
const text = sourceCode.getText(node.key)
24+
25+
context.report({
26+
node: node.key,
27+
loc: node.loc,
28+
message: useHyphenated ? "Attribute '{{text}}' must be hyphenated." : "Attribute '{{text}}' cann't be hyphenated.",
29+
data: {
30+
text
31+
},
32+
fix: fixer => fixer.replaceText(node.key, text.replace(name, caseConverter(name)))
33+
})
34+
}
35+
36+
function isIgnoredAttribute (value) {
37+
if (value.indexOf('data-') !== -1 || value.indexOf('aria-') !== -1) {
38+
return true
39+
}
40+
return useHyphenated ? kebabCase(value) === value : kebabCase(value) !== value
41+
}
42+
43+
// ----------------------------------------------------------------------
44+
// Public
45+
// ----------------------------------------------------------------------
46+
47+
utils.registerTemplateBodyVisitor(context, {
48+
VAttribute (node) {
49+
if (!utils.isCustomComponent(node.parent.parent)) return
50+
51+
const name = !node.directive ? node.key.name : node.key.name === 'bind' ? node.key.argument : false
52+
if (!name) return
53+
54+
if (isIgnoredAttribute(name)) {
55+
return
56+
}
57+
reportIssue(node, name)
58+
}
59+
})
60+
61+
return {}
62+
}
63+
64+
module.exports = {
65+
meta: {
66+
docs: {
67+
description: 'Define a style for the props casing in templates.',
68+
category: 'Stylistic Issues',
69+
recommended: false
70+
},
71+
fixable: 'code',
72+
schema: [
73+
{
74+
enum: ['always', 'never']
75+
}
76+
]
77+
},
78+
79+
create
80+
}

lib/rules/name-property-casing.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ module.exports = {
5151
category: 'Stylistic Issues',
5252
recommended: false
5353
},
54-
fixable: 'code', // or "code" or "whitespace"
54+
fixable: 'code',
5555
schema: [
5656
{
5757
enum: casing.allowedCaseOptions

lib/utils/index.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json'))
1313
const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json'))
14+
const MATHML_ELEMENT_NAMES = new Set(require('./mathml-elements.json'))
1415
const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json'))
1516
const assert = require('assert')
1617

@@ -202,9 +203,9 @@ module.exports = {
202203

203204
const name = node.name
204205
return (
205-
!(this.isHtmlElementName(name) || this.isSvgElementName(name)) ||
206-
this.hasAttribute(node, 'is') ||
207-
this.hasDirective(node, 'bind', 'is')
206+
!(this.isHtmlElementName(name) || this.isSvgElementName(name) || this.isMathMLElementName(name)) ||
207+
this.hasAttribute(node, 'is') ||
208+
this.hasDirective(node, 'bind', 'is')
208209
)
209210
},
210211

@@ -230,6 +231,17 @@ module.exports = {
230231
return SVG_ELEMENT_NAMES.has(name.toLowerCase())
231232
},
232233

234+
/**
235+
* Check whether the given name is a MathML element name or not.
236+
* @param {string} name The name to check.
237+
* @returns {boolean} `true` if the name is a HTML element name.
238+
*/
239+
isMathMLElementName (name) {
240+
assert(typeof name === 'string')
241+
242+
return MATHML_ELEMENT_NAMES.has(name.toLowerCase())
243+
},
244+
233245
/**
234246
* Check whether the given name is a void element name or not.
235247
* @param {string} name The name to check.

lib/utils/mathml-elements.json

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
[
2+
"abs",
3+
"and",
4+
"annotation",
5+
"annotation-xml",
6+
"apply",
7+
"approx",
8+
"arccos",
9+
"arccosh",
10+
"arccot",
11+
"arccoth",
12+
"arccsc",
13+
"arccsch",
14+
"arcsec",
15+
"arcsech",
16+
"arcsin",
17+
"arcsinh",
18+
"arctan",
19+
"arctanh",
20+
"arg",
21+
"bind",
22+
"bvar",
23+
"card",
24+
"cartesianproduct",
25+
"cbytes",
26+
"ceiling",
27+
"cerror",
28+
"ci",
29+
"cn",
30+
"codomain",
31+
"complexes",
32+
"compose",
33+
"condition",
34+
"conjugate",
35+
"cos",
36+
"cosh",
37+
"cot",
38+
"coth",
39+
"cs",
40+
"csc",
41+
"csch",
42+
"csymbol",
43+
"curl",
44+
"declare",
45+
"degree",
46+
"determinant",
47+
"diff",
48+
"divergence",
49+
"divide",
50+
"domain",
51+
"domainofapplication",
52+
"emptyset",
53+
"encoding",
54+
"eq",
55+
"equivalent",
56+
"eulergamma",
57+
"exists",
58+
"exp",
59+
"exponentiale",
60+
"factorial",
61+
"factorof",
62+
"false",
63+
"floor",
64+
"fn",
65+
"forall",
66+
"function",
67+
"gcd",
68+
"geq",
69+
"grad",
70+
"gt",
71+
"ident",
72+
"image",
73+
"imaginary",
74+
"imaginaryi",
75+
"implies",
76+
"in",
77+
"infinity",
78+
"int",
79+
"integers",
80+
"intersect",
81+
"interval",
82+
"inverse",
83+
"lambda",
84+
"laplacian",
85+
"lcm",
86+
"leq",
87+
"limit",
88+
"list",
89+
"ln",
90+
"log",
91+
"logbase",
92+
"lowlimit",
93+
"lt",
94+
"m:apply",
95+
"m:mrow",
96+
"maction",
97+
"malign",
98+
"maligngroup",
99+
"malignmark",
100+
"malignscope",
101+
"math",
102+
"matrix",
103+
"matrixrow",
104+
"max",
105+
"mean",
106+
"median",
107+
"menclose",
108+
"merror",
109+
"mfenced",
110+
"mfrac",
111+
"mfraction",
112+
"mglyph",
113+
"mi",
114+
"min",
115+
"minus",
116+
"mlabeledtr",
117+
"mlongdiv",
118+
"mmultiscripts",
119+
"mn",
120+
"mo",
121+
"mode",
122+
"moment",
123+
"momentabout",
124+
"mover",
125+
"mpadded",
126+
"mphantom",
127+
"mprescripts",
128+
"mroot",
129+
"mrow",
130+
"ms",
131+
"mscarries",
132+
"mscarry",
133+
"msgroup",
134+
"msline",
135+
"mspace",
136+
"msqrt",
137+
"msrow",
138+
"mstack",
139+
"mstyle",
140+
"msub",
141+
"msubsup",
142+
"msup",
143+
"mtable",
144+
"mtd",
145+
"mtext",
146+
"mtr",
147+
"munder",
148+
"munderover",
149+
"naturalnumbers",
150+
"neq",
151+
"none",
152+
"not",
153+
"notanumber",
154+
"notin",
155+
"notprsubset",
156+
"notsubset",
157+
"or",
158+
"otherwise",
159+
"outerproduct",
160+
"partialdiff",
161+
"pi",
162+
"piece",
163+
"piecewice",
164+
"piecewise",
165+
"plus",
166+
"power",
167+
"primes",
168+
"product",
169+
"prsubset",
170+
"quotient",
171+
"rationals",
172+
"real",
173+
"reals",
174+
"reln",
175+
"rem",
176+
"root",
177+
"scalarproduct",
178+
"sdev",
179+
"sec",
180+
"sech",
181+
"select",
182+
"selector",
183+
"semantics",
184+
"sep",
185+
"set",
186+
"setdiff",
187+
"share",
188+
"sin",
189+
"sinh",
190+
"span",
191+
"subset",
192+
"sum",
193+
"tan",
194+
"tanh",
195+
"tendsto",
196+
"times",
197+
"transpose",
198+
"true",
199+
"union",
200+
"uplimit",
201+
"var",
202+
"variance",
203+
"vector",
204+
"vectorproduct",
205+
"xor"
206+
]

0 commit comments

Comments
 (0)