Skip to content

Commit 1402031

Browse files
evilebottnawishellscape
authored andcommitted
feat: unicode-range node (#46)
1 parent 98d9e53 commit 1402031

File tree

6 files changed

+146
-2
lines changed

6 files changed

+146
-2
lines changed

API.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ not documented here are subject to change at any point.*
1717
* [`parser.string([props])`](#parserstringprops)
1818
* [`parser.value([props])`](#parservalueprops)
1919
* [`parser.word([props])`](#parserwordprops)
20+
* [`parser.unicodeRange([props])`](#parserunicoderangeprops)
2021
- [Node types](#node-types)
2122
* [`node.type`](#nodetype)
2223
* [`node.parent`](#nodeparent)
@@ -214,13 +215,26 @@ not documented here are subject to change at any point.*
214215

215216
* `props (object)`: The new node's properties.
216217

218+
### `parser.unicodeRange([props])`
219+
220+
Creates a new unicode range Node.
221+
222+
```js
223+
parser.unicodeRange({ value: 'U+26' });
224+
// → U+26
225+
```
226+
227+
Arguments:
228+
229+
* `props (object)`: The new node's properties.
230+
217231
## Node types
218232

219233
### `node.type`
220234

221235
A string representation of the node type. It can be one of the following;
222236
`atword`, `colon`, `comma`, `comment`, `func`, `number`, `operator`,
223-
`paren`, `string`, `value`, `word`.
237+
`paren`, `string`, `unicoderange`, `value`, `word`.
224238

225239
```js
226240
parser.word({ value: '#fff' }).type;
@@ -493,9 +507,9 @@ methods are:
493507
* `container.walkOperators`
494508
* `container.walkParenthesis`
495509
* `container.walkStringNodes`
510+
* `container.walkUnicodeRanges`
496511
* `container.walkWords`
497512

498-
499513
### `container.prepend(node)` & `container.append(node)`
500514

501515
Add a node to the start/end of the container. Note that doing so will set

lib/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const Num = require('./number');
1010
const Operator = require('./operator');
1111
const Paren = require('./paren');
1212
const Str = require('./string');
13+
const UnicodeRange = require('./unicode-range');
1314
const Value = require('./value');
1415
const Word = require('./word');
1516

@@ -65,4 +66,8 @@ parser.word = function (opts) {
6566
return new Word(opts);
6667
};
6768

69+
parser.unicodeRange = function (opts) {
70+
return new UnicodeRange(opts);
71+
};
72+
6873
module.exports = parser;

lib/parser.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const Operator = require('./operator');
1313
const Paren = require('./paren');
1414
const Str = require('./string');
1515
const Word = require('./word');
16+
const UnicodeRange = require('./unicode-range');
1617

1718
const tokenize = require('./tokenize');
1819

@@ -236,6 +237,9 @@ module.exports = class Parser {
236237
case 'string':
237238
this.string();
238239
break;
240+
case 'unicoderange':
241+
this.unicodeRange();
242+
break;
239243
default:
240244
this.word();
241245
break;
@@ -379,6 +383,27 @@ module.exports = class Parser {
379383
}
380384
}
381385

386+
unicodeRange () {
387+
let token = this.currToken;
388+
389+
this.newNode(new UnicodeRange({
390+
value: token[1],
391+
source: {
392+
start: {
393+
line: token[2],
394+
column: token[3]
395+
},
396+
end: {
397+
line: token[4],
398+
column: token[5]
399+
}
400+
},
401+
sourceIndex: token[6]
402+
}));
403+
404+
this.position ++;
405+
}
406+
382407
splitWord () {
383408
let nextToken = this.nextToken,
384409
word = this.currToken[1],

lib/tokenize.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ const lowerE = 'e'.charCodeAt(0);
2525
const upperE = 'E'.charCodeAt(0);
2626
const digit0 = '0'.charCodeAt(0);
2727
const digit9 = '9'.charCodeAt(0);
28+
const lowerU = 'u'.charCodeAt(0);
29+
const upperU = 'U'.charCodeAt(0);
2830
const atEnd = /[ \n\t\r\{\(\)'"\\;,/]/g;
2931
const wordEnd = /[ \n\t\r\(\)\{\}\*:;@!&'"\+\|~>,\[\]\\]|\/(?=\*)/g;
3032
const wordEndNum = /[ \n\t\r\(\)\{\}\*:;@!&'"\-\+\|~>,\[\]\\]|\//g;
3133
const alphaNum = /^[a-z0-9]/i;
34+
const unicodeRange = /^[a-f0-9?\-]/i;
3235

3336
const util = require('util');
3437
const TokenizeError = require('./errors/TokenizeError');
@@ -287,6 +290,21 @@ module.exports = function tokenize (input, options) {
287290

288291
pos = next - 1;
289292
}
293+
else if ((code === lowerU || code === upperU) && css.charCodeAt(pos + 1) === plus) {
294+
next = pos + 2;
295+
296+
do {
297+
next += 1;
298+
code = css.charCodeAt(next);
299+
} while (next < length && unicodeRange.test(css.slice(next, next + 1)));
300+
301+
tokens.push(['unicoderange', css.slice(pos, next),
302+
line, pos - offset,
303+
line, next - offset,
304+
pos
305+
]);
306+
pos = next - 1;
307+
}
290308
// catch a regular slash, that isn't a comment
291309
else if (code === slash) {
292310
next = pos + 1;

lib/unicode-range.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
const Container = require('./container');
4+
const Node = require('./node');
5+
6+
class UnicodeRange extends Node {
7+
constructor (opts) {
8+
super(opts);
9+
this.type = 'unicode-range';
10+
}
11+
}
12+
13+
Container.registerWalker(UnicodeRange);
14+
15+
module.exports = UnicodeRange;

test/unicode-range.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
const chai = require('chai');
4+
const shallowDeepEqual = require('chai-shallow-deep-equal');
5+
const Parser = require('../lib/parser');
6+
7+
let expect = chai.expect;
8+
9+
describe('Parser → Unicode range', () => {
10+
11+
chai.use(shallowDeepEqual);
12+
13+
let fixtures = [
14+
{
15+
it: 'should parse single codepoint',
16+
test: 'U+26',
17+
expected: [
18+
{ type: 'unicode-range', value: 'U+26' },
19+
]
20+
},
21+
{
22+
it: 'should parse other single codepoint',
23+
test: 'U+0-7F',
24+
expected: [
25+
{ type: 'unicode-range', value: 'U+0-7F' },
26+
]
27+
},
28+
{
29+
it: 'should parse codepoint range',
30+
test: 'U+0025-00FF',
31+
expected: [
32+
{ type: 'unicode-range', value: 'U+0025-00FF' },
33+
]
34+
},
35+
{
36+
it: 'should parse wildcard range',
37+
test: 'U+4??',
38+
expected: [
39+
{ type: 'unicode-range', value: 'U+4??' },
40+
]
41+
},
42+
{
43+
it: 'should parse multiple values',
44+
test: 'U+0025-00FF, U+4??',
45+
expected: [
46+
{ type: 'unicode-range', value: 'U+0025-00FF' },
47+
{ type: 'comma', value: ',', raws: { before: '', after: '' } },
48+
{ type: 'unicode-range', value: 'U+4??', raws: { before: ' ', after: '' } },
49+
]
50+
}
51+
];
52+
53+
fixtures.forEach((fixture) => {
54+
it(fixture.it, () => {
55+
let ast = new Parser(fixture.test).parse();
56+
57+
ast.first.walk((node, index) => {
58+
let expected = fixture.expected[index];
59+
60+
if (expected) {
61+
expect(node).to.shallowDeepEqual(expected);
62+
}
63+
});
64+
});
65+
});
66+
67+
});

0 commit comments

Comments
 (0)