Skip to content

Commit 2a6c900

Browse files
committed
Always semicolon: support scss and less
1 parent e7421ff commit 2a6c900

46 files changed

Lines changed: 406 additions & 17 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ CLI mode:
196196
197197
### always-semicolon
198198
199+
Whether to add a semicolon after the last value/mixin.
200+
199201
Available value: `{Boolean}` `true`
200202
201203
Example: `{ "always-semicolon": true }`
@@ -208,6 +210,45 @@ a { color: red }
208210
a { color: red; }
209211
```
210212
213+
Example: `{ "always-semicolon": true }` (scss file):
214+
215+
```scss
216+
// before
217+
div {
218+
color: tomato;
219+
@include nani
220+
}
221+
222+
// after
223+
div {
224+
color: tomato;
225+
@include nani;
226+
}
227+
```
228+
229+
Note that in `*.scss` and `*.less` files semicolons are not added after `}`
230+
even if it's part of a value:
231+
232+
```scss
233+
// before
234+
div {
235+
color: tomato;
236+
font: {
237+
family: fantasy;
238+
size: 16px
239+
}
240+
}
241+
242+
// after
243+
div {
244+
color: tomato;
245+
font: {
246+
family: fantasy;
247+
size: 16px;
248+
}
249+
}
250+
```
251+
211252
### block-indent
212253
213254
**Note**: better to use with [rule-indent](#rule-indent)

lib/options/always-semicolon.js

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = {
55
*
66
* @param {String|Boolean} value Option value
77
* @returns {Object|undefined}
8+
* TODO: This option accepts only boolean
89
*/
910
setValue: function(value) {
1011
this._value = value === true;
@@ -20,31 +21,53 @@ module.exports = {
2021
process: function(nodeType, node) {
2122
if (nodeType === 'block') {
2223
for (var i = node.length; i--;) {
23-
var nodeItem = node[i];
24-
var type = nodeItem[0];
24+
var currentNode = node[i];
25+
var currentNodeType = currentNode[0];
26+
var nodeWithoutSemicolon;
2527

26-
if (type === 'declDelim') break;
28+
// Skip nodes that already have `;` at the end:
29+
if (currentNodeType === 'declDelim') break;
2730

28-
if (type === 'declaration') {
29-
// Look for value node:
30-
var value;
31-
for (var k = nodeItem.length; k--;) {
32-
if (nodeItem[k][0] === 'value') {
33-
value = nodeItem[k];
31+
// Add semicolon only after declarations and includes.
32+
// If current node is include, insert semicolon right into it.
33+
// If it's declaration, look for value node:
34+
if (currentNodeType === 'include') {
35+
nodeWithoutSemicolon = currentNode;
36+
} else if (currentNodeType === 'declaration') {
37+
for (var k = currentNode.length; k--;) {
38+
if (currentNode[k][0] === 'value') {
39+
nodeWithoutSemicolon = currentNode[k];
3440
break;
3541
}
3642
}
43+
} else {
44+
continue;
45+
}
3746

38-
var space = [];
39-
for (var j = value.length; j--;) {
40-
if (['s', 'commentML', 'commentSL'].indexOf(value[j][0]) === -1) break;
41-
space.unshift(value.splice(j)[0]);
42-
}
43-
node.splice.apply(node, [i + 1, 0, ['declDelim']].concat(space));
44-
break;
47+
var space = [];
48+
var isBlock = false;
49+
50+
// Check if there are spaces and comments at the end of the node:
51+
for (var j = nodeWithoutSemicolon.length; j--;) {
52+
var lastNode = nodeWithoutSemicolon[j][0];
53+
// If the node's last child is block, do not add semicolon:
54+
// TODO: Add syntax check and run the code only for scss
55+
if (lastNode === 'block') {
56+
isBlock = true;
57+
break;
58+
} else if (['s', 'commentML', 'commentSL'].indexOf(lastNode) === -1) break;
59+
60+
space.unshift(nodeWithoutSemicolon[j]);
4561
}
62+
63+
if (isBlock) break;
64+
65+
// Temporarily remove last spaces and comments and insert `;`
66+
// before them:
67+
nodeWithoutSemicolon.splice(nodeWithoutSemicolon.length - space.length);
68+
node.splice.apply(node, [i + 1, 0, ['declDelim']].concat(space));
69+
break;
4670
}
4771
}
4872
}
49-
5073
};

test/always-semicolon-less.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
var Comb = require('../lib/csscomb');
2+
var assert = require('assert');
3+
var fs = require('fs');
4+
5+
describe('options/always-semicolon (scss)', function() {
6+
var comb;
7+
var input;
8+
var expected;
9+
10+
function readFile(path) {
11+
return fs.readFileSync('test/always-semicolon-less/' + path, 'utf8');
12+
}
13+
14+
beforeEach(function() {
15+
comb = new Comb();
16+
comb.configure({ 'always-semicolon': true });
17+
});
18+
19+
it('Should not add semicolon to condition (single-line style)', function() {
20+
input = readFile('condition.less');
21+
22+
assert.equal(comb.processString(input, 'less'), input);
23+
});
24+
25+
it('Should not add semicolon to condition (multi-line style)', function() {
26+
input = readFile('condition-multiline.less');
27+
28+
assert.equal(comb.processString(input, 'less'), input);
29+
});
30+
31+
it('Should add semicolon to last included mixin if missing. Test 1 (single-line style)', function() {
32+
input = readFile('include-1.less');
33+
expected = readFile('include-1.expected.less');
34+
35+
assert.equal(comb.processString(input, 'less'), expected);
36+
});
37+
38+
it('Should add semicolon to last included mixin if missing. Test 1 (multi-line style)', function() {
39+
input = readFile('include-1-multiline.less');
40+
expected = readFile('include-1-multiline.expected.less');
41+
42+
assert.equal(comb.processString(input, 'less'), expected);
43+
});
44+
45+
it('Should add semicolon to last included mixin if missing. Test 2 (single-line style)', function() {
46+
input = readFile('include-2.less');
47+
expected = readFile('include-2.expected.less');
48+
49+
assert.equal(comb.processString(input, 'less'), expected);
50+
});
51+
52+
it('Should add semicolon to last included mixin if missing. Test 2 (multi-line style)', function() {
53+
input = readFile('include-2-multiline.less');
54+
expected = readFile('include-2-multiline.expected.less');
55+
56+
assert.equal(comb.processString(input, 'less'), expected);
57+
});
58+
59+
it('Should add semicolon to last included mixin if missing. Test 3 (single-line style)', function() {
60+
input = readFile('include-3.less');
61+
expected = readFile('include-3.expected.less');
62+
63+
assert.equal(comb.processString(input, 'less'), expected);
64+
});
65+
66+
it('Should add semicolon to last included mixin if missing. Test 3 (multi-line style)', function() {
67+
input = readFile('include-3-multiline.less');
68+
expected = readFile('include-3-multiline.expected.less');
69+
70+
assert.equal(comb.processString(input, 'less'), expected);
71+
});
72+
73+
it('Should add semicolon to last included mixin if missing. Test 4 (single-line style)', function() {
74+
input = readFile('include-4.less');
75+
expected = readFile('include-4.expected.less');
76+
77+
assert.equal(comb.processString(input, 'less'), expected);
78+
});
79+
80+
it('Should add semicolon to last included mixin if missing. Test 4 (multi-line style)', function() {
81+
input = readFile('include-4-multiline.less');
82+
expected = readFile('include-4-multiline.expected.less');
83+
84+
assert.equal(comb.processString(input, 'less'), expected);
85+
});
86+
87+
it('Should add semicolon to last included mixin if missing. Test 5 (single-line style)', function() {
88+
input = readFile('include-5.less');
89+
expected = readFile('include-5.expected.less');
90+
91+
assert.equal(comb.processString(input, 'less'), expected);
92+
});
93+
94+
it('Should add semicolon to last included mixin if missing. Test 5 (multi-line style)', function() {
95+
input = readFile('include-5-multiline.less');
96+
expected = readFile('include-5-multiline.expected.less');
97+
98+
assert.equal(comb.processString(input, 'less'), expected);
99+
});
100+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
div {
2+
@color: tomato;
3+
when (@color = tomato) {
4+
top: 0;
5+
}
6+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
div { @color: tomato; when (@color = tomato) { top: 0; } }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
div {
2+
color: tomato;
3+
.nani;
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
div {
2+
color: tomato;
3+
.nani
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
div { color: tomato; .nani; }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
div { color: tomato; .nani }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
div {
2+
color: tomato;
3+
.nani(2px);
4+
}

0 commit comments

Comments
 (0)