Skip to content

Commit 4c97f17

Browse files
armano2michalsnik
authored andcommitted
Add mustache-curly-spacing rule. (vuejs#153)
* Add `mustache-curly-spacing` rule. fixes vuejs#150 * Rename mustache-curly-spacing to mustache-interpolation-spacing
1 parent 541c238 commit 4c97f17

File tree

3 files changed

+326
-0
lines changed

3 files changed

+326
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# enforce unified spacing in mustache interpolations. (mustache-interpolation-spacing)
2+
3+
- :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.
4+
5+
## :book: Rule Details
6+
7+
This rule aims to enforce unified spacing in mustache interpolations.
8+
9+
:-1: Examples of **incorrect** code for this rule:
10+
11+
```html
12+
<template>
13+
<div>{{ text }}</div>
14+
</template>
15+
```
16+
17+
:+1: Examples of **correct** code for this rule:
18+
19+
```html
20+
<template>
21+
<div>{{ text }}</div>
22+
</template>
23+
```
24+
25+
## :wrench: Options
26+
27+
Default spacing is set to `always`
28+
29+
```
30+
'vue/mustache-interpolation-spacing': [2, 'always'|'never']
31+
```
32+
33+
### `"always"` - Expect one space between expression and curly brackets.
34+
35+
:+1: Examples of **correct** code`:
36+
37+
```html
38+
<template>
39+
<div>{{ text }}</div>
40+
</template>
41+
```
42+
43+
:-1: Examples of **incorrect** code`:
44+
45+
```html
46+
<template>
47+
<div>{{text}}</div>
48+
</template>
49+
```
50+
51+
### `"never"` - Expect no spaces between expression and curly brackets.
52+
53+
:+1: Examples of **correct** code`:
54+
55+
```html
56+
<template>
57+
<div>{{text}}</div>
58+
</template>
59+
```
60+
61+
:-1: Examples of **incorrect** code`:
62+
63+
```html
64+
<template>
65+
<div>{{ text }}</div>
66+
</template>
67+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* @fileoverview enforce unified spacing in mustache interpolations.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
// ------------------------------------------------------------------------------
14+
// Rule Definition
15+
// ------------------------------------------------------------------------------
16+
17+
module.exports = {
18+
meta: {
19+
docs: {
20+
description: 'enforce unified spacing in mustache interpolations.',
21+
category: 'Stylistic Issues',
22+
recommended: false
23+
},
24+
fixable: 'whitespace',
25+
schema: [
26+
{
27+
enum: ['always', 'never']
28+
}
29+
]
30+
},
31+
32+
create (context) {
33+
const options = context.options[0]
34+
const optSpaces = options !== 'never'
35+
const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
36+
37+
// ----------------------------------------------------------------------
38+
// Helpers
39+
// ----------------------------------------------------------------------
40+
41+
function checkTokens (leftToken, rightToken) {
42+
if (leftToken.loc.end.line === rightToken.loc.start.line) {
43+
const spaces = rightToken.loc.start.column - leftToken.loc.end.column
44+
const noSpacesFound = spaces === 0
45+
46+
if (optSpaces === noSpacesFound) {
47+
context.report({
48+
node: rightToken,
49+
loc: {
50+
start: leftToken.loc.end,
51+
end: rightToken.loc.start
52+
},
53+
message: 'Found {{spaces}} whitespaces, {{type}} expected.',
54+
data: {
55+
spaces: spaces === 0 ? 'none' : spaces,
56+
type: optSpaces ? '1' : 'none'
57+
},
58+
fix: (fixer) => fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], optSpaces ? ' ' : '')
59+
})
60+
}
61+
}
62+
}
63+
64+
// ----------------------------------------------------------------------
65+
// Public
66+
// ----------------------------------------------------------------------
67+
68+
utils.registerTemplateBodyVisitor(context, {
69+
'VExpressionContainer[expression!=null]' (node) {
70+
const tokens = template.getTokens(node, {
71+
includeComments: true,
72+
filter: token => token.type !== 'HTMLWhitespace' // When there is only whitespace between ignore it
73+
})
74+
75+
const startToken = tokens.shift()
76+
if (!startToken || startToken.type !== 'VExpressionStart') return
77+
const endToken = tokens.pop()
78+
if (!endToken || endToken.type !== 'VExpressionEnd') return
79+
80+
if (tokens.length > 0) {
81+
checkTokens(startToken, tokens[0])
82+
checkTokens(tokens[tokens.length - 1], endToken)
83+
} else {
84+
checkTokens(startToken, endToken)
85+
}
86+
}
87+
})
88+
89+
return { }
90+
}
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* @fileoverview enforce unified spacing in mustache interpolations.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/mustache-interpolation-spacing')
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('mustache-interpolation-spacing', rule, {
24+
25+
valid: [
26+
{
27+
filename: 'test.vue',
28+
code: '<template></template>'
29+
},
30+
{
31+
filename: 'test.vue',
32+
code: '<template><div></div></template>'
33+
},
34+
{
35+
filename: 'test.vue',
36+
code: '<template> <div id=" "></div> </template>'
37+
},
38+
{
39+
filename: 'test.vue',
40+
code: '<template> <div :style=" " :class=" foo " v-if=foo ></div> </template>'
41+
},
42+
{
43+
filename: 'test.vue',
44+
code: '<template><div>{{ text }}</div></template>'
45+
},
46+
{
47+
filename: 'test.vue',
48+
code: '<template><div>{{ }}</div></template>'
49+
},
50+
{
51+
filename: 'test.vue',
52+
code: '<template><div>{{ }}</div></template>',
53+
options: ['always']
54+
},
55+
{
56+
filename: 'test.vue',
57+
code: '<template><div>{{}}</div></template>',
58+
options: ['never']
59+
},
60+
{
61+
filename: 'test.vue',
62+
code: '<template><div>{{text}}</div></template>',
63+
options: ['never']
64+
},
65+
{
66+
filename: 'test.vue',
67+
code: '<template><div>{{ text }}</div></template>',
68+
options: ['always']
69+
},
70+
{
71+
filename: 'test.vue',
72+
code: '<template><div>{{ }}</div></template>',
73+
options: ['always']
74+
},
75+
{
76+
filename: 'test.vue',
77+
code: '<template><div>{{ }}</div></template>',
78+
options: ['never']
79+
},
80+
{
81+
filename: 'test.vue',
82+
code: '<template><div>{{ text }}</div></template>',
83+
options: ['always']
84+
}
85+
],
86+
87+
invalid: [
88+
{
89+
filename: 'test.vue',
90+
code: '<template><div>{{ text}}</div></template>',
91+
output: '<template><div>{{ text }}</div></template>',
92+
options: ['always'],
93+
errors: [{
94+
message: 'Found none whitespaces, 1 expected.',
95+
type: 'VExpressionEnd'
96+
}]
97+
},
98+
{
99+
filename: 'test.vue',
100+
code: '<template><div>{{text }}</div></template>',
101+
output: '<template><div>{{ text }}</div></template>',
102+
options: ['always'],
103+
errors: [{
104+
message: 'Found none whitespaces, 1 expected.',
105+
type: 'Identifier'
106+
}]
107+
},
108+
{
109+
filename: 'test.vue',
110+
code: '<template><div>{{ text}}</div></template>',
111+
output: '<template><div>{{text}}</div></template>',
112+
options: ['never'],
113+
errors: [{
114+
message: 'Found 1 whitespaces, none expected.',
115+
type: 'Identifier'
116+
}]
117+
},
118+
{
119+
filename: 'test.vue',
120+
code: '<template><div>{{text }}</div></template>',
121+
output: '<template><div>{{text}}</div></template>',
122+
options: ['never'],
123+
errors: [{
124+
message: 'Found 1 whitespaces, none expected.',
125+
type: 'VExpressionEnd'
126+
}]
127+
},
128+
{
129+
filename: 'test.vue',
130+
code: '<template><div>{{text}}</div></template>',
131+
output: '<template><div>{{ text }}</div></template>',
132+
options: ['always'],
133+
errors: [{
134+
message: 'Found none whitespaces, 1 expected.',
135+
type: 'Identifier'
136+
}, {
137+
message: 'Found none whitespaces, 1 expected.',
138+
type: 'VExpressionEnd'
139+
}]
140+
},
141+
{
142+
filename: 'test.vue',
143+
code: '<template><div>{{ text }}</div></template>',
144+
output: '<template><div>{{text}}</div></template>',
145+
options: ['never'],
146+
errors: [{
147+
message: 'Found 1 whitespaces, none expected.',
148+
type: 'Identifier'
149+
}, {
150+
message: 'Found 1 whitespaces, none expected.',
151+
type: 'VExpressionEnd'
152+
}]
153+
},
154+
{
155+
filename: 'test.vue',
156+
code: '<template><div>{{ text }}</div></template>',
157+
output: '<template><div>{{text}}</div></template>',
158+
options: ['never'],
159+
errors: [{
160+
message: 'Found 3 whitespaces, none expected.',
161+
type: 'Identifier'
162+
}, {
163+
message: 'Found 3 whitespaces, none expected.',
164+
type: 'VExpressionEnd'
165+
}]
166+
}
167+
]
168+
})

0 commit comments

Comments
 (0)