@projectwallace/css-parser 0.13.5 → 0.13.8

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.
Files changed (41) hide show
  1. package/dist/css-node-DqyvMXBN.d.ts +313 -0
  2. package/dist/css-node-Uj4oBgaw.js +647 -0
  3. package/dist/index.d.ts +150 -16
  4. package/dist/index.js +103 -13
  5. package/dist/parse-anplusb.d.ts +26 -2
  6. package/dist/parse-anplusb.js +191 -207
  7. package/dist/parse-atrule-prelude.d.ts +40 -2
  8. package/dist/parse-atrule-prelude.js +556 -652
  9. package/dist/parse-declaration.d.ts +16 -2
  10. package/dist/parse-declaration.js +140 -167
  11. package/dist/parse-dimension-CCn_XRDe.js +177 -0
  12. package/dist/parse-dimension.d.ts +6 -3
  13. package/dist/parse-dimension.js +1 -35
  14. package/dist/parse-selector.d.ts +37 -2
  15. package/dist/parse-selector.js +508 -635
  16. package/dist/parse-utils-DnsZRpfd.js +98 -0
  17. package/dist/parse-value.d.ts +23 -2
  18. package/dist/parse-value.js +176 -224
  19. package/dist/parse.d.ts +37 -8
  20. package/dist/parse.js +252 -353
  21. package/dist/tokenize-BQFB1jXg.js +540 -0
  22. package/dist/tokenize-odLrcjj2.d.ts +110 -0
  23. package/dist/tokenize.d.ts +2 -26
  24. package/dist/tokenize.js +1 -545
  25. package/package.json +20 -26
  26. package/dist/arena.d.ts +0 -60
  27. package/dist/arena.js +0 -291
  28. package/dist/char-types.d.ts +0 -14
  29. package/dist/char-types.js +0 -53
  30. package/dist/constants.d.ts +0 -44
  31. package/dist/constants.js +0 -51
  32. package/dist/css-node.d.ts +0 -203
  33. package/dist/css-node.js +0 -498
  34. package/dist/parse-utils.d.ts +0 -1
  35. package/dist/parse-utils.js +0 -60
  36. package/dist/string-utils.d.ts +0 -99
  37. package/dist/string-utils.js +0 -129
  38. package/dist/token-types.d.ts +0 -35
  39. package/dist/token-types.js +0 -29
  40. package/dist/walk.d.ts +0 -28
  41. package/dist/walk.js +0 -51
@@ -1,638 +1,511 @@
1
- import { Lexer } from './tokenize.js';
2
- import { CSSDataArena, SELECTOR_LIST, SELECTOR, COMBINATOR, DIMENSION, NESTING_SELECTOR, ID_SELECTOR, TYPE_SELECTOR, UNIVERSAL_SELECTOR, CLASS_SELECTOR, ATTRIBUTE_SELECTOR, ATTR_OPERATOR_NONE, ATTR_FLAG_NONE, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, FLAG_HAS_PARENS, LANG_SELECTOR, NTH_OF_SELECTOR } from './arena.js';
3
- import { TOKEN_COMMA, TOKEN_DELIM, TOKEN_EOF, TOKEN_WHITESPACE, TOKEN_PERCENTAGE, TOKEN_FUNCTION, TOKEN_COLON, TOKEN_LEFT_BRACKET, TOKEN_HASH, TOKEN_IDENT, TOKEN_RIGHT_BRACKET, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_STRING } from './token-types.js';
4
- import { skip_whitespace_and_comments_forward, skip_whitespace_and_comments_backward, skip_whitespace_forward } from './parse-utils.js';
5
- import { CHAR_GREATER_THAN, CHAR_PLUS, CHAR_TILDE, CHAR_PERIOD, CHAR_ASTERISK, CHAR_AMPERSAND, CHAR_PIPE, is_combinator, is_whitespace, CHAR_EQUALS, CHAR_CARET, CHAR_DOLLAR, CHAR_SINGLE_QUOTE, CHAR_DOUBLE_QUOTE, CHAR_COLON, str_equals, CHAR_FORWARD_SLASH } from './string-utils.js';
6
- import { ANplusBParser } from './parse-anplusb.js';
7
- import { CSSNode } from './css-node.js';
8
-
9
- class SelectorParser {
10
- lexer;
11
- arena;
12
- source;
13
- selector_end;
14
- constructor(arena, source) {
15
- this.arena = arena;
16
- this.source = source;
17
- this.lexer = new Lexer(source);
18
- this.selector_end = 0;
19
- }
20
- // Parse a selector range into selector nodes (standalone use)
21
- // Always returns a NODE_SELECTOR_LIST with selector components as children
22
- parse_selector(start, end, line = 1, column = 1, allow_relative = true) {
23
- this.selector_end = end;
24
- this.lexer.seek(start, line, column);
25
- return this.parse_selector_list(allow_relative);
26
- }
27
- // Parse comma-separated selectors
28
- parse_selector_list(allow_relative = true) {
29
- let selectors = [];
30
- let list_start = this.lexer.pos;
31
- let list_line = this.lexer.line;
32
- let list_column = this.lexer.column;
33
- while (this.lexer.pos < this.selector_end) {
34
- let selector_start = this.lexer.pos;
35
- let selector_line = this.lexer.line;
36
- let selector_column = this.lexer.column;
37
- let complex_selector = this.parse_complex_selector(allow_relative);
38
- if (complex_selector !== null) {
39
- let selector_wrapper = this.arena.create_node(
40
- SELECTOR,
41
- selector_start,
42
- this.lexer.pos - selector_start,
43
- selector_line,
44
- selector_column
45
- );
46
- this.arena.set_content_start_delta(selector_wrapper, 0);
47
- this.arena.set_content_length(selector_wrapper, this.lexer.pos - selector_start);
48
- let last_component = complex_selector;
49
- let next_sibling = this.arena.get_next_sibling(last_component);
50
- while (next_sibling !== 0) {
51
- last_component = next_sibling;
52
- next_sibling = this.arena.get_next_sibling(last_component);
53
- }
54
- this.arena.set_first_child(selector_wrapper, complex_selector);
55
- selectors.push(selector_wrapper);
56
- }
57
- this.skip_whitespace();
58
- if (this.lexer.pos >= this.selector_end) break;
59
- this.lexer.next_token_fast(false);
60
- let token_type = this.lexer.token_type;
61
- if (token_type === TOKEN_COMMA) {
62
- this.skip_whitespace();
63
- continue;
64
- } else {
65
- break;
66
- }
67
- }
68
- if (selectors.length >= 1) {
69
- let list_node = this.arena.create_node(SELECTOR_LIST, list_start, this.lexer.pos - list_start, list_line, list_column);
70
- this.arena.append_children(list_node, selectors);
71
- return list_node;
72
- }
73
- return null;
74
- }
75
- // Parse a complex selector (with combinators)
76
- // e.g., "div.class > p + span"
77
- // Also supports CSS Nesting relaxed syntax: "> a", "~ span", etc.
78
- parse_complex_selector(allow_relative = true) {
79
- let components = [];
80
- this.skip_whitespace();
81
- if (allow_relative && this.lexer.pos < this.selector_end) {
82
- const saved = this.lexer.save_position();
83
- this.lexer.next_token_fast(false);
84
- let token_type = this.lexer.token_type;
85
- if (token_type === TOKEN_DELIM) {
86
- let ch = this.source.charCodeAt(this.lexer.token_start);
87
- if (ch === CHAR_GREATER_THAN || ch === CHAR_PLUS || ch === CHAR_TILDE) {
88
- let combinator = this.create_node_at(
89
- COMBINATOR,
90
- this.lexer.token_start,
91
- this.lexer.token_end,
92
- this.lexer.token_line,
93
- this.lexer.token_column
94
- );
95
- components.push(combinator);
96
- this.skip_whitespace();
97
- } else {
98
- this.lexer.restore_position(saved);
99
- }
100
- } else {
101
- this.lexer.restore_position(saved);
102
- }
103
- }
104
- while (this.lexer.pos < this.selector_end) {
105
- let compound = this.parse_compound_selector();
106
- if (compound !== null) {
107
- components.push(compound);
108
- } else {
109
- break;
110
- }
111
- let combinator = this.try_parse_combinator();
112
- if (combinator !== null) {
113
- components.push(combinator);
114
- this.skip_whitespace();
115
- continue;
116
- }
117
- const saved = this.lexer.save_position();
118
- this.skip_whitespace();
119
- if (this.lexer.pos >= this.selector_end) {
120
- this.lexer.restore_position(saved);
121
- break;
122
- }
123
- this.lexer.next_token_fast(false);
124
- let token_type = this.lexer.token_type;
125
- if (token_type === TOKEN_COMMA || this.lexer.pos >= this.selector_end) {
126
- this.lexer.restore_position(saved);
127
- break;
128
- }
129
- this.lexer.restore_position(saved);
130
- break;
131
- }
132
- if (components.length === 0) return null;
133
- for (let i = 0; i < components.length - 1; i++) {
134
- let last_node = components[i];
135
- while (this.arena.get_next_sibling(last_node) !== 0) {
136
- last_node = this.arena.get_next_sibling(last_node);
137
- }
138
- this.arena.set_next_sibling(last_node, components[i + 1]);
139
- }
140
- return components[0];
141
- }
142
- // Parse a compound selector (no combinators)
143
- // e.g., "div.class#id[attr]:hover"
144
- parse_compound_selector() {
145
- let parts = [];
146
- while (this.lexer.pos < this.selector_end) {
147
- const saved = this.lexer.save_position();
148
- this.lexer.next_token_fast(false);
149
- if (this.lexer.token_start >= this.selector_end) break;
150
- let token_type = this.lexer.token_type;
151
- if (token_type === TOKEN_EOF) break;
152
- let part = this.parse_simple_selector();
153
- if (part !== null) {
154
- parts.push(part);
155
- } else {
156
- this.lexer.restore_position(saved);
157
- break;
158
- }
159
- }
160
- if (parts.length === 0) return null;
161
- for (let i = 0; i < parts.length - 1; i++) {
162
- this.arena.set_next_sibling(parts[i], parts[i + 1]);
163
- }
164
- return parts[0];
165
- }
166
- // Parse a simple selector (single component)
167
- parse_simple_selector() {
168
- let token_type = this.lexer.token_type;
169
- let start = this.lexer.token_start;
170
- let end = this.lexer.token_end;
171
- switch (token_type) {
172
- case TOKEN_IDENT:
173
- return this.parse_type_or_namespace_selector(start, end);
174
- case TOKEN_HASH:
175
- return this.create_node(ID_SELECTOR, start, end);
176
- case TOKEN_DELIM:
177
- let ch = this.source.charCodeAt(start);
178
- if (ch === CHAR_PERIOD) {
179
- return this.parse_class_selector(start);
180
- } else if (ch === CHAR_ASTERISK) {
181
- return this.parse_universal_or_namespace_selector(start, end);
182
- } else if (ch === CHAR_AMPERSAND) {
183
- return this.create_node(NESTING_SELECTOR, start, end);
184
- } else if (ch === CHAR_PIPE) {
185
- return this.parse_empty_namespace_selector(start);
186
- }
187
- return null;
188
- case TOKEN_LEFT_BRACKET:
189
- return this.parse_attribute_selector(start);
190
- case TOKEN_COLON:
191
- return this.parse_pseudo(start);
192
- case TOKEN_FUNCTION:
193
- return this.parse_pseudo_function(start, end);
194
- case TOKEN_PERCENTAGE:
195
- return this.create_node(DIMENSION, start, end);
196
- case TOKEN_WHITESPACE:
197
- case TOKEN_COMMA:
198
- return null;
199
- default:
200
- return null;
201
- }
202
- }
203
- // Parse the local part after | in a namespace selector (E or *)
204
- // Returns the node type (TYPE or UNIVERSAL) or null if invalid
205
- parse_namespace_local_part(selector_start, namespace_start, namespace_length) {
206
- const saved = this.lexer.save_position();
207
- this.lexer.next_token_fast(false);
208
- let node_type;
209
- if (this.lexer.token_type === TOKEN_IDENT) {
210
- node_type = TYPE_SELECTOR;
211
- } else if (this.lexer.token_type === TOKEN_DELIM && this.source.charCodeAt(this.lexer.token_start) === CHAR_ASTERISK) {
212
- node_type = UNIVERSAL_SELECTOR;
213
- } else {
214
- this.lexer.restore_position(saved);
215
- return null;
216
- }
217
- let node = this.create_node(node_type, selector_start, this.lexer.token_end);
218
- this.arena.set_content_start_delta(node, namespace_start - selector_start);
219
- this.arena.set_content_length(node, namespace_length);
220
- return node;
221
- }
222
- // Parse type selector or namespace selector (ns|E or ns|*)
223
- // Called when we've seen an IDENT token
224
- parse_type_or_namespace_selector(start, end) {
225
- const saved = this.lexer.save_position();
226
- this.skip_whitespace();
227
- if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_PIPE) {
228
- this.lexer.pos++;
229
- let node = this.parse_namespace_local_part(start, start, end - start);
230
- if (node !== null) return node;
231
- this.lexer.restore_position(saved);
232
- } else {
233
- this.lexer.restore_position(saved);
234
- }
235
- return this.create_node(TYPE_SELECTOR, start, end);
236
- }
237
- // Parse universal selector or namespace selector (*|E or *|*)
238
- // Called when we've seen a * DELIM token
239
- parse_universal_or_namespace_selector(start, end) {
240
- const saved = this.lexer.save_position();
241
- this.skip_whitespace();
242
- if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_PIPE) {
243
- this.lexer.pos++;
244
- let node = this.parse_namespace_local_part(start, start, end - start);
245
- if (node !== null) return node;
246
- this.lexer.restore_position(saved);
247
- } else {
248
- this.lexer.restore_position(saved);
249
- }
250
- return this.create_node(UNIVERSAL_SELECTOR, start, end);
251
- }
252
- // Parse empty namespace selector (|E or |*)
253
- // Called when we've seen a | DELIM token at the start
254
- parse_empty_namespace_selector(start) {
255
- return this.parse_namespace_local_part(start, start, 1);
256
- }
257
- // Parse combinator (>, +, ~, or descendant space)
258
- try_parse_combinator() {
259
- const saved_whitespace_start = this.lexer.save_position();
260
- let has_whitespace = this.lexer.pos < this.selector_end;
261
- this.skip_whitespace();
262
- has_whitespace = has_whitespace && this.lexer.pos > saved_whitespace_start.pos;
263
- if (this.lexer.pos >= this.selector_end) {
264
- this.lexer.restore_position(saved_whitespace_start);
265
- return null;
266
- }
267
- this.lexer.next_token_fast(false);
268
- if (this.lexer.token_type === TOKEN_DELIM) {
269
- let ch = this.source.charCodeAt(this.lexer.token_start);
270
- if (is_combinator(ch)) {
271
- return this.create_node_at(COMBINATOR, this.lexer.token_start, this.lexer.token_end, this.lexer.token_line, this.lexer.token_column);
272
- }
273
- }
274
- if (has_whitespace) {
275
- this.lexer.restore_position(saved_whitespace_start);
276
- this.skip_whitespace();
277
- return this.create_node_at(COMBINATOR, saved_whitespace_start.pos, this.lexer.pos, saved_whitespace_start.line, saved_whitespace_start.column);
278
- }
279
- this.lexer.restore_position(saved_whitespace_start);
280
- return null;
281
- }
282
- // Parse class selector (.classname)
283
- parse_class_selector(dot_pos) {
284
- const saved = this.lexer.save_position();
285
- this.lexer.next_token_fast(false);
286
- if (this.lexer.token_type !== TOKEN_IDENT) {
287
- this.lexer.restore_position(saved);
288
- return null;
289
- }
290
- return this.create_node(CLASS_SELECTOR, dot_pos, this.lexer.token_end);
291
- }
292
- // Parse attribute selector ([attr], [attr=value], etc.)
293
- parse_attribute_selector(start) {
294
- let bracket_depth = 1;
295
- let end = this.lexer.token_end;
296
- let content_start = start + 1;
297
- let content_end = content_start;
298
- while (this.lexer.pos < this.selector_end && bracket_depth > 0) {
299
- this.lexer.next_token_fast(false);
300
- let token_type = this.lexer.token_type;
301
- if (token_type === TOKEN_LEFT_BRACKET) {
302
- bracket_depth++;
303
- } else if (token_type === TOKEN_RIGHT_BRACKET) {
304
- bracket_depth--;
305
- if (bracket_depth === 0) {
306
- content_end = this.lexer.token_start;
307
- end = this.lexer.token_end;
308
- break;
309
- }
310
- }
311
- }
312
- let node = this.create_node(ATTRIBUTE_SELECTOR, start, end);
313
- this.parse_attribute_content(node, content_start, content_end);
314
- return node;
315
- }
316
- // Parse attribute content to extract name, operator, and value
317
- parse_attribute_content(node, start, end) {
318
- start = skip_whitespace_and_comments_forward(this.source, start, end);
319
- end = skip_whitespace_and_comments_backward(this.source, end, start);
320
- if (start >= end) return;
321
- let name_start = start;
322
- let name_end = start;
323
- let operator_end = -1;
324
- let value_start = -1;
325
- let value_end = -1;
326
- while (name_end < end) {
327
- let ch3 = this.source.charCodeAt(name_end);
328
- if (is_whitespace(ch3) || ch3 === CHAR_EQUALS || ch3 === CHAR_TILDE || ch3 === CHAR_PIPE || ch3 === CHAR_CARET || ch3 === CHAR_DOLLAR || ch3 === CHAR_ASTERISK) {
329
- break;
330
- }
331
- name_end++;
332
- }
333
- if (name_end > name_start) {
334
- this.arena.set_content_start_delta(node, name_start - this.arena.get_start_offset(node));
335
- this.arena.set_content_length(node, name_end - name_start);
336
- }
337
- let pos = skip_whitespace_and_comments_forward(this.source, name_end, end);
338
- if (pos >= end) {
339
- this.arena.set_attr_operator(node, ATTR_OPERATOR_NONE);
340
- this.arena.set_attr_flags(node, ATTR_FLAG_NONE);
341
- return;
342
- }
343
- let ch1 = this.source.charCodeAt(pos);
344
- let ch2 = pos + 1 < end ? this.source.charCodeAt(pos + 1) : 0;
345
- if (ch1 === CHAR_EQUALS) {
346
- operator_end = pos + 1;
347
- this.arena.set_attr_operator(node, ATTR_OPERATOR_EQUAL);
348
- } else if (ch1 === CHAR_TILDE && ch2 === CHAR_EQUALS) {
349
- operator_end = pos + 2;
350
- this.arena.set_attr_operator(node, ATTR_OPERATOR_TILDE_EQUAL);
351
- } else if (ch1 === CHAR_PIPE && ch2 === CHAR_EQUALS) {
352
- operator_end = pos + 2;
353
- this.arena.set_attr_operator(node, ATTR_OPERATOR_PIPE_EQUAL);
354
- } else if (ch1 === CHAR_CARET && ch2 === CHAR_EQUALS) {
355
- operator_end = pos + 2;
356
- this.arena.set_attr_operator(node, ATTR_OPERATOR_CARET_EQUAL);
357
- } else if (ch1 === CHAR_DOLLAR && ch2 === CHAR_EQUALS) {
358
- operator_end = pos + 2;
359
- this.arena.set_attr_operator(node, ATTR_OPERATOR_DOLLAR_EQUAL);
360
- } else if (ch1 === CHAR_ASTERISK && ch2 === CHAR_EQUALS) {
361
- operator_end = pos + 2;
362
- this.arena.set_attr_operator(node, ATTR_OPERATOR_STAR_EQUAL);
363
- } else {
364
- this.arena.set_attr_operator(node, ATTR_OPERATOR_NONE);
365
- this.arena.set_attr_flags(node, ATTR_FLAG_NONE);
366
- return;
367
- }
368
- pos = skip_whitespace_and_comments_forward(this.source, operator_end, end);
369
- if (pos >= end) {
370
- this.arena.set_attr_flags(node, ATTR_FLAG_NONE);
371
- return;
372
- }
373
- value_start = pos;
374
- let ch = this.source.charCodeAt(pos);
375
- if (ch === CHAR_SINGLE_QUOTE || ch === CHAR_DOUBLE_QUOTE) {
376
- let quote = ch;
377
- value_start = pos;
378
- pos++;
379
- while (pos < end) {
380
- let c = this.source.charCodeAt(pos);
381
- if (c === quote) {
382
- pos++;
383
- break;
384
- }
385
- if (c === 92) {
386
- pos += 2;
387
- } else {
388
- pos++;
389
- }
390
- }
391
- value_end = pos;
392
- } else {
393
- while (pos < end) {
394
- let c = this.source.charCodeAt(pos);
395
- if (is_whitespace(c)) {
396
- break;
397
- }
398
- pos++;
399
- }
400
- value_end = pos;
401
- }
402
- if (value_end > value_start) {
403
- this.arena.set_value_start_delta(node, value_start - this.arena.get_start_offset(node));
404
- this.arena.set_value_length(node, value_end - value_start);
405
- }
406
- pos = skip_whitespace_and_comments_forward(this.source, value_end, end);
407
- if (pos < end) {
408
- let flag_ch = this.source.charCodeAt(pos);
409
- if (flag_ch === 105 || flag_ch === 73) {
410
- this.arena.set_attr_flags(node, ATTR_FLAG_CASE_INSENSITIVE);
411
- } else if (flag_ch === 115 || flag_ch === 83) {
412
- this.arena.set_attr_flags(node, ATTR_FLAG_CASE_SENSITIVE);
413
- } else {
414
- this.arena.set_attr_flags(node, ATTR_FLAG_NONE);
415
- }
416
- } else {
417
- this.arena.set_attr_flags(node, ATTR_FLAG_NONE);
418
- }
419
- }
420
- // Parse pseudo-class or pseudo-element (:hover, ::before)
421
- parse_pseudo(start) {
422
- const saved = this.lexer.save_position();
423
- const saved_ws = this.lexer.save_position();
424
- this.skip_whitespace();
425
- let is_pseudo_element = false;
426
- if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_COLON) {
427
- is_pseudo_element = true;
428
- this.lexer.pos++;
429
- } else {
430
- this.lexer.restore_position(saved_ws);
431
- }
432
- this.lexer.next_token_fast(false);
433
- let token_type = this.lexer.token_type;
434
- if (token_type === TOKEN_IDENT) {
435
- let node = this.create_node(is_pseudo_element ? PSEUDO_ELEMENT_SELECTOR : PSEUDO_CLASS_SELECTOR, start, this.lexer.token_end);
436
- this.arena.set_content_start_delta(node, this.lexer.token_start - start);
437
- this.arena.set_content_length(node, this.lexer.token_end - this.lexer.token_start);
438
- return node;
439
- } else if (token_type === TOKEN_FUNCTION) {
440
- return this.parse_pseudo_function_after_colon(start, is_pseudo_element);
441
- }
442
- this.lexer.restore_position(saved);
443
- return null;
444
- }
445
- // Parse pseudo-class function (:nth-child(), :is(), etc.)
446
- parse_pseudo_function(_start, _end) {
447
- return null;
448
- }
449
- // Parse pseudo-class function after we've seen the colon
450
- parse_pseudo_function_after_colon(start, is_pseudo_element) {
451
- let func_name_start = this.lexer.token_start;
452
- let func_name_end = this.lexer.token_end - 1;
453
- let content_start = this.lexer.pos;
454
- let content_end = content_start;
455
- let paren_depth = 1;
456
- let end = this.lexer.token_end;
457
- while (this.lexer.pos < this.selector_end && paren_depth > 0) {
458
- this.lexer.next_token_fast(false);
459
- let token_type = this.lexer.token_type;
460
- if (token_type === TOKEN_LEFT_PAREN || token_type === TOKEN_FUNCTION) {
461
- paren_depth++;
462
- } else if (token_type === TOKEN_RIGHT_PAREN) {
463
- paren_depth--;
464
- if (paren_depth === 0) {
465
- content_end = this.lexer.token_start;
466
- end = this.lexer.token_end;
467
- break;
468
- }
469
- }
470
- }
471
- let node = this.create_node(is_pseudo_element ? PSEUDO_ELEMENT_SELECTOR : PSEUDO_CLASS_SELECTOR, start, end);
472
- this.arena.set_content_start_delta(node, func_name_start - start);
473
- this.arena.set_content_length(node, func_name_end - func_name_start);
474
- this.arena.set_flag(node, FLAG_HAS_PARENS);
475
- if (content_end > content_start) {
476
- let func_name_substr = this.source.substring(func_name_start, func_name_end);
477
- if (this.is_nth_pseudo(func_name_substr)) {
478
- let child = this.parse_nth_expression(content_start, content_end);
479
- if (child !== null) {
480
- this.arena.set_first_child(node, child);
481
- }
482
- } else if (str_equals("lang", func_name_substr)) {
483
- this.parse_lang_identifiers(content_start, content_end, node);
484
- } else {
485
- let saved_selector_end = this.selector_end;
486
- const saved = this.lexer.save_position();
487
- let allow_relative = str_equals("has", func_name_substr);
488
- let child_selector = this.parse_selector(content_start, content_end, this.lexer.line, this.lexer.column, allow_relative);
489
- this.selector_end = saved_selector_end;
490
- this.lexer.restore_position(saved);
491
- if (child_selector !== null) {
492
- this.arena.set_first_child(node, child_selector);
493
- }
494
- }
495
- }
496
- return node;
497
- }
498
- // Check if pseudo-class name is an nth-* pseudo
499
- is_nth_pseudo(name) {
500
- return str_equals("nth-child", name) || str_equals("nth-last-child", name) || str_equals("nth-of-type", name) || str_equals("nth-last-of-type", name) || str_equals("nth-col", name) || str_equals("nth-last-col", name);
501
- }
502
- // Parse :lang() content - comma-separated language identifiers
503
- // Accepts both quoted strings: :lang("en", "fr") and unquoted: :lang(en, fr)
504
- parse_lang_identifiers(start, end, parent_node) {
505
- let temp_lexer = new Lexer(this.source);
506
- temp_lexer.seek(start, this.lexer.line, this.lexer.column);
507
- let saved_selector_end = this.selector_end;
508
- let saved_lexer = this.lexer;
509
- this.lexer = temp_lexer;
510
- this.selector_end = end;
511
- let first_child = null;
512
- let last_child = null;
513
- while (this.lexer.pos < end) {
514
- this.lexer.next_token_fast(false);
515
- let token_type = this.lexer.token_type;
516
- let token_start = this.lexer.token_start;
517
- let token_end = this.lexer.token_end;
518
- if (token_type === TOKEN_WHITESPACE) {
519
- continue;
520
- }
521
- if (token_type === TOKEN_COMMA) {
522
- continue;
523
- }
524
- if (token_type === TOKEN_STRING || token_type === TOKEN_IDENT) {
525
- let lang_node = this.create_node(LANG_SELECTOR, token_start, token_end);
526
- if (first_child === null) {
527
- first_child = lang_node;
528
- }
529
- if (last_child !== null) {
530
- this.arena.set_next_sibling(last_child, lang_node);
531
- }
532
- last_child = lang_node;
533
- }
534
- if (this.lexer.pos >= end) {
535
- break;
536
- }
537
- }
538
- if (first_child !== null) {
539
- this.arena.set_first_child(parent_node, first_child);
540
- }
541
- this.selector_end = saved_selector_end;
542
- this.lexer = saved_lexer;
543
- }
544
- // Parse An+B expression for nth-* pseudo-classes
545
- // Handles both simple An+B and "An+B of S" syntax
546
- parse_nth_expression(start, end) {
547
- let of_index = this.find_of_keyword(start, end);
548
- if (of_index !== -1) {
549
- let anplusb_parser = new ANplusBParser(this.arena, this.source);
550
- let anplusb_node = anplusb_parser.parse_anplusb(start, of_index, this.lexer.line);
551
- let selector_start = of_index + 2;
552
- selector_start = skip_whitespace_forward(this.source, selector_start, end);
553
- let saved_selector_end = this.selector_end;
554
- const saved = this.lexer.save_position();
555
- this.selector_end = end;
556
- this.lexer.pos = selector_start;
557
- let selector_list = this.parse_selector_list();
558
- this.selector_end = saved_selector_end;
559
- this.lexer.restore_position(saved);
560
- let of_node = this.arena.create_node(NTH_OF_SELECTOR, start, end - start, this.lexer.line, 1);
561
- if (anplusb_node !== null && selector_list !== null) {
562
- this.arena.set_first_child(of_node, anplusb_node);
563
- this.arena.set_next_sibling(anplusb_node, selector_list);
564
- } else if (anplusb_node !== null) {
565
- this.arena.set_first_child(of_node, anplusb_node);
566
- }
567
- return of_node;
568
- } else {
569
- let anplusb_parser = new ANplusBParser(this.arena, this.source);
570
- return anplusb_parser.parse_anplusb(start, end, this.lexer.line);
571
- }
572
- }
573
- // Find the position of standalone "of" keyword (case-insensitive)
574
- find_of_keyword(start, end) {
575
- let i = start;
576
- while (i < end - 1) {
577
- i = skip_whitespace_and_comments_forward(this.source, i, end);
578
- if (i >= end - 1) break;
579
- let ch1 = this.source.charCodeAt(i);
580
- let ch2 = this.source.charCodeAt(i + 1);
581
- if ((ch1 === 111 || ch1 === 79) && (ch2 === 102 || ch2 === 70)) {
582
- let before_ok = i === start || is_whitespace(this.source.charCodeAt(i - 1));
583
- let after_ok = i + 2 >= end || is_whitespace(this.source.charCodeAt(i + 2));
584
- if (before_ok && after_ok) {
585
- return i;
586
- }
587
- }
588
- i++;
589
- }
590
- return -1;
591
- }
592
- create_node(type, start, end) {
593
- let node = this.arena.create_node(type, start, end - start, this.lexer.token_line, this.lexer.token_column);
594
- this.arena.set_content_start_delta(node, 0);
595
- this.arena.set_content_length(node, end - start);
596
- return node;
597
- }
598
- create_node_at(type, start, end, line, column) {
599
- let node = this.arena.create_node(type, start, end - start, line, column);
600
- this.arena.set_content_start_delta(node, 0);
601
- this.arena.set_content_length(node, end - start);
602
- return node;
603
- }
604
- // Helper to skip whitespace and comments, updating line/column
605
- skip_whitespace() {
606
- while (this.lexer.pos < this.selector_end) {
607
- let ch = this.source.charCodeAt(this.lexer.pos);
608
- if (is_whitespace(ch)) {
609
- this.lexer.advance();
610
- continue;
611
- }
612
- if (ch === CHAR_FORWARD_SLASH && this.lexer.pos + 1 < this.selector_end && this.source.charCodeAt(this.lexer.pos + 1) === CHAR_ASTERISK) {
613
- this.lexer.advance(2);
614
- while (this.lexer.pos < this.selector_end) {
615
- if (this.source.charCodeAt(this.lexer.pos) === CHAR_ASTERISK && this.lexer.pos + 1 < this.selector_end && this.source.charCodeAt(this.lexer.pos + 1) === CHAR_FORWARD_SLASH) {
616
- this.lexer.advance(2);
617
- break;
618
- }
619
- this.lexer.advance();
620
- }
621
- continue;
622
- }
623
- break;
624
- }
625
- }
626
- }
1
+ import { t as Lexer } from "./tokenize-BQFB1jXg.js";
2
+ import { C as CSSDataArena, r as CSSNode } from "./css-node-Uj4oBgaw.js";
3
+ import { a as is_whitespace, n as is_combinator, o as str_equals } from "./parse-dimension-CCn_XRDe.js";
4
+ import { n as skip_whitespace_and_comments_forward, r as skip_whitespace_forward, t as skip_whitespace_and_comments_backward } from "./parse-utils-DnsZRpfd.js";
5
+ import { ANplusBParser } from "./parse-anplusb.js";
6
+ //#region src/parse-selector.ts
7
+ /** @internal */
8
+ var SelectorParser = class {
9
+ lexer;
10
+ arena;
11
+ source;
12
+ selector_end;
13
+ constructor(arena, source) {
14
+ this.arena = arena;
15
+ this.source = source;
16
+ this.lexer = new Lexer(source);
17
+ this.selector_end = 0;
18
+ }
19
+ parse_selector(start, end, line = 1, column = 1, allow_relative = true) {
20
+ this.selector_end = end;
21
+ this.lexer.seek(start, line, column);
22
+ return this.parse_selector_list(allow_relative);
23
+ }
24
+ parse_selector_list(allow_relative = true) {
25
+ let selectors = [];
26
+ let list_start = this.lexer.pos;
27
+ let list_line = this.lexer.line;
28
+ let list_column = this.lexer.column;
29
+ while (this.lexer.pos < this.selector_end) {
30
+ let selector_start = this.lexer.pos;
31
+ let selector_line = this.lexer.line;
32
+ let selector_column = this.lexer.column;
33
+ let complex_selector = this.parse_complex_selector(allow_relative);
34
+ if (complex_selector !== null) {
35
+ let selector_wrapper = this.arena.create_node(5, selector_start, this.lexer.pos - selector_start, selector_line, selector_column);
36
+ this.arena.set_content_start_delta(selector_wrapper, 0);
37
+ this.arena.set_content_length(selector_wrapper, this.lexer.pos - selector_start);
38
+ let last_component = complex_selector;
39
+ let next_sibling = this.arena.get_next_sibling(last_component);
40
+ while (next_sibling !== 0) {
41
+ last_component = next_sibling;
42
+ next_sibling = this.arena.get_next_sibling(last_component);
43
+ }
44
+ this.arena.set_first_child(selector_wrapper, complex_selector);
45
+ selectors.push(selector_wrapper);
46
+ }
47
+ this.skip_whitespace();
48
+ if (this.lexer.pos >= this.selector_end) break;
49
+ this.lexer.next_token_fast(false);
50
+ if (this.lexer.token_type === 18) {
51
+ this.skip_whitespace();
52
+ continue;
53
+ } else break;
54
+ }
55
+ if (selectors.length >= 1) {
56
+ let list_node = this.arena.create_node(20, list_start, this.lexer.pos - list_start, list_line, list_column);
57
+ this.arena.append_children(list_node, selectors);
58
+ return list_node;
59
+ }
60
+ return null;
61
+ }
62
+ parse_complex_selector(allow_relative = true) {
63
+ let components = [];
64
+ this.skip_whitespace();
65
+ if (allow_relative && this.lexer.pos < this.selector_end) {
66
+ const saved = this.lexer.save_position();
67
+ this.lexer.next_token_fast(false);
68
+ if (this.lexer.token_type === 9) {
69
+ let ch = this.source.charCodeAt(this.lexer.token_start);
70
+ if (ch === 62 || ch === 43 || ch === 126) {
71
+ let combinator = this.create_node_at(27, this.lexer.token_start, this.lexer.token_end, this.lexer.token_line, this.lexer.token_column);
72
+ components.push(combinator);
73
+ this.skip_whitespace();
74
+ } else this.lexer.restore_position(saved);
75
+ } else this.lexer.restore_position(saved);
76
+ }
77
+ while (this.lexer.pos < this.selector_end) {
78
+ let compound = this.parse_compound_selector();
79
+ if (compound !== null) components.push(compound);
80
+ else break;
81
+ let combinator = this.try_parse_combinator();
82
+ if (combinator !== null) {
83
+ components.push(combinator);
84
+ this.skip_whitespace();
85
+ continue;
86
+ }
87
+ const saved = this.lexer.save_position();
88
+ this.skip_whitespace();
89
+ if (this.lexer.pos >= this.selector_end) {
90
+ this.lexer.restore_position(saved);
91
+ break;
92
+ }
93
+ this.lexer.next_token_fast(false);
94
+ if (this.lexer.token_type === 18 || this.lexer.pos >= this.selector_end) {
95
+ this.lexer.restore_position(saved);
96
+ break;
97
+ }
98
+ this.lexer.restore_position(saved);
99
+ break;
100
+ }
101
+ if (components.length === 0) return null;
102
+ for (let i = 0; i < components.length - 1; i++) {
103
+ let last_node = components[i];
104
+ while (this.arena.get_next_sibling(last_node) !== 0) last_node = this.arena.get_next_sibling(last_node);
105
+ this.arena.set_next_sibling(last_node, components[i + 1]);
106
+ }
107
+ return components[0];
108
+ }
109
+ parse_compound_selector() {
110
+ let parts = [];
111
+ while (this.lexer.pos < this.selector_end) {
112
+ const saved = this.lexer.save_position();
113
+ this.lexer.next_token_fast(false);
114
+ if (this.lexer.token_start >= this.selector_end) break;
115
+ if (this.lexer.token_type === 26) break;
116
+ let part = this.parse_simple_selector();
117
+ if (part !== null) parts.push(part);
118
+ else {
119
+ this.lexer.restore_position(saved);
120
+ break;
121
+ }
122
+ }
123
+ if (parts.length === 0) return null;
124
+ for (let i = 0; i < parts.length - 1; i++) this.arena.set_next_sibling(parts[i], parts[i + 1]);
125
+ return parts[0];
126
+ }
127
+ parse_simple_selector() {
128
+ let token_type = this.lexer.token_type;
129
+ let start = this.lexer.token_start;
130
+ let end = this.lexer.token_end;
131
+ switch (token_type) {
132
+ case 1: return this.parse_type_or_namespace_selector(start, end);
133
+ case 4: return this.create_node(23, start, end);
134
+ case 9:
135
+ let ch = this.source.charCodeAt(start);
136
+ if (ch === 46) return this.parse_class_selector(start);
137
+ else if (ch === 42) return this.parse_universal_or_namespace_selector(start, end);
138
+ else if (ch === 38) return this.create_node(29, start, end);
139
+ else if (ch === 124) return this.parse_empty_namespace_selector(start);
140
+ return null;
141
+ case 19: return this.parse_attribute_selector(start);
142
+ case 16: return this.parse_pseudo(start);
143
+ case 2: return this.parse_pseudo_function(start, end);
144
+ case 11: return this.create_node(12, start, end);
145
+ case 13:
146
+ case 18: return null;
147
+ default: return null;
148
+ }
149
+ }
150
+ parse_namespace_local_part(selector_start, namespace_start, namespace_length) {
151
+ const saved = this.lexer.save_position();
152
+ this.lexer.next_token_fast(false);
153
+ let node_type;
154
+ if (this.lexer.token_type === 1) node_type = 21;
155
+ else if (this.lexer.token_type === 9 && this.source.charCodeAt(this.lexer.token_start) === 42) node_type = 28;
156
+ else {
157
+ this.lexer.restore_position(saved);
158
+ return null;
159
+ }
160
+ let node = this.create_node(node_type, selector_start, this.lexer.token_end);
161
+ this.arena.set_content_start_delta(node, namespace_start - selector_start);
162
+ this.arena.set_content_length(node, namespace_length);
163
+ return node;
164
+ }
165
+ parse_type_or_namespace_selector(start, end) {
166
+ const saved = this.lexer.save_position();
167
+ this.skip_whitespace();
168
+ if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === 124) {
169
+ this.lexer.pos++;
170
+ let node = this.parse_namespace_local_part(start, start, end - start);
171
+ if (node !== null) return node;
172
+ this.lexer.restore_position(saved);
173
+ } else this.lexer.restore_position(saved);
174
+ return this.create_node(21, start, end);
175
+ }
176
+ parse_universal_or_namespace_selector(start, end) {
177
+ const saved = this.lexer.save_position();
178
+ this.skip_whitespace();
179
+ if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === 124) {
180
+ this.lexer.pos++;
181
+ let node = this.parse_namespace_local_part(start, start, end - start);
182
+ if (node !== null) return node;
183
+ this.lexer.restore_position(saved);
184
+ } else this.lexer.restore_position(saved);
185
+ return this.create_node(28, start, end);
186
+ }
187
+ parse_empty_namespace_selector(start) {
188
+ return this.parse_namespace_local_part(start, start, 1);
189
+ }
190
+ try_parse_combinator() {
191
+ const saved_whitespace_start = this.lexer.save_position();
192
+ let has_whitespace = this.lexer.pos < this.selector_end;
193
+ this.skip_whitespace();
194
+ has_whitespace = has_whitespace && this.lexer.pos > saved_whitespace_start.pos;
195
+ if (this.lexer.pos >= this.selector_end) {
196
+ this.lexer.restore_position(saved_whitespace_start);
197
+ return null;
198
+ }
199
+ this.lexer.next_token_fast(false);
200
+ if (this.lexer.token_type === 9) {
201
+ if (is_combinator(this.source.charCodeAt(this.lexer.token_start))) return this.create_node_at(27, this.lexer.token_start, this.lexer.token_end, this.lexer.token_line, this.lexer.token_column);
202
+ }
203
+ if (has_whitespace) {
204
+ this.lexer.restore_position(saved_whitespace_start);
205
+ this.skip_whitespace();
206
+ return this.create_node_at(27, saved_whitespace_start.pos, this.lexer.pos, saved_whitespace_start.line, saved_whitespace_start.column);
207
+ }
208
+ this.lexer.restore_position(saved_whitespace_start);
209
+ return null;
210
+ }
211
+ parse_class_selector(dot_pos) {
212
+ const saved = this.lexer.save_position();
213
+ this.lexer.next_token_fast(false);
214
+ if (this.lexer.token_type !== 1) {
215
+ this.lexer.restore_position(saved);
216
+ return null;
217
+ }
218
+ return this.create_node(22, dot_pos, this.lexer.token_end);
219
+ }
220
+ parse_attribute_selector(start) {
221
+ let bracket_depth = 1;
222
+ let end = this.lexer.token_end;
223
+ let content_start = start + 1;
224
+ let content_end = content_start;
225
+ while (this.lexer.pos < this.selector_end && bracket_depth > 0) {
226
+ this.lexer.next_token_fast(false);
227
+ let token_type = this.lexer.token_type;
228
+ if (token_type === 19) bracket_depth++;
229
+ else if (token_type === 20) {
230
+ bracket_depth--;
231
+ if (bracket_depth === 0) {
232
+ content_end = this.lexer.token_start;
233
+ end = this.lexer.token_end;
234
+ break;
235
+ }
236
+ }
237
+ }
238
+ let node = this.create_node(24, start, end);
239
+ this.parse_attribute_content(node, content_start, content_end);
240
+ return node;
241
+ }
242
+ parse_attribute_content(node, start, end) {
243
+ start = skip_whitespace_and_comments_forward(this.source, start, end);
244
+ end = skip_whitespace_and_comments_backward(this.source, end, start);
245
+ if (start >= end) return;
246
+ let name_start = start;
247
+ let name_end = start;
248
+ let operator_end = -1;
249
+ let value_start = -1;
250
+ let value_end = -1;
251
+ while (name_end < end) {
252
+ let ch = this.source.charCodeAt(name_end);
253
+ if (is_whitespace(ch) || ch === 61 || ch === 126 || ch === 124 || ch === 94 || ch === 36 || ch === 42) break;
254
+ name_end++;
255
+ }
256
+ if (name_end > name_start) {
257
+ this.arena.set_content_start_delta(node, name_start - this.arena.get_start_offset(node));
258
+ this.arena.set_content_length(node, name_end - name_start);
259
+ }
260
+ let pos = skip_whitespace_and_comments_forward(this.source, name_end, end);
261
+ if (pos >= end) {
262
+ this.arena.set_attr_operator(node, 0);
263
+ this.arena.set_attr_flags(node, 0);
264
+ return;
265
+ }
266
+ let ch1 = this.source.charCodeAt(pos);
267
+ let ch2 = pos + 1 < end ? this.source.charCodeAt(pos + 1) : 0;
268
+ if (ch1 === 61) {
269
+ operator_end = pos + 1;
270
+ this.arena.set_attr_operator(node, 1);
271
+ } else if (ch1 === 126 && ch2 === 61) {
272
+ operator_end = pos + 2;
273
+ this.arena.set_attr_operator(node, 2);
274
+ } else if (ch1 === 124 && ch2 === 61) {
275
+ operator_end = pos + 2;
276
+ this.arena.set_attr_operator(node, 3);
277
+ } else if (ch1 === 94 && ch2 === 61) {
278
+ operator_end = pos + 2;
279
+ this.arena.set_attr_operator(node, 4);
280
+ } else if (ch1 === 36 && ch2 === 61) {
281
+ operator_end = pos + 2;
282
+ this.arena.set_attr_operator(node, 5);
283
+ } else if (ch1 === 42 && ch2 === 61) {
284
+ operator_end = pos + 2;
285
+ this.arena.set_attr_operator(node, 6);
286
+ } else {
287
+ this.arena.set_attr_operator(node, 0);
288
+ this.arena.set_attr_flags(node, 0);
289
+ return;
290
+ }
291
+ pos = skip_whitespace_and_comments_forward(this.source, operator_end, end);
292
+ if (pos >= end) {
293
+ this.arena.set_attr_flags(node, 0);
294
+ return;
295
+ }
296
+ value_start = pos;
297
+ let ch = this.source.charCodeAt(pos);
298
+ if (ch === 39 || ch === 34) {
299
+ let quote = ch;
300
+ value_start = pos;
301
+ pos++;
302
+ while (pos < end) {
303
+ let c = this.source.charCodeAt(pos);
304
+ if (c === quote) {
305
+ pos++;
306
+ break;
307
+ }
308
+ if (c === 92) pos += 2;
309
+ else pos++;
310
+ }
311
+ value_end = pos;
312
+ } else {
313
+ while (pos < end) {
314
+ if (is_whitespace(this.source.charCodeAt(pos))) break;
315
+ pos++;
316
+ }
317
+ value_end = pos;
318
+ }
319
+ if (value_end > value_start) {
320
+ this.arena.set_value_start_delta(node, value_start - this.arena.get_start_offset(node));
321
+ this.arena.set_value_length(node, value_end - value_start);
322
+ }
323
+ pos = skip_whitespace_and_comments_forward(this.source, value_end, end);
324
+ if (pos < end) {
325
+ let flag_ch = this.source.charCodeAt(pos);
326
+ if (flag_ch === 105 || flag_ch === 73) this.arena.set_attr_flags(node, 1);
327
+ else if (flag_ch === 115 || flag_ch === 83) this.arena.set_attr_flags(node, 2);
328
+ else this.arena.set_attr_flags(node, 0);
329
+ } else this.arena.set_attr_flags(node, 0);
330
+ }
331
+ parse_pseudo(start) {
332
+ const saved = this.lexer.save_position();
333
+ const saved_ws = this.lexer.save_position();
334
+ this.skip_whitespace();
335
+ let is_pseudo_element = false;
336
+ if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === 58) {
337
+ is_pseudo_element = true;
338
+ this.lexer.pos++;
339
+ } else this.lexer.restore_position(saved_ws);
340
+ this.lexer.next_token_fast(false);
341
+ let token_type = this.lexer.token_type;
342
+ if (token_type === 1) {
343
+ let node = this.create_node(is_pseudo_element ? 26 : 25, start, this.lexer.token_end);
344
+ this.arena.set_content_start_delta(node, this.lexer.token_start - start);
345
+ this.arena.set_content_length(node, this.lexer.token_end - this.lexer.token_start);
346
+ return node;
347
+ } else if (token_type === 2) return this.parse_pseudo_function_after_colon(start, is_pseudo_element);
348
+ this.lexer.restore_position(saved);
349
+ return null;
350
+ }
351
+ parse_pseudo_function(_start, _end) {
352
+ return null;
353
+ }
354
+ parse_pseudo_function_after_colon(start, is_pseudo_element) {
355
+ let func_name_start = this.lexer.token_start;
356
+ let func_name_end = this.lexer.token_end - 1;
357
+ let content_start = this.lexer.pos;
358
+ let content_end = content_start;
359
+ let paren_depth = 1;
360
+ let end = this.lexer.token_end;
361
+ while (this.lexer.pos < this.selector_end && paren_depth > 0) {
362
+ this.lexer.next_token_fast(false);
363
+ let token_type = this.lexer.token_type;
364
+ if (token_type === 21 || token_type === 2) paren_depth++;
365
+ else if (token_type === 22) {
366
+ paren_depth--;
367
+ if (paren_depth === 0) {
368
+ content_end = this.lexer.token_start;
369
+ end = this.lexer.token_end;
370
+ break;
371
+ }
372
+ }
373
+ }
374
+ let node = this.create_node(is_pseudo_element ? 26 : 25, start, end);
375
+ this.arena.set_content_start_delta(node, func_name_start - start);
376
+ this.arena.set_content_length(node, func_name_end - func_name_start);
377
+ this.arena.set_flag(node, 64);
378
+ if (content_end > content_start) {
379
+ let func_name_substr = this.source.substring(func_name_start, func_name_end);
380
+ if (this.is_nth_pseudo(func_name_substr)) {
381
+ let child = this.parse_nth_expression(content_start, content_end);
382
+ if (child !== null) this.arena.set_first_child(node, child);
383
+ } else if (str_equals("lang", func_name_substr)) this.parse_lang_identifiers(content_start, content_end, node);
384
+ else {
385
+ let saved_selector_end = this.selector_end;
386
+ const saved = this.lexer.save_position();
387
+ let allow_relative = str_equals("has", func_name_substr);
388
+ let child_selector = this.parse_selector(content_start, content_end, this.lexer.line, this.lexer.column, allow_relative);
389
+ this.selector_end = saved_selector_end;
390
+ this.lexer.restore_position(saved);
391
+ if (child_selector !== null) this.arena.set_first_child(node, child_selector);
392
+ }
393
+ }
394
+ return node;
395
+ }
396
+ is_nth_pseudo(name) {
397
+ return str_equals("nth-child", name) || str_equals("nth-last-child", name) || str_equals("nth-of-type", name) || str_equals("nth-last-of-type", name) || str_equals("nth-col", name) || str_equals("nth-last-col", name);
398
+ }
399
+ parse_lang_identifiers(start, end, parent_node) {
400
+ let temp_lexer = new Lexer(this.source);
401
+ temp_lexer.seek(start, this.lexer.line, this.lexer.column);
402
+ let saved_selector_end = this.selector_end;
403
+ let saved_lexer = this.lexer;
404
+ this.lexer = temp_lexer;
405
+ this.selector_end = end;
406
+ let first_child = null;
407
+ let last_child = null;
408
+ while (this.lexer.pos < end) {
409
+ this.lexer.next_token_fast(false);
410
+ let token_type = this.lexer.token_type;
411
+ let token_start = this.lexer.token_start;
412
+ let token_end = this.lexer.token_end;
413
+ if (token_type === 13) continue;
414
+ if (token_type === 18) continue;
415
+ if (token_type === 5 || token_type === 1) {
416
+ let lang_node = this.create_node(56, token_start, token_end);
417
+ if (first_child === null) first_child = lang_node;
418
+ if (last_child !== null) this.arena.set_next_sibling(last_child, lang_node);
419
+ last_child = lang_node;
420
+ }
421
+ if (this.lexer.pos >= end) break;
422
+ }
423
+ if (first_child !== null) this.arena.set_first_child(parent_node, first_child);
424
+ if (last_child !== null) {}
425
+ this.selector_end = saved_selector_end;
426
+ this.lexer = saved_lexer;
427
+ }
428
+ parse_nth_expression(start, end) {
429
+ let of_index = this.find_of_keyword(start, end);
430
+ if (of_index !== -1) {
431
+ let anplusb_node = new ANplusBParser(this.arena, this.source).parse_anplusb(start, of_index, this.lexer.line);
432
+ let selector_start = of_index + 2;
433
+ selector_start = skip_whitespace_forward(this.source, selector_start, end);
434
+ let saved_selector_end = this.selector_end;
435
+ const saved = this.lexer.save_position();
436
+ this.selector_end = end;
437
+ this.lexer.pos = selector_start;
438
+ let selector_list = this.parse_selector_list();
439
+ this.selector_end = saved_selector_end;
440
+ this.lexer.restore_position(saved);
441
+ let of_node = this.arena.create_node(31, start, end - start, this.lexer.line, 1);
442
+ if (anplusb_node !== null && selector_list !== null) {
443
+ this.arena.set_first_child(of_node, anplusb_node);
444
+ this.arena.set_next_sibling(anplusb_node, selector_list);
445
+ } else if (anplusb_node !== null) this.arena.set_first_child(of_node, anplusb_node);
446
+ return of_node;
447
+ } else return new ANplusBParser(this.arena, this.source).parse_anplusb(start, end, this.lexer.line);
448
+ }
449
+ find_of_keyword(start, end) {
450
+ let i = start;
451
+ while (i < end - 1) {
452
+ i = skip_whitespace_and_comments_forward(this.source, i, end);
453
+ if (i >= end - 1) break;
454
+ let ch1 = this.source.charCodeAt(i);
455
+ let ch2 = this.source.charCodeAt(i + 1);
456
+ if ((ch1 === 111 || ch1 === 79) && (ch2 === 102 || ch2 === 70)) {
457
+ let before_ok = i === start || is_whitespace(this.source.charCodeAt(i - 1));
458
+ let after_ok = i + 2 >= end || is_whitespace(this.source.charCodeAt(i + 2));
459
+ if (before_ok && after_ok) return i;
460
+ }
461
+ i++;
462
+ }
463
+ return -1;
464
+ }
465
+ create_node(type, start, end) {
466
+ let node = this.arena.create_node(type, start, end - start, this.lexer.token_line, this.lexer.token_column);
467
+ this.arena.set_content_start_delta(node, 0);
468
+ this.arena.set_content_length(node, end - start);
469
+ return node;
470
+ }
471
+ create_node_at(type, start, end, line, column) {
472
+ let node = this.arena.create_node(type, start, end - start, line, column);
473
+ this.arena.set_content_start_delta(node, 0);
474
+ this.arena.set_content_length(node, end - start);
475
+ return node;
476
+ }
477
+ skip_whitespace() {
478
+ while (this.lexer.pos < this.selector_end) {
479
+ let ch = this.source.charCodeAt(this.lexer.pos);
480
+ if (is_whitespace(ch)) {
481
+ this.lexer.advance();
482
+ continue;
483
+ }
484
+ if (ch === 47 && this.lexer.pos + 1 < this.selector_end && this.source.charCodeAt(this.lexer.pos + 1) === 42) {
485
+ this.lexer.advance(2);
486
+ while (this.lexer.pos < this.selector_end) {
487
+ if (this.source.charCodeAt(this.lexer.pos) === 42 && this.lexer.pos + 1 < this.selector_end && this.source.charCodeAt(this.lexer.pos + 1) === 47) {
488
+ this.lexer.advance(2);
489
+ break;
490
+ }
491
+ this.lexer.advance();
492
+ }
493
+ continue;
494
+ }
495
+ break;
496
+ }
497
+ }
498
+ };
499
+ /**
500
+ * Parse a CSS selector string and return an AST
501
+ * @param source - The CSS selector to parse (e.g., "div.class > p#id")
502
+ * @returns The root CSSNode of the selector AST
503
+ */
627
504
  function parse_selector(source) {
628
- const arena = new CSSDataArena(CSSDataArena.capacity_for_source(source.length));
629
- const selector_parser = new SelectorParser(arena, source);
630
- const selector_index = selector_parser.parse_selector(0, source.length);
631
- if (selector_index === null) {
632
- const empty = arena.create_node(SELECTOR_LIST, 0, 0, 1, 1);
633
- return new CSSNode(arena, source, empty);
634
- }
635
- return new CSSNode(arena, source, selector_index);
505
+ const arena = new CSSDataArena(CSSDataArena.capacity_for_source(source.length));
506
+ const selector_index = new SelectorParser(arena, source).parse_selector(0, source.length);
507
+ if (selector_index === null) return new CSSNode(arena, source, arena.create_node(20, 0, 0, 1, 1));
508
+ return new CSSNode(arena, source, selector_index);
636
509
  }
637
-
510
+ //#endregion
638
511
  export { SelectorParser, parse_selector };