Skip to content

Commit 014110b

Browse files
committed
Add "no-multi-spaces" rule
1 parent 8761bde commit 014110b

File tree

4 files changed

+283
-1
lines changed

4 files changed

+283
-1
lines changed

docs/rules/no-multi-spaces.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This rule warns about the usage of extra whitespaces between attributes (no-multi-spaces)
2+
3+
The `--fix` option on the command line can automatically fix some of the problems reported by this rule.
4+
5+
This rule aims to remove multiple spaces in a row between attributes witch are not used for indentation.
6+
7+
## Rule Details
8+
9+
Examples of **incorrect** code for this rule:
10+
11+
```html
12+
<template>
13+
<div class="foo" :style="foo"
14+
:foo="bar" >
15+
</div>
16+
</template>
17+
```
18+
19+
Examples of **correct** code for this rule:
20+
21+
```html
22+
<template>
23+
<div class="foo"
24+
:style="foo">
25+
</div>
26+
</template>
27+
```
28+
29+
### Options
30+
31+
Nothing

lib/rules/no-multi-spaces.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @fileoverview This rule warns about the usage of extra whitespaces between attributes
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
11+
module.exports = {
12+
meta: {
13+
docs: {
14+
description: 'This rule warns about the usage of extra whitespaces between attributes',
15+
category: 'Stylistic Issues',
16+
recommended: false
17+
},
18+
fixable: 'whitespace', // or "code" or "whitespace"
19+
schema: []
20+
},
21+
22+
/**
23+
* @param {RuleContext} context - The rule context.
24+
* @returns {Object} AST event handlers.
25+
*/
26+
create (context) {
27+
function formatValue (token) {
28+
switch (token.type) {
29+
case 'HTMLSelfClosingTagClose': return '/>'
30+
case 'HTMLTagClose': return '>'
31+
}
32+
33+
return token.value
34+
}
35+
36+
// ----------------------------------------------------------------------
37+
// Public
38+
// ----------------------------------------------------------------------
39+
40+
return {
41+
Program (node) {
42+
if (context.parserServices.getTemplateBodyTokenStore == null) {
43+
context.report({
44+
loc: { line: 1, column: 0 },
45+
message: 'Use the latest vue-eslint-parser. See also https://github.com/vuejs/eslint-plugin-vue#what-is-the-use-the-latest-vue-eslint-parser-error.'
46+
})
47+
return
48+
}
49+
const tokenStore = context.parserServices.getTemplateBodyTokenStore()
50+
const tokens = tokenStore.getTokens(node.templateBody, { includeComments: true })
51+
52+
let prevToken = tokens.shift()
53+
for (const token of tokens) {
54+
const spaces = token.range[0] - prevToken.range[1]
55+
if (spaces > 1 && token.loc.start.line === prevToken.loc.start.line) {
56+
context.report({
57+
node: token,
58+
loc: {
59+
start: prevToken.loc.end,
60+
end: token.loc.start
61+
},
62+
message: "Multiple spaces found before '{{displayValue}}'.",
63+
fix: (fixer) => fixer.replaceTextRange([prevToken.range[1], token.range[0]], ' '),
64+
data: {
65+
displayValue: formatValue(token)
66+
}
67+
})
68+
}
69+
prevToken = token
70+
}
71+
}
72+
}
73+
}
74+
}

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.6"
48+
"vue-eslint-parser": "2.0.0-beta.7"
4949
},
5050
"devDependencies": {
5151
"@types/node": "^4.2.16",

tests/lib/rules/no-multi-spaces.js

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
* @fileoverview This rule warns about the usage of extra whitespaces between attributes
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/no-multi-spaces')
12+
const RuleTester = require('eslint').RuleTester
13+
14+
// ------------------------------------------------------------------------------
15+
// Tests
16+
// ------------------------------------------------------------------------------
17+
18+
const ruleTester = new RuleTester({
19+
parser: 'vue-eslint-parser',
20+
parserOptions: { ecmaVersion: 2015 }
21+
})
22+
23+
ruleTester.run('no-multi-spaces', rule, {
24+
valid: [
25+
'',
26+
'<template></template>',
27+
'<template><div /></template>',
28+
'<template><div class="foo"></div></template>',
29+
'<template><div class=" foo " style=" foo "></div></template>',
30+
'<template><div class="foo" @click="bar"></div></template>',
31+
'<template><div class="foo"\n :style="foo"></div></template>',
32+
'<template><div class="foo"\n\t\t\t:style="foo"></div></template>',
33+
'<template><div class="foo"\n :style="foo"\n ></div></template>',
34+
'<template><div class="foo"\n :style="foo" /></template>',
35+
'<template><div class="foo"\n :style="foo"\n /></template>',
36+
'<template><div>{{ test }}</div></template>',
37+
'<template><div>{{test}}</div></template>',
38+
'<template><div>{{test}}<!-- fooo --></div></template>',
39+
'<template><div>{{test}} <!-- fooo --></div></template>',
40+
'<template><div v-for="i in b">{{ i }}</div></template>',
41+
'<template><div v-for=" i in b ">{{ i }}</div></template>',
42+
'<template><div :test="` `"> {{ a }} </div></template>',
43+
'<template><div :test="` `"> \n {{ a }} </div></template>'
44+
],
45+
invalid: [
46+
{
47+
code: '<template><div /></template>',
48+
output: '<template><div /></template>',
49+
errors: [{
50+
message: "Multiple spaces found before '/>'.",
51+
type: 'HTMLSelfClosingTagClose'
52+
}]
53+
},
54+
{
55+
code: '<template><div class="foo" /></template>',
56+
output: '<template><div class="foo" /></template>',
57+
errors: [
58+
{
59+
message: "Multiple spaces found before 'class'.",
60+
type: 'HTMLIdentifier'
61+
},
62+
{
63+
message: "Multiple spaces found before '/>'.",
64+
type: 'HTMLSelfClosingTagClose'
65+
}
66+
]
67+
},
68+
{
69+
code: '<template><div\t\tclass="foo"\t\t/></template>',
70+
output: '<template><div class="foo" /></template>',
71+
errors: [
72+
{
73+
message: "Multiple spaces found before 'class'.",
74+
type: 'HTMLIdentifier'
75+
},
76+
{
77+
message: "Multiple spaces found before '/>'.",
78+
type: 'HTMLSelfClosingTagClose'
79+
}
80+
]
81+
},
82+
{
83+
code: '<template><div :class="foo" /></template>',
84+
output: '<template><div :class="foo" /></template>',
85+
errors: [
86+
{
87+
message: "Multiple spaces found before ':class'.",
88+
type: 'HTMLIdentifier'
89+
},
90+
{
91+
message: "Multiple spaces found before '/>'.",
92+
type: 'HTMLSelfClosingTagClose'
93+
}
94+
]
95+
},
96+
{
97+
code: '<template><div :foo="" class="foo" /></template>',
98+
output: '<template><div :foo="" class="foo" /></template>',
99+
errors: [{
100+
message: "Multiple spaces found before '/>'.",
101+
type: 'HTMLSelfClosingTagClose'
102+
}]
103+
},
104+
{
105+
code: '<template><div foo="" class="foo" /></template>',
106+
output: '<template><div foo="" class="foo" /></template>',
107+
errors: [{
108+
message: "Multiple spaces found before '/>'.",
109+
type: 'HTMLSelfClosingTagClose'
110+
}]
111+
},
112+
{
113+
code: '<template><foo v-foo="" class="foo" /></template>',
114+
output: '<template><foo v-foo="" class="foo" /></template>',
115+
errors: [{
116+
message: "Multiple spaces found before '/>'.",
117+
type: 'HTMLSelfClosingTagClose'
118+
}]
119+
},
120+
{
121+
code: '<template><foo v-foo="" \n class="foo" /></template>',
122+
output: '<template><foo v-foo="" \n class="foo" /></template>',
123+
errors: [
124+
{
125+
message: "Multiple spaces found before '/>'.",
126+
type: 'HTMLSelfClosingTagClose'
127+
}
128+
]
129+
},
130+
{
131+
code: '<template><div>{{ test }}</div></template>',
132+
output: '<template><div>{{ test }}</div></template>',
133+
errors: [
134+
{
135+
message: "Multiple spaces found before 'test'.",
136+
type: 'Identifier'
137+
},
138+
{
139+
message: "Multiple spaces found before '}}'.",
140+
type: 'VExpressionEnd'
141+
}
142+
]
143+
},
144+
{
145+
code: '<template><div ></div></template>',
146+
output: '<template><div ></div></template>',
147+
errors: [
148+
{
149+
message: "Multiple spaces found before '>'.",
150+
type: 'HTMLTagClose'
151+
}
152+
]
153+
},
154+
{
155+
code: '<template><div v-for=" i in b ">{{ test }}</div></template>',
156+
output: '<template><div v-for=" i in b ">{{ test }}</div></template>',
157+
errors: [
158+
{
159+
message: "Multiple spaces found before 'i'.",
160+
type: 'Identifier'
161+
},
162+
{
163+
message: "Multiple spaces found before 'in'.",
164+
type: 'Keyword'
165+
},
166+
{
167+
message: "Multiple spaces found before 'b'.",
168+
type: 'Identifier'
169+
},
170+
{
171+
message: "Multiple spaces found before '\"'.",
172+
type: 'Punctuator'
173+
}
174+
]
175+
}
176+
]
177+
})

0 commit comments

Comments
 (0)