@projectwallace/css-parser 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/arena.js +36 -24
- package/dist/css-node.d.ts +4 -15
- package/dist/css-node.js +6 -15
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/parse-anplusb.js +1 -1
- package/dist/parse-atrule-prelude.js +1 -1
- package/dist/parse-declaration.js +1 -1
- package/dist/parse-selector.js +1 -1
- package/dist/parse-value.js +1 -1
- package/dist/parse.js +1 -1
- package/dist/string-utils.d.ts +14 -0
- package/dist/string-utils.js +5 -1
- package/dist/tokenize.d.ts +11 -1
- package/dist/tokenize.js +429 -3
- package/dist/walk.d.ts +5 -52
- package/dist/walk.js +2 -2
- package/package.json +8 -2
- package/dist/lexer.d.ts +0 -11
- package/dist/lexer.js +0 -431
package/dist/tokenize.js
CHANGED
|
@@ -1,6 +1,432 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { TOKEN_EOF } from './token-types.js';
|
|
1
|
+
import { is_whitespace, is_newline, is_digit, is_ident_start, is_hex_digit, is_ident_char } from './char-types.js';
|
|
2
|
+
import { TOKEN_EOF, TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE, TOKEN_COLON, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_CDO, TOKEN_CDC, TOKEN_DELIM, TOKEN_WHITESPACE, TOKEN_COMMENT, TOKEN_STRING, TOKEN_BAD_STRING, TOKEN_PERCENTAGE, TOKEN_DIMENSION, TOKEN_NUMBER, TOKEN_FUNCTION, TOKEN_IDENT, TOKEN_AT_KEYWORD, TOKEN_HASH } from './token-types.js';
|
|
3
3
|
|
|
4
|
+
const CHAR_LEFT_BRACE = 123;
|
|
5
|
+
const CHAR_RIGHT_BRACE = 125;
|
|
6
|
+
const CHAR_COLON = 58;
|
|
7
|
+
const CHAR_SEMICOLON = 59;
|
|
8
|
+
const CHAR_COMMA = 44;
|
|
9
|
+
const CHAR_LEFT_BRACKET = 91;
|
|
10
|
+
const CHAR_RIGHT_BRACKET = 93;
|
|
11
|
+
const CHAR_LEFT_PAREN = 40;
|
|
12
|
+
const CHAR_RIGHT_PAREN = 41;
|
|
13
|
+
const CHAR_FORWARD_SLASH = 47;
|
|
14
|
+
const CHAR_ASTERISK = 42;
|
|
15
|
+
const CHAR_DOUBLE_QUOTE = 34;
|
|
16
|
+
const CHAR_SINGLE_QUOTE = 39;
|
|
17
|
+
const CHAR_DOT = 46;
|
|
18
|
+
const CHAR_LESS_THAN = 60;
|
|
19
|
+
const CHAR_EXCLAMATION = 33;
|
|
20
|
+
const CHAR_HYPHEN = 45;
|
|
21
|
+
const CHAR_GREATER_THAN = 62;
|
|
22
|
+
const CHAR_AT_SIGN = 64;
|
|
23
|
+
const CHAR_HASH = 35;
|
|
24
|
+
const CHAR_BACKSLASH = 92;
|
|
25
|
+
const CHAR_PLUS = 43;
|
|
26
|
+
const CHAR_PERCENT = 37;
|
|
27
|
+
const CHAR_LOWERCASE_E = 101;
|
|
28
|
+
const CHAR_UPPERCASE_E = 69;
|
|
29
|
+
const CHAR_CARRIAGE_RETURN = 13;
|
|
30
|
+
const CHAR_LINE_FEED = 10;
|
|
31
|
+
class Lexer {
|
|
32
|
+
source;
|
|
33
|
+
pos;
|
|
34
|
+
line;
|
|
35
|
+
column;
|
|
36
|
+
skip_comments;
|
|
37
|
+
// Current token properties (avoiding object allocation)
|
|
38
|
+
token_type;
|
|
39
|
+
token_start;
|
|
40
|
+
token_end;
|
|
41
|
+
token_line;
|
|
42
|
+
token_column;
|
|
43
|
+
constructor(source, skip_comments = false) {
|
|
44
|
+
this.source = source;
|
|
45
|
+
this.pos = 0;
|
|
46
|
+
this.line = 1;
|
|
47
|
+
this.column = 1;
|
|
48
|
+
this.skip_comments = skip_comments;
|
|
49
|
+
this.token_type = TOKEN_EOF;
|
|
50
|
+
this.token_start = 0;
|
|
51
|
+
this.token_end = 0;
|
|
52
|
+
this.token_line = 1;
|
|
53
|
+
this.token_column = 1;
|
|
54
|
+
}
|
|
55
|
+
// Fast token advancing without object allocation (for internal parser use)
|
|
56
|
+
next_token_fast(skip_whitespace = false) {
|
|
57
|
+
if (skip_whitespace) {
|
|
58
|
+
while (this.pos < this.source.length) {
|
|
59
|
+
let ch2 = this.source.charCodeAt(this.pos);
|
|
60
|
+
if (!is_whitespace(ch2) && !is_newline(ch2)) break;
|
|
61
|
+
this.advance();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (this.pos >= this.source.length) {
|
|
65
|
+
return this.make_token(TOKEN_EOF, this.pos, this.pos);
|
|
66
|
+
}
|
|
67
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
68
|
+
let start = this.pos;
|
|
69
|
+
let start_line = this.line;
|
|
70
|
+
let start_column = this.column;
|
|
71
|
+
if (ch === CHAR_LEFT_BRACE) {
|
|
72
|
+
this.advance();
|
|
73
|
+
return this.make_token(TOKEN_LEFT_BRACE, start, this.pos, start_line, start_column);
|
|
74
|
+
}
|
|
75
|
+
if (ch === CHAR_RIGHT_BRACE) {
|
|
76
|
+
this.advance();
|
|
77
|
+
return this.make_token(TOKEN_RIGHT_BRACE, start, this.pos, start_line, start_column);
|
|
78
|
+
}
|
|
79
|
+
if (ch === CHAR_COLON) {
|
|
80
|
+
this.advance();
|
|
81
|
+
return this.make_token(TOKEN_COLON, start, this.pos, start_line, start_column);
|
|
82
|
+
}
|
|
83
|
+
if (ch === CHAR_SEMICOLON) {
|
|
84
|
+
this.advance();
|
|
85
|
+
return this.make_token(TOKEN_SEMICOLON, start, this.pos, start_line, start_column);
|
|
86
|
+
}
|
|
87
|
+
if (ch === CHAR_COMMA) {
|
|
88
|
+
this.advance();
|
|
89
|
+
return this.make_token(TOKEN_COMMA, start, this.pos, start_line, start_column);
|
|
90
|
+
}
|
|
91
|
+
if (ch === CHAR_LEFT_BRACKET) {
|
|
92
|
+
this.advance();
|
|
93
|
+
return this.make_token(TOKEN_LEFT_BRACKET, start, this.pos, start_line, start_column);
|
|
94
|
+
}
|
|
95
|
+
if (ch === CHAR_RIGHT_BRACKET) {
|
|
96
|
+
this.advance();
|
|
97
|
+
return this.make_token(TOKEN_RIGHT_BRACKET, start, this.pos, start_line, start_column);
|
|
98
|
+
}
|
|
99
|
+
if (ch === CHAR_LEFT_PAREN) {
|
|
100
|
+
this.advance();
|
|
101
|
+
return this.make_token(TOKEN_LEFT_PAREN, start, this.pos, start_line, start_column);
|
|
102
|
+
}
|
|
103
|
+
if (ch === CHAR_RIGHT_PAREN) {
|
|
104
|
+
this.advance();
|
|
105
|
+
return this.make_token(TOKEN_RIGHT_PAREN, start, this.pos, start_line, start_column);
|
|
106
|
+
}
|
|
107
|
+
if (is_whitespace(ch) || is_newline(ch)) {
|
|
108
|
+
return this.consume_whitespace(start_line, start_column);
|
|
109
|
+
}
|
|
110
|
+
if (ch === CHAR_FORWARD_SLASH && this.peek() === CHAR_ASTERISK) {
|
|
111
|
+
if (this.skip_comments) {
|
|
112
|
+
this.advance(2);
|
|
113
|
+
while (this.pos < this.source.length - 1) {
|
|
114
|
+
let ch2 = this.source.charCodeAt(this.pos);
|
|
115
|
+
if (ch2 === CHAR_ASTERISK && this.source.charCodeAt(this.pos + 1) === CHAR_FORWARD_SLASH) {
|
|
116
|
+
this.advance(2);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
this.advance();
|
|
120
|
+
}
|
|
121
|
+
return this.next_token_fast(skip_whitespace);
|
|
122
|
+
}
|
|
123
|
+
return this.consume_comment(start_line, start_column);
|
|
124
|
+
}
|
|
125
|
+
if (ch === CHAR_DOUBLE_QUOTE || ch === CHAR_SINGLE_QUOTE) {
|
|
126
|
+
return this.consume_string(ch, start_line, start_column);
|
|
127
|
+
}
|
|
128
|
+
if (is_digit(ch)) {
|
|
129
|
+
return this.consume_number(start_line, start_column);
|
|
130
|
+
}
|
|
131
|
+
if (ch === CHAR_DOT && is_digit(this.peek())) {
|
|
132
|
+
return this.consume_number(start_line, start_column);
|
|
133
|
+
}
|
|
134
|
+
if (ch === CHAR_LESS_THAN && this.pos + 3 < this.source.length) {
|
|
135
|
+
if (this.source.charCodeAt(this.pos + 1) === CHAR_EXCLAMATION && this.source.charCodeAt(this.pos + 2) === CHAR_HYPHEN && this.source.charCodeAt(this.pos + 3) === CHAR_HYPHEN) {
|
|
136
|
+
this.advance(4);
|
|
137
|
+
return this.make_token(TOKEN_CDO, start, this.pos, start_line, start_column);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (ch === CHAR_HYPHEN && this.pos + 2 < this.source.length) {
|
|
141
|
+
if (this.source.charCodeAt(this.pos + 1) === CHAR_HYPHEN && this.source.charCodeAt(this.pos + 2) === CHAR_GREATER_THAN) {
|
|
142
|
+
this.advance(3);
|
|
143
|
+
return this.make_token(TOKEN_CDC, start, this.pos, start_line, start_column);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (ch === CHAR_AT_SIGN) {
|
|
147
|
+
return this.consume_at_keyword(start_line, start_column);
|
|
148
|
+
}
|
|
149
|
+
if (ch === CHAR_HASH) {
|
|
150
|
+
return this.consume_hash(start_line, start_column);
|
|
151
|
+
}
|
|
152
|
+
if (is_ident_start(ch)) {
|
|
153
|
+
return this.consume_ident_or_function(start_line, start_column);
|
|
154
|
+
}
|
|
155
|
+
if (ch === CHAR_HYPHEN) {
|
|
156
|
+
let next = this.peek();
|
|
157
|
+
if (is_ident_start(next) || next === CHAR_HYPHEN) {
|
|
158
|
+
return this.consume_ident_or_function(start_line, start_column);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (ch === CHAR_BACKSLASH) {
|
|
162
|
+
let next = this.peek();
|
|
163
|
+
if (next !== 0 && !is_newline(next)) {
|
|
164
|
+
return this.consume_ident_or_function(start_line, start_column);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (ch === CHAR_HYPHEN || ch === CHAR_PLUS) {
|
|
168
|
+
let next = this.peek();
|
|
169
|
+
if (is_digit(next) || next === CHAR_DOT && is_digit(this.peek(2))) {
|
|
170
|
+
return this.consume_number(start_line, start_column);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
this.advance();
|
|
174
|
+
return this.make_token(TOKEN_DELIM, start, this.pos, start_line, start_column);
|
|
175
|
+
}
|
|
176
|
+
consume_whitespace(start_line, start_column) {
|
|
177
|
+
let start = this.pos;
|
|
178
|
+
while (this.pos < this.source.length) {
|
|
179
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
180
|
+
if (!is_whitespace(ch) && !is_newline(ch)) break;
|
|
181
|
+
this.advance();
|
|
182
|
+
}
|
|
183
|
+
return this.make_token(TOKEN_WHITESPACE, start, this.pos, start_line, start_column);
|
|
184
|
+
}
|
|
185
|
+
consume_comment(start_line, start_column) {
|
|
186
|
+
let start = this.pos;
|
|
187
|
+
this.advance(2);
|
|
188
|
+
while (this.pos < this.source.length - 1) {
|
|
189
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
190
|
+
if (ch === CHAR_ASTERISK && this.source.charCodeAt(this.pos + 1) === CHAR_FORWARD_SLASH) {
|
|
191
|
+
this.advance(2);
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
this.advance();
|
|
195
|
+
}
|
|
196
|
+
return this.make_token(TOKEN_COMMENT, start, this.pos, start_line, start_column);
|
|
197
|
+
}
|
|
198
|
+
consume_string(quote, start_line, start_column) {
|
|
199
|
+
let start = this.pos;
|
|
200
|
+
this.advance();
|
|
201
|
+
while (this.pos < this.source.length) {
|
|
202
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
203
|
+
if (ch === quote) {
|
|
204
|
+
this.advance();
|
|
205
|
+
return this.make_token(TOKEN_STRING, start, this.pos, start_line, start_column);
|
|
206
|
+
}
|
|
207
|
+
if (is_newline(ch)) {
|
|
208
|
+
return this.make_token(TOKEN_BAD_STRING, start, this.pos, start_line, start_column);
|
|
209
|
+
}
|
|
210
|
+
if (ch === CHAR_BACKSLASH) {
|
|
211
|
+
this.advance();
|
|
212
|
+
if (this.pos < this.source.length) {
|
|
213
|
+
let next = this.source.charCodeAt(this.pos);
|
|
214
|
+
if (is_hex_digit(next)) {
|
|
215
|
+
this.consume_hex_escape();
|
|
216
|
+
} else if (!is_newline(next)) {
|
|
217
|
+
this.advance();
|
|
218
|
+
} else {
|
|
219
|
+
this.advance();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
this.advance();
|
|
225
|
+
}
|
|
226
|
+
return this.make_token(TOKEN_BAD_STRING, start, this.pos, start_line, start_column);
|
|
227
|
+
}
|
|
228
|
+
consume_hex_escape() {
|
|
229
|
+
let count = 0;
|
|
230
|
+
while (count < 6 && this.pos < this.source.length) {
|
|
231
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
232
|
+
if (!is_hex_digit(ch)) break;
|
|
233
|
+
this.advance();
|
|
234
|
+
count++;
|
|
235
|
+
}
|
|
236
|
+
if (this.pos < this.source.length) {
|
|
237
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
238
|
+
if (is_whitespace(ch) || is_newline(ch)) {
|
|
239
|
+
this.advance();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
consume_number(start_line, start_column) {
|
|
244
|
+
let start = this.pos;
|
|
245
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
246
|
+
if (ch === CHAR_PLUS || ch === CHAR_HYPHEN) {
|
|
247
|
+
this.advance();
|
|
248
|
+
}
|
|
249
|
+
while (this.pos < this.source.length && is_digit(this.source.charCodeAt(this.pos))) {
|
|
250
|
+
this.advance();
|
|
251
|
+
}
|
|
252
|
+
if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_DOT && this.pos + 1 < this.source.length && is_digit(this.source.charCodeAt(this.pos + 1))) {
|
|
253
|
+
this.advance();
|
|
254
|
+
while (this.pos < this.source.length && is_digit(this.source.charCodeAt(this.pos))) {
|
|
255
|
+
this.advance();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (this.pos < this.source.length) {
|
|
259
|
+
let ch2 = this.source.charCodeAt(this.pos);
|
|
260
|
+
if (ch2 === CHAR_LOWERCASE_E || ch2 === CHAR_UPPERCASE_E) {
|
|
261
|
+
let next = this.peek();
|
|
262
|
+
if (is_digit(next) || (next === CHAR_PLUS || next === CHAR_HYPHEN) && is_digit(this.peek(2))) {
|
|
263
|
+
this.advance();
|
|
264
|
+
if (this.pos < this.source.length) {
|
|
265
|
+
let sign = this.source.charCodeAt(this.pos);
|
|
266
|
+
if (sign === CHAR_PLUS || sign === CHAR_HYPHEN) {
|
|
267
|
+
this.advance();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
while (this.pos < this.source.length && is_digit(this.source.charCodeAt(this.pos))) {
|
|
271
|
+
this.advance();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (this.pos < this.source.length) {
|
|
277
|
+
let ch2 = this.source.charCodeAt(this.pos);
|
|
278
|
+
if (ch2 === CHAR_PERCENT) {
|
|
279
|
+
this.advance();
|
|
280
|
+
return this.make_token(TOKEN_PERCENTAGE, start, this.pos, start_line, start_column);
|
|
281
|
+
}
|
|
282
|
+
if (is_ident_start(ch2) || ch2 === CHAR_HYPHEN && is_ident_start(this.peek())) {
|
|
283
|
+
while (this.pos < this.source.length && is_ident_char(this.source.charCodeAt(this.pos))) {
|
|
284
|
+
this.advance();
|
|
285
|
+
}
|
|
286
|
+
return this.make_token(TOKEN_DIMENSION, start, this.pos, start_line, start_column);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return this.make_token(TOKEN_NUMBER, start, this.pos, start_line, start_column);
|
|
290
|
+
}
|
|
291
|
+
consume_ident_or_function(start_line, start_column) {
|
|
292
|
+
let start = this.pos;
|
|
293
|
+
while (this.pos < this.source.length) {
|
|
294
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
295
|
+
if (ch === CHAR_BACKSLASH) {
|
|
296
|
+
if (this.pos + 1 >= this.source.length) break;
|
|
297
|
+
let next = this.source.charCodeAt(this.pos + 1);
|
|
298
|
+
if (is_newline(next)) break;
|
|
299
|
+
this.advance();
|
|
300
|
+
if (is_hex_digit(next)) {
|
|
301
|
+
this.advance();
|
|
302
|
+
for (let i = 0; i < 5 && this.pos < this.source.length; i++) {
|
|
303
|
+
if (!is_hex_digit(this.source.charCodeAt(this.pos))) break;
|
|
304
|
+
this.advance();
|
|
305
|
+
}
|
|
306
|
+
if (this.pos < this.source.length) {
|
|
307
|
+
let ws = this.source.charCodeAt(this.pos);
|
|
308
|
+
if (is_whitespace(ws) || is_newline(ws)) {
|
|
309
|
+
this.advance();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
this.advance();
|
|
314
|
+
}
|
|
315
|
+
} else if (is_ident_char(ch)) {
|
|
316
|
+
this.advance();
|
|
317
|
+
} else {
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_LEFT_PAREN) {
|
|
322
|
+
this.advance();
|
|
323
|
+
return this.make_token(TOKEN_FUNCTION, start, this.pos, start_line, start_column);
|
|
324
|
+
}
|
|
325
|
+
return this.make_token(TOKEN_IDENT, start, this.pos, start_line, start_column);
|
|
326
|
+
}
|
|
327
|
+
consume_at_keyword(start_line, start_column) {
|
|
328
|
+
let start = this.pos;
|
|
329
|
+
this.advance();
|
|
330
|
+
while (this.pos < this.source.length && is_ident_char(this.source.charCodeAt(this.pos))) {
|
|
331
|
+
this.advance();
|
|
332
|
+
}
|
|
333
|
+
return this.make_token(TOKEN_AT_KEYWORD, start, this.pos, start_line, start_column);
|
|
334
|
+
}
|
|
335
|
+
consume_hash(start_line, start_column) {
|
|
336
|
+
let start = this.pos;
|
|
337
|
+
this.advance();
|
|
338
|
+
while (this.pos < this.source.length && is_ident_char(this.source.charCodeAt(this.pos))) {
|
|
339
|
+
this.advance();
|
|
340
|
+
}
|
|
341
|
+
return this.make_token(TOKEN_HASH, start, this.pos, start_line, start_column);
|
|
342
|
+
}
|
|
343
|
+
advance(count = 1) {
|
|
344
|
+
if (count === 1) {
|
|
345
|
+
if (this.pos >= this.source.length) return;
|
|
346
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
347
|
+
this.pos++;
|
|
348
|
+
if (is_newline(ch)) {
|
|
349
|
+
if (ch === CHAR_CARRIAGE_RETURN && this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_LINE_FEED) {
|
|
350
|
+
this.pos++;
|
|
351
|
+
}
|
|
352
|
+
this.line++;
|
|
353
|
+
this.column = 1;
|
|
354
|
+
} else {
|
|
355
|
+
this.column++;
|
|
356
|
+
}
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
for (let i = 0; i < count; i++) {
|
|
360
|
+
if (this.pos >= this.source.length) break;
|
|
361
|
+
let ch = this.source.charCodeAt(this.pos);
|
|
362
|
+
this.pos++;
|
|
363
|
+
if (is_newline(ch)) {
|
|
364
|
+
if (ch === CHAR_CARRIAGE_RETURN && this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_LINE_FEED) {
|
|
365
|
+
this.pos++;
|
|
366
|
+
i++;
|
|
367
|
+
}
|
|
368
|
+
this.line++;
|
|
369
|
+
this.column = 1;
|
|
370
|
+
} else {
|
|
371
|
+
this.column++;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
peek(offset = 1) {
|
|
376
|
+
let index = this.pos + offset;
|
|
377
|
+
if (index >= this.source.length) return 0;
|
|
378
|
+
return this.source.charCodeAt(index);
|
|
379
|
+
}
|
|
380
|
+
make_token(type, start, end, line = this.line, column = this.column) {
|
|
381
|
+
this.token_type = type;
|
|
382
|
+
this.token_start = start;
|
|
383
|
+
this.token_end = end;
|
|
384
|
+
this.token_line = line;
|
|
385
|
+
this.token_column = column;
|
|
386
|
+
return type;
|
|
387
|
+
}
|
|
388
|
+
// Public API: returns Token object for backwards compatibility
|
|
389
|
+
next_token(skip_whitespace = false) {
|
|
390
|
+
this.next_token_fast(skip_whitespace);
|
|
391
|
+
return {
|
|
392
|
+
type: this.token_type,
|
|
393
|
+
start: this.token_start,
|
|
394
|
+
end: this.token_end,
|
|
395
|
+
line: this.token_line,
|
|
396
|
+
column: this.token_column
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Save complete lexer state for backtracking
|
|
401
|
+
* @returns Object containing all lexer state
|
|
402
|
+
*/
|
|
403
|
+
save_position() {
|
|
404
|
+
return {
|
|
405
|
+
pos: this.pos,
|
|
406
|
+
line: this.line,
|
|
407
|
+
column: this.column,
|
|
408
|
+
token_type: this.token_type,
|
|
409
|
+
token_start: this.token_start,
|
|
410
|
+
token_end: this.token_end,
|
|
411
|
+
token_line: this.token_line,
|
|
412
|
+
token_column: this.token_column
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Restore lexer state from saved position
|
|
417
|
+
* @param saved The saved position to restore
|
|
418
|
+
*/
|
|
419
|
+
restore_position(saved) {
|
|
420
|
+
this.pos = saved.pos;
|
|
421
|
+
this.line = saved.line;
|
|
422
|
+
this.column = saved.column;
|
|
423
|
+
this.token_type = saved.token_type;
|
|
424
|
+
this.token_start = saved.token_start;
|
|
425
|
+
this.token_end = saved.token_end;
|
|
426
|
+
this.token_line = saved.token_line;
|
|
427
|
+
this.token_column = saved.token_column;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
4
430
|
function* tokenize(source, skip_comments = true) {
|
|
5
431
|
const lexer = new Lexer(source, skip_comments);
|
|
6
432
|
while (true) {
|
|
@@ -12,4 +438,4 @@ function* tokenize(source, skip_comments = true) {
|
|
|
12
438
|
}
|
|
13
439
|
}
|
|
14
440
|
|
|
15
|
-
export { tokenize };
|
|
441
|
+
export { Lexer, tokenize };
|
package/dist/walk.d.ts
CHANGED
|
@@ -3,34 +3,12 @@ export declare const SKIP: unique symbol;
|
|
|
3
3
|
export declare const BREAK: unique symbol;
|
|
4
4
|
type WalkCallback = (node: CSSNode, depth: number) => void | typeof SKIP | typeof BREAK;
|
|
5
5
|
/**
|
|
6
|
-
* Walk the AST in depth-first order, calling the callback for each node
|
|
6
|
+
* Walk the AST in depth-first order, calling the callback for each node.
|
|
7
|
+
* Return SKIP to skip children, BREAK to stop traversal. See API.md for examples.
|
|
7
8
|
*
|
|
8
9
|
* @param node - The root node to start walking from
|
|
9
|
-
* @param callback - Function
|
|
10
|
-
* Return SKIP to skip children of current node, or BREAK to stop traversal entirely.
|
|
10
|
+
* @param callback - Function called for each node. Receives the node and its depth (0 for root).
|
|
11
11
|
* @param depth - Starting depth (default: 0)
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* import { parse, walk, SKIP, BREAK } from '@projectwallace/css-parser'
|
|
15
|
-
*
|
|
16
|
-
* const ast = parse('.a { .b { .c { color: red; } } }')
|
|
17
|
-
*
|
|
18
|
-
* // Skip nested rules
|
|
19
|
-
* walk(ast, (node) => {
|
|
20
|
-
* if (node.type === STYLE_RULE) {
|
|
21
|
-
* console.log(node.text)
|
|
22
|
-
* return SKIP // Don't visit nested rules
|
|
23
|
-
* }
|
|
24
|
-
* })
|
|
25
|
-
* // Output: .a { ... }, but not .b or .c
|
|
26
|
-
*
|
|
27
|
-
* // Stop on first declaration
|
|
28
|
-
* walk(ast, (node) => {
|
|
29
|
-
* if (node.type === DECLARATION) {
|
|
30
|
-
* console.log(node.name)
|
|
31
|
-
* return BREAK // Stop traversal
|
|
32
|
-
* }
|
|
33
|
-
* })
|
|
34
12
|
*/
|
|
35
13
|
export declare function walk(node: CSSNode, callback: WalkCallback, depth?: number): boolean;
|
|
36
14
|
type WalkEnterLeaveCallback = (node: CSSNode) => void | typeof SKIP | typeof BREAK;
|
|
@@ -39,36 +17,11 @@ interface WalkEnterLeaveOptions {
|
|
|
39
17
|
leave?: WalkEnterLeaveCallback;
|
|
40
18
|
}
|
|
41
19
|
/**
|
|
42
|
-
* Walk the AST in depth-first order, calling enter before visiting children and leave after
|
|
20
|
+
* Walk the AST in depth-first order, calling enter before visiting children and leave after.
|
|
21
|
+
* Return SKIP in enter to skip children (leave still called), BREAK to stop (leave NOT called). See API.md for examples.
|
|
43
22
|
*
|
|
44
23
|
* @param node - The root node to start walking from
|
|
45
24
|
* @param options - Object with optional enter and leave callback functions
|
|
46
|
-
* @param options.enter - Called before visiting children. Return SKIP to skip children (leave still called),
|
|
47
|
-
* or BREAK to stop traversal entirely (leave NOT called).
|
|
48
|
-
* @param options.leave - Called after visiting children. Return BREAK to stop traversal.
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* import { parse, traverse, SKIP, BREAK } from '@projectwallace/css-parser'
|
|
52
|
-
*
|
|
53
|
-
* const ast = parse('@media screen { .a { color: red; } }')
|
|
54
|
-
*
|
|
55
|
-
* // Track context with skip
|
|
56
|
-
* let depth = 0
|
|
57
|
-
* traverse(ast, {
|
|
58
|
-
* enter(node) {
|
|
59
|
-
* depth++
|
|
60
|
-
* if (node.type === AT_RULE) {
|
|
61
|
-
* console.log('Entering media query at depth', depth)
|
|
62
|
-
* return SKIP // Skip contents but still call leave
|
|
63
|
-
* }
|
|
64
|
-
* },
|
|
65
|
-
* leave(node) {
|
|
66
|
-
* if (node.type === AT_RULE) {
|
|
67
|
-
* console.log('Leaving media query at depth', depth)
|
|
68
|
-
* }
|
|
69
|
-
* depth--
|
|
70
|
-
* }
|
|
71
|
-
* })
|
|
72
25
|
*/
|
|
73
26
|
export declare function traverse(node: CSSNode, { enter, leave }?: WalkEnterLeaveOptions): boolean;
|
|
74
27
|
export {};
|
package/dist/walk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const SKIP = Symbol("SKIP");
|
|
2
|
-
const BREAK = Symbol("BREAK");
|
|
1
|
+
const SKIP = /* @__PURE__ */ Symbol("SKIP");
|
|
2
|
+
const BREAK = /* @__PURE__ */ Symbol("BREAK");
|
|
3
3
|
function walk(node, callback, depth = 0) {
|
|
4
4
|
const result = callback(node, depth);
|
|
5
5
|
if (result === BREAK) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projectwallace/css-parser",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"description": "High-performance CSS lexer and parser, optimized for CSS inspection and analysis",
|
|
5
|
-
"author": "Bart Veneman <
|
|
5
|
+
"author": "Bart Veneman <bart@projectwallace.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"ast"
|
|
71
71
|
],
|
|
72
72
|
"devDependencies": {
|
|
73
|
+
"@codecov/vite-plugin": "^1.9.1",
|
|
73
74
|
"@projectwallace/preset-oxlint": "^0.0.7",
|
|
74
75
|
"@types/node": "^24.10.1",
|
|
75
76
|
"@vitest/coverage-v8": "^4.0.8",
|
|
@@ -84,5 +85,10 @@
|
|
|
84
85
|
"typescript": "^5.9.3",
|
|
85
86
|
"vite": "^7.2.2",
|
|
86
87
|
"vitest": "^4.0.8"
|
|
88
|
+
},
|
|
89
|
+
"overrides": {
|
|
90
|
+
"@codecov/vite-plugin": {
|
|
91
|
+
"vite": "$vite"
|
|
92
|
+
}
|
|
87
93
|
}
|
|
88
94
|
}
|
package/dist/lexer.d.ts
DELETED