@projectwallace/css-parser 0.1.0
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/README.md +72 -0
- package/dist/arena.d.ts +84 -0
- package/dist/at-rule-prelude-parser-DlqYQAYH.js +464 -0
- package/dist/at-rule-prelude-parser.d.ts +24 -0
- package/dist/char-types.d.ts +7 -0
- package/dist/css-node.d.ts +31 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +28 -0
- package/dist/lexer-DXablYMZ.js +479 -0
- package/dist/lexer.d.ts +27 -0
- package/dist/parse-atrule-prelude.d.ts +8 -0
- package/dist/parse-atrule-prelude.js +11 -0
- package/dist/parse-selector.d.ts +7 -0
- package/dist/parse-selector.js +19 -0
- package/dist/parse.d.ts +9 -0
- package/dist/parse.js +512 -0
- package/dist/parser.d.ts +34 -0
- package/dist/selector-parser-2b3tGyri.js +365 -0
- package/dist/selector-parser.d.ts +25 -0
- package/dist/string-utils-B-rJII-E.js +440 -0
- package/dist/string-utils.d.ts +29 -0
- package/dist/token-types.d.ts +34 -0
- package/dist/tokenize.d.ts +8 -0
- package/dist/tokenize.js +14 -0
- package/dist/value-parser.d.ts +19 -0
- package/dist/walk.d.ts +20 -0
- package/package.json +78 -0
package/dist/parse.js
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { L as Lexer, y as TOKEN_EOF, q as TOKEN_COMMA, h as TOKEN_DELIM, a as TOKEN_FUNCTION, c as TOKEN_HASH, d as TOKEN_STRING, k as TOKEN_DIMENSION, j as TOKEN_PERCENTAGE, i as TOKEN_NUMBER, T as TOKEN_IDENT, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN, b as TOKEN_AT_KEYWORD, v as TOKEN_LEFT_BRACE, w as TOKEN_RIGHT_BRACE, o as TOKEN_COLON, p as TOKEN_SEMICOLON } from './lexer-DXablYMZ.js';
|
|
2
|
+
import { J as is_whitespace, f as NODE_VALUE_KEYWORD, g as NODE_VALUE_NUMBER, h as NODE_VALUE_DIMENSION, i as NODE_VALUE_STRING, j as NODE_VALUE_COLOR, l as NODE_VALUE_OPERATOR, k as NODE_VALUE_FUNCTION, K as CSSDataArena, e as NODE_STYLESHEET, C as CSSNode, N as NODE_STYLE_RULE, L as FLAG_HAS_BLOCK, d as NODE_SELECTOR, c as NODE_DECLARATION, M as trim_boundaries, I as FLAG_IMPORTANT, a as NODE_AT_RULE } from './string-utils-B-rJII-E.js';
|
|
3
|
+
import { S as SelectorParser } from './selector-parser-2b3tGyri.js';
|
|
4
|
+
import { A as AtRulePreludeParser } from './at-rule-prelude-parser-DlqYQAYH.js';
|
|
5
|
+
|
|
6
|
+
const CH_PLUS = 43;
|
|
7
|
+
const CH_MINUS = 45;
|
|
8
|
+
const CH_ASTERISK = 42;
|
|
9
|
+
const CH_SLASH = 47;
|
|
10
|
+
class ValueParser {
|
|
11
|
+
lexer;
|
|
12
|
+
arena;
|
|
13
|
+
source;
|
|
14
|
+
value_end;
|
|
15
|
+
constructor(arena, source) {
|
|
16
|
+
this.arena = arena;
|
|
17
|
+
this.source = source;
|
|
18
|
+
this.lexer = new Lexer(source, false);
|
|
19
|
+
this.value_end = 0;
|
|
20
|
+
}
|
|
21
|
+
// Parse a declaration value range into value nodes
|
|
22
|
+
// Returns array of value node indices
|
|
23
|
+
parse_value(start, end) {
|
|
24
|
+
this.value_end = end;
|
|
25
|
+
this.lexer.pos = start;
|
|
26
|
+
this.lexer.line = 1;
|
|
27
|
+
let nodes = [];
|
|
28
|
+
while (this.lexer.pos < this.value_end) {
|
|
29
|
+
this.lexer.next_token_fast(false);
|
|
30
|
+
if (this.lexer.token_start >= this.value_end) break;
|
|
31
|
+
let token_type = this.lexer.token_type;
|
|
32
|
+
if (token_type === TOKEN_EOF) break;
|
|
33
|
+
if (this.is_whitespace_token()) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
let node = this.parse_value_node();
|
|
37
|
+
if (node !== null) {
|
|
38
|
+
nodes.push(node);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return nodes;
|
|
42
|
+
}
|
|
43
|
+
is_whitespace_token() {
|
|
44
|
+
let start = this.lexer.token_start;
|
|
45
|
+
let end = this.lexer.token_end;
|
|
46
|
+
if (start >= end) return false;
|
|
47
|
+
for (let i = start; i < end; i++) {
|
|
48
|
+
let ch = this.source.charCodeAt(i);
|
|
49
|
+
if (!is_whitespace(ch)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
parse_value_node() {
|
|
56
|
+
let token_type = this.lexer.token_type;
|
|
57
|
+
let start = this.lexer.token_start;
|
|
58
|
+
let end = this.lexer.token_end;
|
|
59
|
+
switch (token_type) {
|
|
60
|
+
case TOKEN_IDENT:
|
|
61
|
+
return this.create_keyword_node(start, end);
|
|
62
|
+
case TOKEN_NUMBER:
|
|
63
|
+
return this.create_number_node(start, end);
|
|
64
|
+
case TOKEN_PERCENTAGE:
|
|
65
|
+
case TOKEN_DIMENSION:
|
|
66
|
+
return this.create_dimension_node(start, end);
|
|
67
|
+
case TOKEN_STRING:
|
|
68
|
+
return this.create_string_node(start, end);
|
|
69
|
+
case TOKEN_HASH:
|
|
70
|
+
return this.create_color_node(start, end);
|
|
71
|
+
case TOKEN_FUNCTION:
|
|
72
|
+
return this.parse_function_node(start, end);
|
|
73
|
+
case TOKEN_DELIM:
|
|
74
|
+
return this.parse_operator_node(start, end);
|
|
75
|
+
case TOKEN_COMMA:
|
|
76
|
+
return this.create_operator_node(start, end);
|
|
77
|
+
default:
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
create_keyword_node(start, end) {
|
|
82
|
+
let node = this.arena.create_node();
|
|
83
|
+
this.arena.set_type(node, NODE_VALUE_KEYWORD);
|
|
84
|
+
this.arena.set_start_offset(node, start);
|
|
85
|
+
this.arena.set_length(node, end - start);
|
|
86
|
+
this.arena.set_content_start(node, start);
|
|
87
|
+
this.arena.set_content_length(node, end - start);
|
|
88
|
+
return node;
|
|
89
|
+
}
|
|
90
|
+
create_number_node(start, end) {
|
|
91
|
+
let node = this.arena.create_node();
|
|
92
|
+
this.arena.set_type(node, NODE_VALUE_NUMBER);
|
|
93
|
+
this.arena.set_start_offset(node, start);
|
|
94
|
+
this.arena.set_length(node, end - start);
|
|
95
|
+
this.arena.set_content_start(node, start);
|
|
96
|
+
this.arena.set_content_length(node, end - start);
|
|
97
|
+
return node;
|
|
98
|
+
}
|
|
99
|
+
create_dimension_node(start, end) {
|
|
100
|
+
let node = this.arena.create_node();
|
|
101
|
+
this.arena.set_type(node, NODE_VALUE_DIMENSION);
|
|
102
|
+
this.arena.set_start_offset(node, start);
|
|
103
|
+
this.arena.set_length(node, end - start);
|
|
104
|
+
this.arena.set_content_start(node, start);
|
|
105
|
+
this.arena.set_content_length(node, end - start);
|
|
106
|
+
return node;
|
|
107
|
+
}
|
|
108
|
+
create_string_node(start, end) {
|
|
109
|
+
let node = this.arena.create_node();
|
|
110
|
+
this.arena.set_type(node, NODE_VALUE_STRING);
|
|
111
|
+
this.arena.set_start_offset(node, start);
|
|
112
|
+
this.arena.set_length(node, end - start);
|
|
113
|
+
this.arena.set_content_start(node, start);
|
|
114
|
+
this.arena.set_content_length(node, end - start);
|
|
115
|
+
return node;
|
|
116
|
+
}
|
|
117
|
+
create_color_node(start, end) {
|
|
118
|
+
let node = this.arena.create_node();
|
|
119
|
+
this.arena.set_type(node, NODE_VALUE_COLOR);
|
|
120
|
+
this.arena.set_start_offset(node, start);
|
|
121
|
+
this.arena.set_length(node, end - start);
|
|
122
|
+
this.arena.set_content_start(node, start);
|
|
123
|
+
this.arena.set_content_length(node, end - start);
|
|
124
|
+
return node;
|
|
125
|
+
}
|
|
126
|
+
create_operator_node(start, end) {
|
|
127
|
+
let node = this.arena.create_node();
|
|
128
|
+
this.arena.set_type(node, NODE_VALUE_OPERATOR);
|
|
129
|
+
this.arena.set_start_offset(node, start);
|
|
130
|
+
this.arena.set_length(node, end - start);
|
|
131
|
+
this.arena.set_content_start(node, start);
|
|
132
|
+
this.arena.set_content_length(node, end - start);
|
|
133
|
+
return node;
|
|
134
|
+
}
|
|
135
|
+
parse_operator_node(start, end) {
|
|
136
|
+
let ch = this.source.charCodeAt(start);
|
|
137
|
+
if (ch === CH_PLUS || ch === CH_MINUS || ch === CH_ASTERISK || ch === CH_SLASH) {
|
|
138
|
+
return this.create_operator_node(start, end);
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
parse_function_node(start, end) {
|
|
143
|
+
let node = this.arena.create_node();
|
|
144
|
+
this.arena.set_type(node, NODE_VALUE_FUNCTION);
|
|
145
|
+
this.arena.set_start_offset(node, start);
|
|
146
|
+
let name_end = end - 1;
|
|
147
|
+
this.arena.set_content_start(node, start);
|
|
148
|
+
this.arena.set_content_length(node, name_end - start);
|
|
149
|
+
let args = [];
|
|
150
|
+
let paren_depth = 1;
|
|
151
|
+
let func_end = end;
|
|
152
|
+
while (this.lexer.pos < this.value_end && paren_depth > 0) {
|
|
153
|
+
this.lexer.next_token_fast(false);
|
|
154
|
+
let token_type = this.lexer.token_type;
|
|
155
|
+
if (token_type === TOKEN_EOF) break;
|
|
156
|
+
if (this.lexer.token_start >= this.value_end) break;
|
|
157
|
+
if (token_type === TOKEN_LEFT_PAREN) {
|
|
158
|
+
paren_depth++;
|
|
159
|
+
} else if (token_type === TOKEN_RIGHT_PAREN) {
|
|
160
|
+
paren_depth--;
|
|
161
|
+
if (paren_depth === 0) {
|
|
162
|
+
func_end = this.lexer.token_end;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (this.is_whitespace_token()) continue;
|
|
167
|
+
let arg_node = this.parse_value_node();
|
|
168
|
+
if (arg_node !== null) {
|
|
169
|
+
args.push(arg_node);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
this.arena.set_length(node, func_end - start);
|
|
173
|
+
if (args.length > 0) {
|
|
174
|
+
this.arena.set_first_child(node, args[0]);
|
|
175
|
+
this.arena.set_last_child(node, args[args.length - 1]);
|
|
176
|
+
for (let i = 0; i < args.length - 1; i++) {
|
|
177
|
+
this.arena.set_next_sibling(args[i], args[i + 1]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return node;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let DECLARATION_AT_RULES = /* @__PURE__ */ new Set(["font-face", "font-feature-values", "page", "property", "counter-style"]);
|
|
185
|
+
let CONDITIONAL_AT_RULES = /* @__PURE__ */ new Set(["media", "supports", "container", "layer", "nest"]);
|
|
186
|
+
class Parser {
|
|
187
|
+
source;
|
|
188
|
+
lexer;
|
|
189
|
+
arena;
|
|
190
|
+
value_parser;
|
|
191
|
+
selector_parser;
|
|
192
|
+
prelude_parser;
|
|
193
|
+
parse_values_enabled;
|
|
194
|
+
parse_selectors_enabled;
|
|
195
|
+
parse_atrule_preludes_enabled;
|
|
196
|
+
constructor(source, options) {
|
|
197
|
+
this.source = source;
|
|
198
|
+
let opts;
|
|
199
|
+
if (typeof options === "boolean") {
|
|
200
|
+
opts = { skip_comments: options };
|
|
201
|
+
} else {
|
|
202
|
+
opts = options || {};
|
|
203
|
+
}
|
|
204
|
+
let skip_comments = opts.skip_comments ?? true;
|
|
205
|
+
this.parse_values_enabled = opts.parse_values ?? true;
|
|
206
|
+
this.parse_selectors_enabled = opts.parse_selectors ?? true;
|
|
207
|
+
this.parse_atrule_preludes_enabled = opts.parse_atrule_preludes ?? true;
|
|
208
|
+
this.lexer = new Lexer(source, skip_comments);
|
|
209
|
+
let capacity = CSSDataArena.capacity_for_source(source.length);
|
|
210
|
+
this.arena = new CSSDataArena(capacity);
|
|
211
|
+
this.value_parser = this.parse_values_enabled ? new ValueParser(this.arena, source) : null;
|
|
212
|
+
this.selector_parser = this.parse_selectors_enabled ? new SelectorParser(this.arena, source) : null;
|
|
213
|
+
this.prelude_parser = this.parse_atrule_preludes_enabled ? new AtRulePreludeParser(this.arena, source) : null;
|
|
214
|
+
}
|
|
215
|
+
// Get the arena (for internal/advanced use only)
|
|
216
|
+
get_arena() {
|
|
217
|
+
return this.arena;
|
|
218
|
+
}
|
|
219
|
+
// Get the source code
|
|
220
|
+
get_source() {
|
|
221
|
+
return this.source;
|
|
222
|
+
}
|
|
223
|
+
// Advance to the next token, skipping whitespace
|
|
224
|
+
next_token() {
|
|
225
|
+
this.lexer.next_token_fast(true);
|
|
226
|
+
}
|
|
227
|
+
// Peek at current token type
|
|
228
|
+
peek_type() {
|
|
229
|
+
return this.lexer.token_type;
|
|
230
|
+
}
|
|
231
|
+
// Check if we're at the end of input
|
|
232
|
+
is_eof() {
|
|
233
|
+
return this.peek_type() === TOKEN_EOF;
|
|
234
|
+
}
|
|
235
|
+
// Parse the entire stylesheet and return the root CSSNode
|
|
236
|
+
parse() {
|
|
237
|
+
this.next_token();
|
|
238
|
+
let stylesheet = this.arena.create_node();
|
|
239
|
+
this.arena.set_type(stylesheet, NODE_STYLESHEET);
|
|
240
|
+
this.arena.set_start_offset(stylesheet, 0);
|
|
241
|
+
this.arena.set_length(stylesheet, this.source.length);
|
|
242
|
+
this.arena.set_start_line(stylesheet, 1);
|
|
243
|
+
while (!this.is_eof()) {
|
|
244
|
+
let rule = this.parse_rule();
|
|
245
|
+
if (rule !== null) {
|
|
246
|
+
this.arena.append_child(stylesheet, rule);
|
|
247
|
+
} else {
|
|
248
|
+
this.next_token();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return new CSSNode(this.arena, this.source, stylesheet);
|
|
252
|
+
}
|
|
253
|
+
// Parse a rule (style rule or at-rule)
|
|
254
|
+
parse_rule() {
|
|
255
|
+
if (this.is_eof()) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
if (this.peek_type() === TOKEN_AT_KEYWORD) {
|
|
259
|
+
return this.parse_atrule();
|
|
260
|
+
}
|
|
261
|
+
return this.parse_style_rule();
|
|
262
|
+
}
|
|
263
|
+
// Parse a style rule: selector { declarations }
|
|
264
|
+
parse_style_rule() {
|
|
265
|
+
if (this.is_eof()) return null;
|
|
266
|
+
let rule_start = this.lexer.token_start;
|
|
267
|
+
let rule_line = this.lexer.token_line;
|
|
268
|
+
let style_rule = this.arena.create_node();
|
|
269
|
+
this.arena.set_type(style_rule, NODE_STYLE_RULE);
|
|
270
|
+
this.arena.set_start_line(style_rule, rule_line);
|
|
271
|
+
let selector = this.parse_selector();
|
|
272
|
+
if (selector !== null) {
|
|
273
|
+
this.arena.append_child(style_rule, selector);
|
|
274
|
+
}
|
|
275
|
+
if (this.peek_type() !== TOKEN_LEFT_BRACE) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
this.next_token();
|
|
279
|
+
this.arena.set_flag(style_rule, FLAG_HAS_BLOCK);
|
|
280
|
+
while (!this.is_eof()) {
|
|
281
|
+
let token_type = this.peek_type();
|
|
282
|
+
if (token_type === TOKEN_RIGHT_BRACE) break;
|
|
283
|
+
if (token_type === TOKEN_AT_KEYWORD) {
|
|
284
|
+
let nested_at_rule = this.parse_atrule();
|
|
285
|
+
if (nested_at_rule !== null) {
|
|
286
|
+
this.arena.append_child(style_rule, nested_at_rule);
|
|
287
|
+
} else {
|
|
288
|
+
this.next_token();
|
|
289
|
+
}
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
let declaration = this.parse_declaration();
|
|
293
|
+
if (declaration !== null) {
|
|
294
|
+
this.arena.append_child(style_rule, declaration);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
let nested_rule = this.parse_style_rule();
|
|
298
|
+
if (nested_rule !== null) {
|
|
299
|
+
this.arena.append_child(style_rule, nested_rule);
|
|
300
|
+
} else {
|
|
301
|
+
this.next_token();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (this.peek_type() === TOKEN_RIGHT_BRACE) {
|
|
305
|
+
this.next_token();
|
|
306
|
+
}
|
|
307
|
+
this.arena.set_start_offset(style_rule, rule_start);
|
|
308
|
+
this.arena.set_length(style_rule, this.lexer.token_end - rule_start);
|
|
309
|
+
return style_rule;
|
|
310
|
+
}
|
|
311
|
+
// Parse a selector (everything before '{')
|
|
312
|
+
parse_selector() {
|
|
313
|
+
if (this.is_eof()) return null;
|
|
314
|
+
let selector_start = this.lexer.token_start;
|
|
315
|
+
let selector_line = this.lexer.token_line;
|
|
316
|
+
let last_end = this.lexer.token_end;
|
|
317
|
+
while (!this.is_eof() && this.peek_type() !== TOKEN_LEFT_BRACE) {
|
|
318
|
+
last_end = this.lexer.token_end;
|
|
319
|
+
this.next_token();
|
|
320
|
+
}
|
|
321
|
+
if (this.parse_selectors_enabled && this.selector_parser) {
|
|
322
|
+
let selectorNode = this.selector_parser.parse_selector(selector_start, last_end, selector_line);
|
|
323
|
+
if (selectorNode !== null) {
|
|
324
|
+
return selectorNode;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
let selector = this.arena.create_node();
|
|
328
|
+
this.arena.set_type(selector, NODE_SELECTOR);
|
|
329
|
+
this.arena.set_start_line(selector, selector_line);
|
|
330
|
+
this.arena.set_start_offset(selector, selector_start);
|
|
331
|
+
this.arena.set_length(selector, last_end - selector_start);
|
|
332
|
+
return selector;
|
|
333
|
+
}
|
|
334
|
+
// Parse a declaration: property: value;
|
|
335
|
+
parse_declaration() {
|
|
336
|
+
if (this.peek_type() !== TOKEN_IDENT) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
let prop_start = this.lexer.token_start;
|
|
340
|
+
let prop_end = this.lexer.token_end;
|
|
341
|
+
let decl_line = this.lexer.token_line;
|
|
342
|
+
this.next_token();
|
|
343
|
+
if (this.peek_type() !== TOKEN_COLON) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
this.next_token();
|
|
347
|
+
let declaration = this.arena.create_node();
|
|
348
|
+
this.arena.set_type(declaration, NODE_DECLARATION);
|
|
349
|
+
this.arena.set_start_line(declaration, decl_line);
|
|
350
|
+
this.arena.set_start_offset(declaration, prop_start);
|
|
351
|
+
this.arena.set_content_start(declaration, prop_start);
|
|
352
|
+
this.arena.set_content_length(declaration, prop_end - prop_start);
|
|
353
|
+
let value_start = this.lexer.token_start;
|
|
354
|
+
let value_end = value_start;
|
|
355
|
+
let has_important = false;
|
|
356
|
+
let last_end = this.lexer.token_end;
|
|
357
|
+
while (!this.is_eof()) {
|
|
358
|
+
let token_type = this.peek_type();
|
|
359
|
+
if (token_type === TOKEN_SEMICOLON || token_type === TOKEN_RIGHT_BRACE) break;
|
|
360
|
+
if (token_type === TOKEN_DELIM && this.source[this.lexer.token_start] === "!") {
|
|
361
|
+
value_end = this.lexer.token_start;
|
|
362
|
+
let next_type = this.lexer.next_token_fast();
|
|
363
|
+
if (next_type === TOKEN_IDENT) {
|
|
364
|
+
has_important = true;
|
|
365
|
+
last_end = this.lexer.token_end;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
last_end = this.lexer.token_end;
|
|
370
|
+
value_end = last_end;
|
|
371
|
+
this.next_token();
|
|
372
|
+
}
|
|
373
|
+
let trimmed = trim_boundaries(this.source, value_start, value_end);
|
|
374
|
+
if (trimmed) {
|
|
375
|
+
this.arena.set_value_start(declaration, trimmed[0]);
|
|
376
|
+
this.arena.set_value_length(declaration, trimmed[1] - trimmed[0]);
|
|
377
|
+
if (this.parse_values_enabled && this.value_parser) {
|
|
378
|
+
let valueNodes = this.value_parser.parse_value(trimmed[0], trimmed[1]);
|
|
379
|
+
if (valueNodes.length > 0) {
|
|
380
|
+
this.arena.set_first_child(declaration, valueNodes[0]);
|
|
381
|
+
this.arena.set_last_child(declaration, valueNodes[valueNodes.length - 1]);
|
|
382
|
+
for (let i = 0; i < valueNodes.length - 1; i++) {
|
|
383
|
+
this.arena.set_next_sibling(valueNodes[i], valueNodes[i + 1]);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (has_important) {
|
|
389
|
+
this.arena.set_flag(declaration, FLAG_IMPORTANT);
|
|
390
|
+
}
|
|
391
|
+
if (this.peek_type() === TOKEN_SEMICOLON) {
|
|
392
|
+
last_end = this.lexer.token_end;
|
|
393
|
+
this.next_token();
|
|
394
|
+
}
|
|
395
|
+
this.arena.set_length(declaration, last_end - prop_start);
|
|
396
|
+
return declaration;
|
|
397
|
+
}
|
|
398
|
+
// Parse an at-rule: @media, @import, @font-face, etc.
|
|
399
|
+
parse_atrule() {
|
|
400
|
+
if (this.peek_type() !== TOKEN_AT_KEYWORD) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
let at_rule_start = this.lexer.token_start;
|
|
404
|
+
let at_rule_line = this.lexer.token_line;
|
|
405
|
+
let at_rule_name = this.source.substring(this.lexer.token_start + 1, this.lexer.token_end);
|
|
406
|
+
let name_start = this.lexer.token_start + 1;
|
|
407
|
+
let name_length = at_rule_name.length;
|
|
408
|
+
this.next_token();
|
|
409
|
+
let at_rule = this.arena.create_node();
|
|
410
|
+
this.arena.set_type(at_rule, NODE_AT_RULE);
|
|
411
|
+
this.arena.set_start_line(at_rule, at_rule_line);
|
|
412
|
+
this.arena.set_start_offset(at_rule, at_rule_start);
|
|
413
|
+
this.arena.set_content_start(at_rule, name_start);
|
|
414
|
+
this.arena.set_content_length(at_rule, name_length);
|
|
415
|
+
let prelude_start = this.lexer.token_start;
|
|
416
|
+
let prelude_end = prelude_start;
|
|
417
|
+
while (!this.is_eof() && this.peek_type() !== TOKEN_LEFT_BRACE && this.peek_type() !== TOKEN_SEMICOLON) {
|
|
418
|
+
prelude_end = this.lexer.token_end;
|
|
419
|
+
this.next_token();
|
|
420
|
+
}
|
|
421
|
+
let trimmed = trim_boundaries(this.source, prelude_start, prelude_end);
|
|
422
|
+
if (trimmed) {
|
|
423
|
+
this.arena.set_value_start(at_rule, trimmed[0]);
|
|
424
|
+
this.arena.set_value_length(at_rule, trimmed[1] - trimmed[0]);
|
|
425
|
+
if (this.prelude_parser) {
|
|
426
|
+
let prelude_nodes = this.prelude_parser.parse_prelude(at_rule_name, trimmed[0], trimmed[1], at_rule_line);
|
|
427
|
+
for (let prelude_node of prelude_nodes) {
|
|
428
|
+
this.arena.append_child(at_rule, prelude_node);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
let last_end = this.lexer.token_end;
|
|
433
|
+
if (this.peek_type() === TOKEN_LEFT_BRACE) {
|
|
434
|
+
this.next_token();
|
|
435
|
+
this.arena.set_flag(at_rule, FLAG_HAS_BLOCK);
|
|
436
|
+
let has_declarations = this.atrule_has_declarations(at_rule_name);
|
|
437
|
+
let is_conditional = this.atrule_is_conditional(at_rule_name);
|
|
438
|
+
if (has_declarations) {
|
|
439
|
+
while (!this.is_eof()) {
|
|
440
|
+
let token_type = this.peek_type();
|
|
441
|
+
if (token_type === TOKEN_RIGHT_BRACE) break;
|
|
442
|
+
let declaration = this.parse_declaration();
|
|
443
|
+
if (declaration !== null) {
|
|
444
|
+
this.arena.append_child(at_rule, declaration);
|
|
445
|
+
} else {
|
|
446
|
+
this.next_token();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
} else if (is_conditional) {
|
|
450
|
+
while (!this.is_eof()) {
|
|
451
|
+
let token_type = this.peek_type();
|
|
452
|
+
if (token_type === TOKEN_RIGHT_BRACE) break;
|
|
453
|
+
if (token_type === TOKEN_AT_KEYWORD) {
|
|
454
|
+
let nested_at_rule = this.parse_atrule();
|
|
455
|
+
if (nested_at_rule !== null) {
|
|
456
|
+
this.arena.append_child(at_rule, nested_at_rule);
|
|
457
|
+
} else {
|
|
458
|
+
this.next_token();
|
|
459
|
+
}
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
let declaration = this.parse_declaration();
|
|
463
|
+
if (declaration !== null) {
|
|
464
|
+
this.arena.append_child(at_rule, declaration);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
let nested_rule = this.parse_style_rule();
|
|
468
|
+
if (nested_rule !== null) {
|
|
469
|
+
this.arena.append_child(at_rule, nested_rule);
|
|
470
|
+
} else {
|
|
471
|
+
this.next_token();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
while (!this.is_eof()) {
|
|
476
|
+
let token_type = this.peek_type();
|
|
477
|
+
if (token_type === TOKEN_RIGHT_BRACE) break;
|
|
478
|
+
let rule = this.parse_rule();
|
|
479
|
+
if (rule !== null) {
|
|
480
|
+
this.arena.append_child(at_rule, rule);
|
|
481
|
+
} else {
|
|
482
|
+
this.next_token();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (this.peek_type() === TOKEN_RIGHT_BRACE) {
|
|
487
|
+
last_end = this.lexer.token_end;
|
|
488
|
+
this.next_token();
|
|
489
|
+
}
|
|
490
|
+
} else if (this.peek_type() === TOKEN_SEMICOLON) {
|
|
491
|
+
last_end = this.lexer.token_end;
|
|
492
|
+
this.next_token();
|
|
493
|
+
}
|
|
494
|
+
this.arena.set_length(at_rule, last_end - at_rule_start);
|
|
495
|
+
return at_rule;
|
|
496
|
+
}
|
|
497
|
+
// Determine if an at-rule contains declarations or nested rules
|
|
498
|
+
atrule_has_declarations(name) {
|
|
499
|
+
return DECLARATION_AT_RULES.has(name.toLowerCase());
|
|
500
|
+
}
|
|
501
|
+
// Determine if an at-rule is conditional (can contain both declarations and rules in CSS Nesting)
|
|
502
|
+
atrule_is_conditional(name) {
|
|
503
|
+
return CONDITIONAL_AT_RULES.has(name.toLowerCase());
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function parse(source, options) {
|
|
508
|
+
const parser = new Parser(source, options);
|
|
509
|
+
return parser.parse();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export { parse };
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CSSDataArena } from './arena';
|
|
2
|
+
import { CSSNode } from './css-node';
|
|
3
|
+
export interface ParserOptions {
|
|
4
|
+
skip_comments?: boolean;
|
|
5
|
+
parse_values?: boolean;
|
|
6
|
+
parse_selectors?: boolean;
|
|
7
|
+
parse_atrule_preludes?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class Parser {
|
|
10
|
+
private source;
|
|
11
|
+
private lexer;
|
|
12
|
+
private arena;
|
|
13
|
+
private value_parser;
|
|
14
|
+
private selector_parser;
|
|
15
|
+
private prelude_parser;
|
|
16
|
+
private parse_values_enabled;
|
|
17
|
+
private parse_selectors_enabled;
|
|
18
|
+
private parse_atrule_preludes_enabled;
|
|
19
|
+
constructor(source: string, options?: ParserOptions | boolean);
|
|
20
|
+
get_arena(): CSSDataArena;
|
|
21
|
+
get_source(): string;
|
|
22
|
+
private next_token;
|
|
23
|
+
private peek_type;
|
|
24
|
+
private is_eof;
|
|
25
|
+
parse(): CSSNode;
|
|
26
|
+
private parse_rule;
|
|
27
|
+
private parse_style_rule;
|
|
28
|
+
private parse_selector;
|
|
29
|
+
private parse_declaration;
|
|
30
|
+
private parse_atrule;
|
|
31
|
+
private atrule_has_declarations;
|
|
32
|
+
private atrule_is_conditional;
|
|
33
|
+
}
|
|
34
|
+
export { NODE_STYLESHEET, NODE_STYLE_RULE, NODE_AT_RULE, NODE_DECLARATION, NODE_SELECTOR, NODE_COMMENT, NODE_VALUE_KEYWORD, NODE_VALUE_NUMBER, NODE_VALUE_DIMENSION, NODE_VALUE_STRING, NODE_VALUE_COLOR, NODE_VALUE_FUNCTION, NODE_VALUE_OPERATOR, NODE_SELECTOR_LIST, NODE_SELECTOR_TYPE, NODE_SELECTOR_CLASS, NODE_SELECTOR_ID, NODE_SELECTOR_ATTRIBUTE, NODE_SELECTOR_PSEUDO_CLASS, NODE_SELECTOR_PSEUDO_ELEMENT, NODE_SELECTOR_COMBINATOR, NODE_SELECTOR_UNIVERSAL, NODE_SELECTOR_NESTING, NODE_PRELUDE_MEDIA_QUERY, NODE_PRELUDE_MEDIA_FEATURE, NODE_PRELUDE_MEDIA_TYPE, NODE_PRELUDE_CONTAINER_QUERY, NODE_PRELUDE_SUPPORTS_QUERY, NODE_PRELUDE_LAYER_NAME, NODE_PRELUDE_IDENTIFIER, NODE_PRELUDE_OPERATOR, NODE_PRELUDE_IMPORT_URL, NODE_PRELUDE_IMPORT_LAYER, NODE_PRELUDE_IMPORT_SUPPORTS, FLAG_IMPORTANT, } from './arena';
|