@projectwallace/css-parser 0.18.4 → 0.18.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.
@@ -0,0 +1,737 @@
1
+ import { t as Lexer } from "./tokenize-DK24ACbY.js";
2
+ import "./arena-e9O_g2AN.js";
3
+ import { a as is_whitespace, o as str_equals } from "./parse-dimension-ouWXNHdx.js";
4
+ import { i as find_colon_at_depth_zero, o as skip_whitespace_and_comments_forward, s as trim_boundaries, t as SelectorParser } from "./parse-selector-DMduLN4o.js";
5
+ //#region src/parse-condition.ts
6
+ /** @internal */
7
+ var ConditionParser = class {
8
+ lexer;
9
+ arena;
10
+ source;
11
+ end = 0;
12
+ value_node_parser;
13
+ selector_parser;
14
+ constructor(arena, source, value_node_parser) {
15
+ this.arena = arena;
16
+ this.source = source;
17
+ this.lexer = new Lexer(source);
18
+ this.value_node_parser = value_node_parser;
19
+ this.selector_parser = new SelectorParser(arena, source);
20
+ }
21
+ get end_position() {
22
+ return [
23
+ this.lexer.pos,
24
+ this.lexer.line,
25
+ this.lexer.column
26
+ ];
27
+ }
28
+ create_node(type, start, end) {
29
+ return this.arena.create_node(type, start, end - start, this.lexer.token_line, this.lexer.token_column);
30
+ }
31
+ next_token() {
32
+ if (this.lexer.pos >= this.end) {
33
+ this.lexer.token_type = 26;
34
+ return 26;
35
+ }
36
+ return this.lexer.next_token_fast(false);
37
+ }
38
+ is_and_or_not(str) {
39
+ return str_equals("and", str) || str_equals("or", str) || str_equals("not", str);
40
+ }
41
+ scan_matching_paren() {
42
+ let depth = 1;
43
+ let content_end = this.lexer.pos;
44
+ let close_end = this.lexer.token_end;
45
+ while (this.lexer.pos < this.end && depth > 0) {
46
+ let token_type = this.next_token();
47
+ if (token_type === 21 || token_type === 2) depth++;
48
+ else if (token_type === 22) {
49
+ depth--;
50
+ if (depth === 0) {
51
+ content_end = this.lexer.token_start;
52
+ close_end = this.lexer.token_end;
53
+ }
54
+ } else if (token_type === 26) break;
55
+ }
56
+ return [
57
+ content_end,
58
+ close_end,
59
+ depth === 0
60
+ ];
61
+ }
62
+ parse_feature_value(start, end) {
63
+ return this.value_node_parser.parse_chain(start, end, this.lexer.line, this.lexer.column);
64
+ }
65
+ wrap_ratio_value(first_node) {
66
+ if (this.arena.get_type(first_node) !== 11) return first_node;
67
+ let op_node = this.arena.get_next_sibling(first_node);
68
+ if (op_node === 0 || this.arena.get_type(op_node) !== 16) return first_node;
69
+ if (this.arena.get_length(op_node) !== 1) return first_node;
70
+ if (this.source.charCodeAt(this.arena.get_start_offset(op_node)) !== 47) return first_node;
71
+ let second_node = this.arena.get_next_sibling(op_node);
72
+ if (second_node === 0 || this.arena.get_type(second_node) !== 11) return first_node;
73
+ if (this.arena.get_next_sibling(second_node) !== 0) return first_node;
74
+ let start = this.arena.get_start_offset(first_node);
75
+ let end = this.arena.get_start_offset(second_node) + this.arena.get_length(second_node);
76
+ let ratio_node = this.arena.create_node(58, start, end - start, this.arena.get_start_line(first_node), this.arena.get_start_column(first_node));
77
+ this.arena.set_first_child(ratio_node, first_node);
78
+ this.arena.set_next_sibling(first_node, second_node);
79
+ return ratio_node;
80
+ }
81
+ create_supports_declaration(content_start, content_end, colon_pos) {
82
+ let prop_trimmed = trim_boundaries(this.source, content_start, colon_pos);
83
+ let val_trimmed = trim_boundaries(this.source, colon_pos + 1, content_end);
84
+ if (!prop_trimmed) return this.create_node(57, content_start, content_end);
85
+ let decl_start = prop_trimmed[0];
86
+ let decl_end = val_trimmed ? val_trimmed[1] : colon_pos + 1;
87
+ let decl = this.create_node(4, decl_start, decl_end);
88
+ this.arena.set_content_start_delta(decl, 0);
89
+ this.arena.set_content_length(decl, prop_trimmed[1] - prop_trimmed[0]);
90
+ if (val_trimmed) {
91
+ let value_first = this.parse_feature_value(val_trimmed[0], val_trimmed[1]);
92
+ let value_node;
93
+ if (value_first === 0) value_node = this.arena.create_node(50, val_trimmed[0], 0, this.lexer.token_line, this.lexer.token_column);
94
+ else {
95
+ value_node = this.arena.create_node(50, val_trimmed[0], val_trimmed[1] - val_trimmed[0], this.lexer.token_line, this.lexer.token_column);
96
+ this.arena.set_first_child(value_node, value_first);
97
+ }
98
+ this.arena.set_first_child(decl, value_node);
99
+ }
100
+ let supports_decl = this.create_node(57, content_start, content_end);
101
+ this.arena.set_content_start_delta(supports_decl, prop_trimmed[0] - content_start);
102
+ this.arena.set_content_length(supports_decl, prop_trimmed[1] - prop_trimmed[0]);
103
+ this.arena.set_first_child(supports_decl, decl);
104
+ return supports_decl;
105
+ }
106
+ parse_feature_range(feature_start, feature_end, content_start, content_end) {
107
+ let range_node = this.create_node(39, feature_start, feature_end);
108
+ let first_child = 0;
109
+ let last_child = 0;
110
+ let feature_name_start = -1;
111
+ let feature_name_end = -1;
112
+ let pos = content_start;
113
+ while (pos < content_end) {
114
+ pos = skip_whitespace_and_comments_forward(this.source, pos, content_end);
115
+ if (pos >= content_end) break;
116
+ let ch = this.source.charCodeAt(pos);
117
+ if (ch === 60 || ch === 62 || ch === 61) {
118
+ let op_start = pos++;
119
+ if (pos < content_end && this.source.charCodeAt(pos) === 61) pos++;
120
+ let op = this.create_node(38, op_start, pos);
121
+ if (first_child === 0) first_child = op;
122
+ else this.arena.set_next_sibling(last_child, op);
123
+ last_child = op;
124
+ } else {
125
+ let saved = this.lexer.save_position();
126
+ this.lexer.pos = pos;
127
+ this.next_token();
128
+ if (this.lexer.token_type === 1) {
129
+ feature_name_start = this.lexer.token_start;
130
+ feature_name_end = this.lexer.token_end;
131
+ } else {
132
+ let value_first = this.parse_feature_value(this.lexer.token_start, this.lexer.token_end);
133
+ if (value_first !== 0) {
134
+ if (first_child === 0) first_child = value_first;
135
+ else this.arena.set_next_sibling(last_child, value_first);
136
+ last_child = this.arena.get_last_sibling(value_first);
137
+ }
138
+ }
139
+ pos = this.lexer.pos;
140
+ this.lexer.restore_position(saved);
141
+ }
142
+ }
143
+ if (feature_name_start !== -1) {
144
+ this.arena.set_content_start_delta(range_node, feature_name_start - feature_start);
145
+ this.arena.set_content_length(range_node, feature_name_end - feature_name_start);
146
+ }
147
+ if (first_child !== 0) this.arena.set_first_child(range_node, first_child);
148
+ return range_node;
149
+ }
150
+ /**
151
+ * Parse the content of a media feature — everything between its parentheses, already
152
+ * known by the caller as `[content_start, content_end)`. `feature_start`/`feature_end`
153
+ * are the span of the feature node itself: for a real `@media (min-width: 768px)` feature
154
+ * this includes the surrounding parens (`feature_start < content_start`); for if()'s
155
+ * `media(min-width: 768px)`, media()'s own parens already delimit the content, so callers
156
+ * pass `feature_start === content_start` and `feature_end === content_end`.
157
+ * Returns a MEDIA_FEATURE (name/boolean/name:value) or FEATURE_RANGE (comparison syntax) node.
158
+ */
159
+ parse_media_feature_content(feature_start, feature_end, content_start, content_end) {
160
+ this.end = content_end;
161
+ let has_comparison = false;
162
+ let i = content_start;
163
+ while (i < content_end) {
164
+ i = skip_whitespace_and_comments_forward(this.source, i, content_end);
165
+ if (i >= content_end) break;
166
+ let ch = this.source.charCodeAt(i);
167
+ if (ch === 60 || ch === 62 || ch === 61) {
168
+ has_comparison = true;
169
+ break;
170
+ }
171
+ i++;
172
+ }
173
+ if (has_comparison) return this.parse_feature_range(feature_start, feature_end, content_start, content_end);
174
+ let feature = this.create_node(33, feature_start, feature_end);
175
+ let colon_pos = -1;
176
+ let j = content_start;
177
+ while (j < content_end) {
178
+ j = skip_whitespace_and_comments_forward(this.source, j, content_end);
179
+ if (j >= content_end) break;
180
+ if (this.source.charCodeAt(j) === 58) {
181
+ colon_pos = j;
182
+ break;
183
+ }
184
+ j++;
185
+ }
186
+ if (colon_pos === -1) {
187
+ let trimmed = trim_boundaries(this.source, content_start, content_end);
188
+ if (trimmed) {
189
+ this.arena.set_content_start_delta(feature, trimmed[0] - feature_start);
190
+ this.arena.set_content_length(feature, trimmed[1] - trimmed[0]);
191
+ }
192
+ } else {
193
+ let name_trimmed = trim_boundaries(this.source, content_start, colon_pos);
194
+ if (name_trimmed) {
195
+ this.arena.set_content_start_delta(feature, name_trimmed[0] - feature_start);
196
+ this.arena.set_content_length(feature, name_trimmed[1] - name_trimmed[0]);
197
+ }
198
+ let value_trimmed = trim_boundaries(this.source, colon_pos + 1, content_end);
199
+ if (value_trimmed) {
200
+ let value_first = this.parse_feature_value(value_trimmed[0], value_trimmed[1]);
201
+ if (value_first !== 0) this.arena.set_first_child(feature, this.wrap_ratio_value(value_first));
202
+ }
203
+ }
204
+ return feature;
205
+ }
206
+ parse_media_feature(open_paren_start, end, line, column) {
207
+ this.end = end;
208
+ this.lexer.seek(open_paren_start, line, column);
209
+ this.next_token();
210
+ let feature_start = this.lexer.token_start;
211
+ let content_start = this.lexer.pos;
212
+ let [content_end, feature_end, matched] = this.scan_matching_paren();
213
+ if (!matched) return null;
214
+ return this.parse_media_feature_content(feature_start, feature_end, content_start, content_end);
215
+ }
216
+ parse_function_condition(func_start, end, line, column) {
217
+ this.end = end;
218
+ this.lexer.seek(func_start, line, column);
219
+ this.next_token();
220
+ let func_name = this.source.substring(this.lexer.token_start, this.lexer.token_end - 1);
221
+ let content_start = this.lexer.token_end;
222
+ let [content_end, func_end] = this.scan_matching_paren();
223
+ let func_node = this.create_node(15, func_start, func_end);
224
+ this.arena.set_content_start_delta(func_node, 0);
225
+ this.arena.set_content_length(func_node, func_name.length);
226
+ this.arena.set_value_start_delta(func_node, content_start - func_start);
227
+ this.arena.set_value_length(func_node, content_end - content_start);
228
+ if (str_equals("selector", func_name)) {
229
+ let selector_list = this.selector_parser.parse_selector(content_start, content_end, this.lexer.line, this.lexer.column);
230
+ if (selector_list !== null) this.arena.set_first_child(func_node, selector_list);
231
+ } else if (str_equals("style", func_name)) {
232
+ let decl_child = this.parse_supports_declaration_content(content_start, content_end);
233
+ if (decl_child !== null) this.arena.set_first_child(func_node, decl_child);
234
+ }
235
+ return func_node;
236
+ }
237
+ /**
238
+ * Parse a single `(property: value)` supports declaration from a content range — the
239
+ * `<style-feature>`/`<supports-feature>` grammar shared by `style()`, `@supports (…)`, and
240
+ * `@import … supports(…)`. Returns null if no top-level ':' is found.
241
+ */
242
+ parse_supports_declaration_content(content_start, content_end) {
243
+ let colon_pos = find_colon_at_depth_zero(this.source, content_start, content_end);
244
+ if (colon_pos === -1) return null;
245
+ return this.create_supports_declaration(content_start, content_end, colon_pos);
246
+ }
247
+ /**
248
+ * Parse a `<supports-condition>` — the compound and/or/not grammar shared by `@supports`
249
+ * preludes and if()'s `supports(...)` condition function: parenthesized `(property: value)`
250
+ * declarations, `and`/`or`/`not` logical operators, and nested function conditions
251
+ * (`selector()`, `style()`, etc.), chained as flat siblings.
252
+ * `start`/`end` bound the range to scan; the caller has already positioned nothing — this
253
+ * seeks its own lexer, so it's safe to call with any sub-range of the source.
254
+ */
255
+ parse_supports_condition(start, end, line, column) {
256
+ let saved_end = this.end;
257
+ this.end = end;
258
+ this.lexer.seek(start, line, column);
259
+ let nodes = [];
260
+ while (this.lexer.pos < this.end) {
261
+ this.lexer.skip_whitespace_in_range(this.end);
262
+ if (this.lexer.pos >= this.end) break;
263
+ this.next_token();
264
+ let token_type = this.lexer.token_type;
265
+ if (token_type === 21) {
266
+ let feature_start = this.lexer.token_start;
267
+ let content_start = this.lexer.pos;
268
+ let [content_end, feature_end, matched] = this.scan_matching_paren();
269
+ if (matched) {
270
+ let query = this.create_node(36, feature_start, feature_end);
271
+ let trimmed = trim_boundaries(this.source, content_start, content_end);
272
+ if (trimmed) {
273
+ this.arena.set_value_start_delta(query, trimmed[0] - feature_start);
274
+ this.arena.set_value_length(query, trimmed[1] - trimmed[0]);
275
+ let decl_child = this.parse_supports_declaration_content(trimmed[0], trimmed[1]);
276
+ if (decl_child !== null) this.arena.set_first_child(query, decl_child);
277
+ }
278
+ nodes.push(query);
279
+ }
280
+ } else if (token_type === 1) {
281
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
282
+ if (this.is_and_or_not(text)) {
283
+ let op = this.create_node(38, this.lexer.token_start, this.lexer.token_end);
284
+ nodes.push(op);
285
+ }
286
+ } else if (token_type === 2) nodes.push(this.parse_function_condition(this.lexer.token_start, this.end, this.lexer.token_line, this.lexer.token_column));
287
+ }
288
+ this.end = saved_end;
289
+ return nodes;
290
+ }
291
+ };
292
+ //#endregion
293
+ //#region src/value-node-parser.ts
294
+ /** @internal */
295
+ var ValueNodeParser = class ValueNodeParser {
296
+ lexer;
297
+ arena;
298
+ source;
299
+ end = 0;
300
+ last_chain_node = 0;
301
+ _condition_parser = null;
302
+ get condition_parser() {
303
+ if (this._condition_parser === null) this._condition_parser = new ConditionParser(this.arena, this.source, new ValueNodeParser(this.arena, this.source));
304
+ return this._condition_parser;
305
+ }
306
+ constructor(arena, source) {
307
+ this.arena = arena;
308
+ this.source = source;
309
+ this.lexer = new Lexer(source);
310
+ }
311
+ parse_chain(start, end, start_line, start_column) {
312
+ this.end = end;
313
+ this.lexer.seek(start, start_line, start_column);
314
+ let first_node = 0;
315
+ let last_node = 0;
316
+ while (this.lexer.pos < this.end) {
317
+ this.lexer.next_token_fast(false);
318
+ if (this.lexer.token_start >= this.end) break;
319
+ if (this.lexer.token_type === 26) break;
320
+ if (this.is_whitespace_inline()) continue;
321
+ let node = this.parse_value_node();
322
+ if (node !== null) {
323
+ if (first_node === 0) first_node = node;
324
+ else this.arena.set_next_sibling(last_node, node);
325
+ last_node = node;
326
+ }
327
+ }
328
+ this.last_chain_node = last_node;
329
+ return first_node;
330
+ }
331
+ is_whitespace_inline() {
332
+ if (this.lexer.token_start >= this.lexer.token_end) return false;
333
+ for (let i = this.lexer.token_start; i < this.lexer.token_end; i++) if (!is_whitespace(this.source.charCodeAt(i))) return false;
334
+ return true;
335
+ }
336
+ parse_value_node() {
337
+ let token_type = this.lexer.token_type;
338
+ let start = this.lexer.token_start;
339
+ let end = this.lexer.token_end;
340
+ switch (token_type) {
341
+ case 1: return this.create_node(10, start, end);
342
+ case 10: return this.create_node(11, start, end);
343
+ case 11:
344
+ case 12: return this.create_node(12, start, end);
345
+ case 5: return this.create_node(13, start, end);
346
+ case 4: return this.create_node(14, start, end);
347
+ case 27: return this.create_node(19, start, end);
348
+ case 2: return this.parse_function_node(start, end);
349
+ case 9: return this.parse_operator_node(start, end);
350
+ case 18:
351
+ case 16:
352
+ case 17: return this.create_node(16, start, end);
353
+ case 21: return this.parse_parenthesis_node(start, end);
354
+ default: return null;
355
+ }
356
+ }
357
+ create_node(node_type, start, end) {
358
+ let node = this.arena.create_node(node_type, start, end - start, this.lexer.token_line, this.lexer.token_column);
359
+ this.arena.set_content_length(node, end - start);
360
+ return node;
361
+ }
362
+ parse_operator_node(start, end) {
363
+ let ch = this.source.charCodeAt(start);
364
+ if (ch === 43 || ch === 45 || ch === 42 || ch === 47) return this.create_node(16, start, end);
365
+ return null;
366
+ }
367
+ scan_matching_paren(bounded) {
368
+ let depth = 1;
369
+ let content_end = this.lexer.pos;
370
+ let close_end = this.lexer.token_end;
371
+ while ((!bounded || this.lexer.pos < this.end) && depth > 0) {
372
+ this.lexer.next_token_fast(false);
373
+ let token_type = this.lexer.token_type;
374
+ if (token_type === 26) break;
375
+ if (bounded && this.lexer.token_start >= this.end) break;
376
+ if (token_type === 21 || token_type === 2) depth++;
377
+ else if (token_type === 22) {
378
+ depth--;
379
+ if (depth === 0) {
380
+ content_end = this.lexer.token_start;
381
+ close_end = this.lexer.token_end;
382
+ }
383
+ }
384
+ }
385
+ return [
386
+ content_end,
387
+ close_end,
388
+ depth === 0
389
+ ];
390
+ }
391
+ parse_function_node(start, end) {
392
+ let name_end = end - 1;
393
+ let func_name_substr = this.source.substring(start, name_end);
394
+ if (str_equals("if", func_name_substr)) return this.parse_if_function_node(start, end);
395
+ let node = this.arena.create_node(str_equals("url", func_name_substr) ? 18 : 15, start, 0, this.lexer.token_line, this.lexer.token_column);
396
+ this.arena.set_content_start_delta(node, 0);
397
+ this.arena.set_content_length(node, name_end - start);
398
+ if (str_equals("url", func_name_substr) || str_equals("src", func_name_substr)) {
399
+ let save_pos = this.lexer.save_position();
400
+ this.lexer.next_token_fast(false);
401
+ while (this.is_whitespace_inline() && this.lexer.pos < this.end) this.lexer.next_token_fast(false);
402
+ let first_token_type = this.lexer.token_type;
403
+ this.lexer.restore_position(save_pos);
404
+ if (first_token_type === 5) {} else {
405
+ let func_end = end;
406
+ let content_start = end;
407
+ let content_end = end;
408
+ let [scanned_content_end, scanned_func_end, matched] = this.scan_matching_paren(false);
409
+ if (matched) {
410
+ content_end = scanned_content_end;
411
+ func_end = scanned_func_end;
412
+ }
413
+ this.arena.set_length(node, func_end - start);
414
+ this.arena.set_value_start_delta(node, content_start - start);
415
+ this.arena.set_value_length(node, content_end - content_start);
416
+ return node;
417
+ }
418
+ }
419
+ let first_arg = 0;
420
+ let last_arg = 0;
421
+ let paren_depth = 1;
422
+ let func_end = end;
423
+ let content_start = end;
424
+ let content_end = end;
425
+ while (this.lexer.pos < this.end && paren_depth > 0) {
426
+ this.lexer.next_token_fast(false);
427
+ let token_type = this.lexer.token_type;
428
+ if (token_type === 26) break;
429
+ if (this.lexer.token_start >= this.end) break;
430
+ if (token_type === 22) {
431
+ paren_depth--;
432
+ if (paren_depth === 0) {
433
+ content_end = this.lexer.token_start;
434
+ func_end = this.lexer.token_end;
435
+ break;
436
+ }
437
+ }
438
+ if (this.is_whitespace_inline()) continue;
439
+ let arg_node = this.parse_value_node();
440
+ if (arg_node !== null) {
441
+ if (first_arg === 0) first_arg = arg_node;
442
+ else this.arena.set_next_sibling(last_arg, arg_node);
443
+ last_arg = arg_node;
444
+ }
445
+ }
446
+ this.arena.set_length(node, func_end - start);
447
+ this.arena.set_value_start_delta(node, content_start - start);
448
+ this.arena.set_value_length(node, content_end - content_start);
449
+ if (first_arg !== 0) this.arena.set_first_child(node, first_arg);
450
+ return node;
451
+ }
452
+ /**
453
+ * Parse an if() inline conditional function.
454
+ *
455
+ * Spec grammar (CSS Values Level 5):
456
+ * if( <if-branch>+ )
457
+ * <if-branch> = <if-condition> : <declaration-value>? ;?
458
+ * <if-condition> = style(…) | media(…) | supports(…) | else
459
+ *
460
+ * Each branch becomes an IF_BRANCH child (see the `IfBranch` type in node-types.ts
461
+ * for its shape). Colons/semicolons here are structural separators, not OPERATOR nodes.
462
+ */
463
+ parse_if_function_node(start, end) {
464
+ let name_end = end - 1;
465
+ let save_line = this.lexer.token_line;
466
+ let save_col = this.lexer.token_column;
467
+ let node = this.arena.create_node(15, start, 0, save_line, save_col);
468
+ this.arena.set_content_start_delta(node, 0);
469
+ this.arena.set_content_length(node, name_end - start);
470
+ let branches = [];
471
+ let func_end = end;
472
+ let content_start = end;
473
+ let content_end = end;
474
+ let if_closed = false;
475
+ while (this.lexer.pos < this.end && !if_closed) {
476
+ this.lexer.next_token_fast(false);
477
+ let tt = this.lexer.token_type;
478
+ if (tt === 26) break;
479
+ if (this.lexer.token_start >= this.end) break;
480
+ if (tt === 22) {
481
+ content_end = this.lexer.token_start;
482
+ func_end = this.lexer.token_end;
483
+ break;
484
+ }
485
+ if (this.is_whitespace_inline() || tt === 17 || tt === 16) continue;
486
+ let branch_start = this.lexer.token_start;
487
+ let branch_line = this.lexer.token_line;
488
+ let branch_col = this.lexer.token_column;
489
+ let condition_node = this.parse_if_condition();
490
+ if (condition_node === null) continue;
491
+ let condition_end_pos = this.arena.get_start_offset(condition_node) + this.arena.get_length(condition_node);
492
+ let colon_found = false;
493
+ while (this.lexer.pos < this.end) {
494
+ this.lexer.next_token_fast(false);
495
+ let t = this.lexer.token_type;
496
+ if (t === 26) break;
497
+ if (this.lexer.token_start >= this.end) break;
498
+ if (this.is_whitespace_inline()) continue;
499
+ if (t === 16) {
500
+ colon_found = true;
501
+ break;
502
+ }
503
+ if (t === 22) {
504
+ content_end = this.lexer.token_start;
505
+ func_end = this.lexer.token_end;
506
+ if_closed = true;
507
+ break;
508
+ }
509
+ }
510
+ let value_tokens = [];
511
+ let value_start = -1;
512
+ let value_last_end = condition_end_pos;
513
+ let value_line = 0;
514
+ let value_col = 0;
515
+ if (colon_found && !if_closed) while (this.lexer.pos < this.end) {
516
+ this.lexer.next_token_fast(false);
517
+ let t = this.lexer.token_type;
518
+ if (t === 26) break;
519
+ if (this.lexer.token_start >= this.end) break;
520
+ if (this.is_whitespace_inline()) continue;
521
+ if (t === 17) break;
522
+ if (t === 22) {
523
+ content_end = this.lexer.token_start;
524
+ func_end = this.lexer.token_end;
525
+ if_closed = true;
526
+ break;
527
+ }
528
+ let vnode = this.parse_value_node();
529
+ if (vnode !== null) {
530
+ let ns = this.arena.get_start_offset(vnode);
531
+ if (value_start === -1) {
532
+ value_start = ns;
533
+ value_line = this.arena.get_start_line(vnode);
534
+ value_col = this.arena.get_start_column(vnode);
535
+ }
536
+ value_tokens.push(vnode);
537
+ value_last_end = ns + this.arena.get_length(vnode);
538
+ }
539
+ }
540
+ let value_node = null;
541
+ if (value_tokens.length > 0) {
542
+ value_node = this.arena.create_node(50, value_start, value_last_end - value_start, value_line, value_col);
543
+ this.arena.append_children(value_node, value_tokens);
544
+ }
545
+ let branch_end = value_node === null ? condition_end_pos : value_last_end;
546
+ let branch_node = this.arena.create_node(59, branch_start, branch_end - branch_start, branch_line, branch_col);
547
+ this.arena.set_content_start_delta(branch_node, 0);
548
+ this.arena.set_content_length(branch_node, condition_end_pos - branch_start);
549
+ if (value_start !== -1) {
550
+ this.arena.set_value_start_delta(branch_node, value_start - branch_start);
551
+ this.arena.set_value_length(branch_node, value_last_end - value_start);
552
+ }
553
+ let branch_children = [condition_node];
554
+ if (value_node !== null) branch_children.push(value_node);
555
+ this.arena.append_children(branch_node, branch_children);
556
+ branches.push(branch_node);
557
+ }
558
+ this.arena.set_length(node, func_end - start);
559
+ this.arena.set_value_start_delta(node, content_start - start);
560
+ this.arena.set_value_length(node, content_end - content_start);
561
+ this.arena.append_children(node, branches);
562
+ return node;
563
+ }
564
+ is_and_or_not(str) {
565
+ return str_equals("and", str) || str_equals("or", str) || str_equals("not", str);
566
+ }
567
+ next_significant_token() {
568
+ while (this.lexer.pos < this.end) {
569
+ this.lexer.next_token_fast(false);
570
+ if (this.lexer.token_start >= this.end) return 26;
571
+ if (this.is_whitespace_inline()) continue;
572
+ return this.lexer.token_type;
573
+ }
574
+ return 26;
575
+ }
576
+ /**
577
+ * Parse an if()-branch condition:
578
+ * <if-condition> = <boolean-expr[ <if-test> ]> | else
579
+ * <if-test> = style(…) | media(…) | supports(…)
580
+ * i.e. a single test function, the bare "else" identifier, or those test functions
581
+ * combined with not/and/or (e.g. "not style(--x: 1)", "style(--a: 1) and media(width > 600px)").
582
+ * Called with the lexer's current token already positioned at the condition's first token.
583
+ * A simple single-function/"else" condition returns that node directly, unwrapped (matching
584
+ * prior behavior); a compound not/and/or condition wraps the flat operator/function chain in
585
+ * an IF_CONDITION node so IfBranch.condition/.value (single first_child/next_sibling hops)
586
+ * still see exactly one node for "the condition".
587
+ */
588
+ parse_if_condition() {
589
+ if (this.lexer.token_type === 1) {
590
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
591
+ if (str_equals("else", text)) return this.parse_value_node();
592
+ }
593
+ let first = 0;
594
+ let last = 0;
595
+ let component_count = 0;
596
+ let compound = false;
597
+ for (;;) {
598
+ let tt = this.lexer.token_type;
599
+ let component = null;
600
+ let is_operator = false;
601
+ if (tt === 2) component = this.parse_if_condition_function(this.lexer.token_start, this.lexer.token_end);
602
+ else if (tt === 1) {
603
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
604
+ if (this.is_and_or_not(text)) {
605
+ is_operator = true;
606
+ component = this.arena.create_node(38, this.lexer.token_start, this.lexer.token_end - this.lexer.token_start, this.lexer.token_line, this.lexer.token_column);
607
+ } else component = this.parse_value_node();
608
+ } else component = this.parse_value_node();
609
+ if (component !== null) {
610
+ component_count++;
611
+ if (first === 0) first = component;
612
+ else this.arena.set_next_sibling(last, component);
613
+ last = component;
614
+ }
615
+ if (tt !== 2 && !is_operator) break;
616
+ let saved = this.lexer.save_position();
617
+ let next_tt = this.next_significant_token();
618
+ if (next_tt === 2) {
619
+ compound = true;
620
+ continue;
621
+ }
622
+ if (next_tt === 1) {
623
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
624
+ if (this.is_and_or_not(text)) {
625
+ compound = true;
626
+ continue;
627
+ }
628
+ }
629
+ this.lexer.restore_position(saved);
630
+ break;
631
+ }
632
+ if (first === 0) return null;
633
+ if (component_count === 1 && !compound) return first;
634
+ let wrapper_start = this.arena.get_start_offset(first);
635
+ let last_sibling = this.arena.get_last_sibling(first);
636
+ let wrapper_end = this.arena.get_start_offset(last_sibling) + this.arena.get_length(last_sibling);
637
+ let wrapper = this.arena.create_node(60, wrapper_start, wrapper_end - wrapper_start, this.arena.get_start_line(first), this.arena.get_start_column(first));
638
+ this.arena.set_first_child(wrapper, first);
639
+ return wrapper;
640
+ }
641
+ /**
642
+ * Parse a condition function inside if() — style(), supports(), or media(). Content parsing
643
+ * is delegated to the shared ConditionParser (see parse-condition.ts), so these produce the
644
+ * same node shapes real `@supports`/`@media` conditions do:
645
+ * style()/supports() → SUPPORTS_DECLARATION(s) — supports() gets the full compound
646
+ * <supports-condition> grammar (and/or/not, nested conditions), same as `@supports`.
647
+ * media() → a single MEDIA_FEATURE or FEATURE_RANGE (comparison syntax), same as `@media`.
648
+ * anything else → generic value nodes, as before.
649
+ *
650
+ * Called with the current token at TOKEN_FUNCTION (the '(' already consumed).
651
+ * @param func_start Offset of the function name's first char.
652
+ * @param token_end Offset right after '(' (== lexer.token_end here).
653
+ */
654
+ parse_if_condition_function(func_start, token_end) {
655
+ let func_name_end = token_end - 1;
656
+ let func_name = this.source.substring(func_start, func_name_end);
657
+ let func_line = this.lexer.token_line;
658
+ let func_col = this.lexer.token_column;
659
+ let content_start = token_end;
660
+ let content_end = content_start;
661
+ let func_end = content_start;
662
+ let [scanned_content_end, scanned_func_end, matched] = this.scan_matching_paren(true);
663
+ if (matched) {
664
+ content_end = scanned_content_end;
665
+ func_end = scanned_func_end;
666
+ }
667
+ let func_node = this.arena.create_node(15, func_start, func_end - func_start, func_line, func_col);
668
+ this.arena.set_content_start_delta(func_node, 0);
669
+ this.arena.set_content_length(func_node, func_name_end - func_start);
670
+ this.arena.set_value_start_delta(func_node, content_start - func_start);
671
+ this.arena.set_value_length(func_node, content_end - content_start);
672
+ let child_nodes = [];
673
+ if (str_equals("style", func_name)) {
674
+ let decl = this.condition_parser.parse_supports_declaration_content(content_start, content_end);
675
+ if (decl === null) child_nodes = this.condition_parser.parse_supports_condition(content_start, content_end, func_line, func_col);
676
+ else child_nodes = [decl];
677
+ } else if (str_equals("supports", func_name)) {
678
+ let decl = this.condition_parser.parse_supports_declaration_content(content_start, content_end);
679
+ if (decl === null) child_nodes = this.condition_parser.parse_supports_condition(content_start, content_end, func_line, func_col);
680
+ else child_nodes = [decl];
681
+ } else if (str_equals("media", func_name)) child_nodes = [this.condition_parser.parse_media_feature_content(content_start, content_end, content_start, content_end)];
682
+ else child_nodes = this.parse_value_nodes_in_range(content_start, content_end);
683
+ this.arena.append_children(func_node, child_nodes);
684
+ return func_node;
685
+ }
686
+ /** Parse value tokens in a source sub-range using save/restore to protect lexer state. */
687
+ parse_value_nodes_in_range(start, end) {
688
+ let saved_end = this.end;
689
+ let saved_pos = this.lexer.save_position();
690
+ this.end = end;
691
+ this.lexer.seek(start, this.lexer.line, this.lexer.column);
692
+ let nodes = [];
693
+ while (this.lexer.pos < this.end) {
694
+ this.lexer.next_token_fast(false);
695
+ if (this.lexer.token_start >= this.end) break;
696
+ if (this.lexer.token_type === 26) break;
697
+ if (this.is_whitespace_inline()) continue;
698
+ let node = this.parse_value_node();
699
+ if (node !== null) nodes.push(node);
700
+ }
701
+ this.lexer.restore_position(saved_pos);
702
+ this.end = saved_end;
703
+ return nodes;
704
+ }
705
+ parse_parenthesis_node(start, end) {
706
+ let node = this.arena.create_node(17, start, 0, this.lexer.token_line, this.lexer.token_column);
707
+ let first_child = 0;
708
+ let last_child = 0;
709
+ let paren_depth = 1;
710
+ let paren_end = end;
711
+ while (this.lexer.pos < this.end && paren_depth > 0) {
712
+ this.lexer.next_token_fast(false);
713
+ let token_type = this.lexer.token_type;
714
+ if (token_type === 26) break;
715
+ if (this.lexer.token_start >= this.end) break;
716
+ if (token_type === 22) {
717
+ paren_depth--;
718
+ if (paren_depth === 0) {
719
+ paren_end = this.lexer.token_end;
720
+ break;
721
+ }
722
+ }
723
+ if (this.is_whitespace_inline()) continue;
724
+ let child_node = this.parse_value_node();
725
+ if (child_node !== null) {
726
+ if (first_child === 0) first_child = child_node;
727
+ else this.arena.set_next_sibling(last_child, child_node);
728
+ last_child = child_node;
729
+ }
730
+ }
731
+ this.arena.set_length(node, paren_end - start);
732
+ if (first_child !== 0) this.arena.set_first_child(node, first_child);
733
+ return node;
734
+ }
735
+ };
736
+ //#endregion
737
+ export { ConditionParser as n, ValueNodeParser as t };