Skip to content

Commit 5e35136

Browse files
committed
fixes #23; handle scss interpolation group, word boundary
1 parent 48118d5 commit 5e35136

File tree

6 files changed

+66
-12
lines changed

6 files changed

+66
-12
lines changed

lib/container.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ class Container extends Node {
171171
}
172172

173173
toString () {
174-
// console.log('container >', 'toString:', this.nodes.length, this.type, this.value);
175174
let result = this.nodes.map(String).join('');
176175

177176
if (this.value) {

lib/node.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ module.exports = class Node {
4949
}
5050

5151
toString () {
52-
// console.log('node >', 'toString:', this.value);
53-
5452
return [
5553
this.raws.before,
5654
String(this.value),

lib/parser.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -383,15 +383,23 @@ module.exports = class Parser {
383383
let nextToken = this.nextToken,
384384
word = this.currToken[1],
385385
rNumber = /^[\+\-]?((\d+(\.\d*)?)|(\.\d+))/,
386+
387+
// treat css-like groupings differently so they can be inspected,
388+
// but don't address them as anything but a word, but allow hex values
389+
// to pass through.
390+
rNoFollow = /^(?!\#([a-z0-9]+))[\#\{\}]/gi,
391+
386392
hasAt, indices;
387393

388-
while (nextToken && nextToken[0] === 'word') {
389-
this.position ++;
394+
if (!rNoFollow.test(word)) {
395+
while (nextToken && nextToken[0] === 'word') {
396+
this.position ++;
390397

391-
let current = this.currToken[1];
392-
word += current;
398+
let current = this.currToken[1];
399+
word += current;
393400

394-
nextToken = this.nextToken;
401+
nextToken = this.nextToken;
402+
}
395403
}
396404

397405
hasAt = indexesOf(word, '@');
@@ -454,7 +462,7 @@ module.exports = class Parser {
454462
});
455463

456464
if (node.constructor.name === 'Word') {
457-
node.isHex = /^#/.test(value);
465+
node.isHex = /^#(.+)/.test(value);
458466
node.isColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(value);
459467
}
460468
else {

lib/tokenize.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const openBracket = '{'.charCodeAt(0);
4+
const closeBracket = '}'.charCodeAt(0);
35
const openParen = '('.charCodeAt(0);
46
const closeParen = ')'.charCodeAt(0);
57
const singleQuote = '\''.charCodeAt(0);
@@ -11,15 +13,17 @@ const colon = ':'.charCodeAt(0);
1113
const asterisk = '*'.charCodeAt(0);
1214
const minus = '-'.charCodeAt(0);
1315
const plus = '+'.charCodeAt(0);
16+
const pound = '#'.charCodeAt(0);
1417
const newline = '\n'.charCodeAt(0);
1518
const space = ' '.charCodeAt(0);
1619
const feed = '\f'.charCodeAt(0);
1720
const tab = '\t'.charCodeAt(0);
1821
const cr = '\r'.charCodeAt(0);
1922
const at = '@'.charCodeAt(0);
2023
const atEnd = /[ \n\t\r\{\(\)'"\\;,/]/g;
21-
const wordEnd = /[ \n\t\r\(\)\*:;@!&'"\+\|~>,\[\]\\]|\/(?=\*)/g;
22-
const wordEndNum = /[ \n\t\r\(\)\*:;@!&'"\-\+\|~>,\[\]\\]|\//g;
24+
const wordEnd = /[ \n\t\r\(\)\{\}\*:;@!&'"\+\|~>,\[\]\\]|\/(?=\*)/g;
25+
const wordEndNum = /[ \n\t\r\(\)\{\}\*:;@!&'"\-\+\|~>,\[\]\\]|\//g;
26+
const alphaNum = /^[a-z0-9]/i;
2327

2428
const util = require('util');
2529
const TokenizeError = require('./errors/TokenizeError');
@@ -107,6 +111,22 @@ module.exports = function tokenize (input, options) {
107111
pos = next - 1;
108112
break;
109113

114+
case openBracket:
115+
tokens.push(['{', '{',
116+
line, pos - offset,
117+
line, next - offset,
118+
pos
119+
]);
120+
break;
121+
122+
case closeBracket:
123+
tokens.push(['}', '}',
124+
line, pos - offset,
125+
line, next - offset,
126+
pos
127+
]);
128+
break;
129+
110130
case openParen:
111131
tokens.push(['(', '(',
112132
line, pos - offset,
@@ -251,6 +271,17 @@ module.exports = function tokenize (input, options) {
251271
pos = next;
252272

253273
}
274+
else if (code === pound && !alphaNum.test(css.slice(pos + 1, pos + 2))) {
275+
next = pos + 1;
276+
277+
tokens.push(['#', css.slice(pos, next),
278+
line, pos - offset,
279+
line, next - offset,
280+
pos
281+
]);
282+
283+
pos = next - 1;
284+
}
254285
// catch a regular slash, that isn't a comment
255286
else if (code === slash) {
256287
next = pos + 1;

test/function.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,23 @@ describe('Parser → Function', () => {
133133
{ type: 'paren', value: ')' }
134134
]
135135
},
136+
{
137+
it: 'should parse calc function with scss interpolation #23',
138+
test: 'calc(100% - #{$margin * 2px})',
139+
expected: [
140+
{ type: 'func', value: 'calc' },
141+
{ type: 'paren', value: '(' },
142+
{ type: 'number', value: '100', unit: '%' },
143+
{ type: 'operator', value: '-' },
144+
{ type: 'word', value: '#' },
145+
{ type: 'word', value: '{' },
146+
{ type: 'word', value: '$margin' },
147+
{ type: 'operator', value: '*' },
148+
{ type: 'number', value: '2', unit: 'px' },
149+
{ type: 'word', value: '}' },
150+
{ type: 'paren', value: ')' }
151+
]
152+
},
136153
{
137154
it: 'should parse nested functions',
138155
test: 'bar(baz(black, 10%), 10%)',

test/tokenize.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ describe('Tokenize', () => {
77

88
let fixtures = [
99
{ value: '#ffffff', expectedLength: 1 },
10+
{ value: '#{ffffff}', expectedLength: 4 },
1011
{ value: '#fff #000 #ccc #ddd', expectedLength: 7 },
1112
{ value: '( calc(( ) ))word', expectedLength: 11 },
1213
{ value: ' rgba( 34 , 45 , 54, .5 ) ', expectedLength: 19 },
1314
{ value: 'w1 w2 w6 \n f(4) ( ) () \t "s\'t" \'st\\"2\'', expectedLength: 21 },
14-
{ value: '#ffffff', expectedLength: 1 },
1515
{ value: 'Bond\\ 007', expectedLength: 4 },
1616
{ value: ' \\"word\\\'"\\ \\\t ', expectedLength: 7 },
1717
{ value: 'bar(baz(black, 10%), 10%)', expectedLength: 13 },
@@ -21,6 +21,7 @@ describe('Tokenize', () => {
2121
fixtures.forEach((fixture) => {
2222
it('should tokenize ' + fixture.value.replace(/\n/g, '\\n').replace(/\t/g, '\\t'), () => {
2323
let tokens = tokenize(fixture.value);
24+
2425
expect(tokens.length).to.equal(fixture.expectedLength);
2526
});
2627
});

0 commit comments

Comments
 (0)