@projectwallace/css-parser 0.6.7 → 0.7.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.
@@ -1,6 +1,10 @@
1
- import { L as Lexer, q as TOKEN_COMMA, h as TOKEN_DELIM, y as TOKEN_EOF, l as TOKEN_WHITESPACE, a as TOKEN_FUNCTION, o as TOKEN_COLON, r as TOKEN_LEFT_BRACKET, c as TOKEN_HASH, T as TOKEN_IDENT, s as TOKEN_RIGHT_BRACKET, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN, d as TOKEN_STRING } from './lexer-CtBKgfVv.js';
2
- import { Z as CSSDataArena, w as NODE_SELECTOR_LIST, C as CSSNode, m as NODE_SELECTOR, a4 as CHAR_GREATER_THAN, a5 as CHAR_PLUS, a6 as CHAR_TILDE, F as NODE_SELECTOR_COMBINATOR, a7 as CHAR_PERIOD, a8 as CHAR_ASTERISK, a9 as CHAR_AMPERSAND, H as NODE_SELECTOR_NESTING, aa as CHAR_PIPE, z as NODE_SELECTOR_ID, x as NODE_SELECTOR_TYPE, G as NODE_SELECTOR_UNIVERSAL, ab as is_whitespace, ac as is_combinator, y as NODE_SELECTOR_CLASS, B as NODE_SELECTOR_ATTRIBUTE, ad as skip_whitespace_and_comments_forward, ae as skip_whitespace_and_comments_backward, af as CHAR_EQUALS, ag as CHAR_CARET, ah as CHAR_DOLLAR, A as ATTR_OPERATOR_NONE, g as ATTR_FLAG_NONE, a as ATTR_OPERATOR_EQUAL, b as ATTR_OPERATOR_TILDE_EQUAL, c as ATTR_OPERATOR_PIPE_EQUAL, d as ATTR_OPERATOR_CARET_EQUAL, e as ATTR_OPERATOR_DOLLAR_EQUAL, f as ATTR_OPERATOR_STAR_EQUAL, ai as CHAR_SINGLE_QUOTE, aj as CHAR_DOUBLE_QUOTE, h as ATTR_FLAG_CASE_INSENSITIVE, i as ATTR_FLAG_CASE_SENSITIVE, ak as CHAR_COLON, E as NODE_SELECTOR_PSEUDO_ELEMENT, D as NODE_SELECTOR_PSEUDO_CLASS, a1 as is_vendor_prefixed, a2 as FLAG_VENDOR_PREFIXED, al as FLAG_HAS_PARENS, K as NODE_SELECTOR_LANG, am as skip_whitespace_forward, J as NODE_SELECTOR_NTH_OF } from './css-node-GOEvp2OO.js';
1
+ import { Lexer } from './lexer.js';
2
+ import { CSSDataArena, SELECTOR_LIST, SELECTOR, COMBINATOR, 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_VENDOR_PREFIXED, FLAG_HAS_PARENS, LANG_SELECTOR, NTH_OF_SELECTOR } from './arena.js';
3
+ import { TOKEN_COMMA, TOKEN_DELIM, TOKEN_EOF, TOKEN_WHITESPACE, 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, CHAR_SPACE, CHAR_NEWLINE, CHAR_CARRIAGE_RETURN, CHAR_FORM_FEED, is_combinator, is_whitespace, CHAR_EQUALS, CHAR_CARET, CHAR_DOLLAR, CHAR_SINGLE_QUOTE, CHAR_DOUBLE_QUOTE, CHAR_COLON, is_vendor_prefixed } from './string-utils.js';
3
6
  import { ANplusBParser } from './parse-anplusb.js';
7
+ import { CSSNode } from './css-node.js';
4
8
 
5
9
  class SelectorParser {
6
10
  lexer;
@@ -32,10 +36,12 @@ class SelectorParser {
32
36
  let selector_start = this.lexer.pos;
33
37
  let complex_selector = this.parse_complex_selector(allow_relative);
34
38
  if (complex_selector !== null) {
35
- let selector_wrapper = this.create_node(NODE_SELECTOR, selector_start, this.lexer.pos);
39
+ let selector_wrapper = this.create_node(SELECTOR, selector_start, this.lexer.pos);
36
40
  let last_component = complex_selector;
37
- while (this.arena.get_next_sibling(last_component) !== 0) {
38
- last_component = this.arena.get_next_sibling(last_component);
41
+ let next_sibling = this.arena.get_next_sibling(last_component);
42
+ while (next_sibling !== 0) {
43
+ last_component = next_sibling;
44
+ next_sibling = this.arena.get_next_sibling(last_component);
39
45
  }
40
46
  this.arena.set_first_child(selector_wrapper, complex_selector);
41
47
  this.arena.set_last_child(selector_wrapper, last_component);
@@ -53,17 +59,14 @@ class SelectorParser {
53
59
  }
54
60
  }
55
61
  if (selectors.length >= 1) {
56
- let list_node = this.arena.create_node();
57
- this.arena.set_type(list_node, NODE_SELECTOR_LIST);
58
- this.arena.set_start_offset(list_node, list_start);
59
- this.arena.set_length(list_node, this.lexer.pos - list_start);
60
- this.arena.set_start_line(list_node, list_line);
61
- this.arena.set_start_column(list_node, list_column);
62
- this.arena.set_first_child(list_node, selectors[0]);
63
- this.arena.set_last_child(list_node, selectors[selectors.length - 1]);
64
- for (let i = 0; i < selectors.length - 1; i++) {
65
- this.arena.set_next_sibling(selectors[i], selectors[i + 1]);
66
- }
62
+ let list_node = this.arena.create_node(
63
+ SELECTOR_LIST,
64
+ list_start,
65
+ this.lexer.pos - list_start,
66
+ list_line,
67
+ list_column
68
+ );
69
+ this.arena.append_children(list_node, selectors);
67
70
  return list_node;
68
71
  }
69
72
  return null;
@@ -81,7 +84,7 @@ class SelectorParser {
81
84
  if (token_type === TOKEN_DELIM) {
82
85
  let ch = this.source.charCodeAt(this.lexer.token_start);
83
86
  if (ch === CHAR_GREATER_THAN || ch === CHAR_PLUS || ch === CHAR_TILDE) {
84
- let combinator = this.create_node(NODE_SELECTOR_COMBINATOR, this.lexer.token_start, this.lexer.token_end);
87
+ let combinator = this.create_node(COMBINATOR, this.lexer.token_start, this.lexer.token_end);
85
88
  components.push(combinator);
86
89
  this.skip_whitespace();
87
90
  } else {
@@ -92,7 +95,6 @@ class SelectorParser {
92
95
  }
93
96
  }
94
97
  while (this.lexer.pos < this.selector_end) {
95
- if (this.lexer.pos >= this.selector_end) break;
96
98
  let compound = this.parse_compound_selector();
97
99
  if (compound !== null) {
98
100
  components.push(compound);
@@ -160,7 +162,7 @@ class SelectorParser {
160
162
  case TOKEN_IDENT:
161
163
  return this.parse_type_or_namespace_selector(start, end);
162
164
  case TOKEN_HASH:
163
- return this.create_node(NODE_SELECTOR_ID, start, end);
165
+ return this.create_node(ID_SELECTOR, start, end);
164
166
  case TOKEN_DELIM:
165
167
  let ch = this.source.charCodeAt(start);
166
168
  if (ch === CHAR_PERIOD) {
@@ -168,7 +170,7 @@ class SelectorParser {
168
170
  } else if (ch === CHAR_ASTERISK) {
169
171
  return this.parse_universal_or_namespace_selector(start, end);
170
172
  } else if (ch === CHAR_AMPERSAND) {
171
- return this.create_node(NODE_SELECTOR_NESTING, start, end);
173
+ return this.create_node(NESTING_SELECTOR, start, end);
172
174
  } else if (ch === CHAR_PIPE) {
173
175
  return this.parse_empty_namespace_selector(start);
174
176
  }
@@ -193,15 +195,15 @@ class SelectorParser {
193
195
  this.lexer.next_token_fast(false);
194
196
  let node_type;
195
197
  if (this.lexer.token_type === TOKEN_IDENT) {
196
- node_type = NODE_SELECTOR_TYPE;
198
+ node_type = TYPE_SELECTOR;
197
199
  } else if (this.lexer.token_type === TOKEN_DELIM && this.source.charCodeAt(this.lexer.token_start) === CHAR_ASTERISK) {
198
- node_type = NODE_SELECTOR_UNIVERSAL;
200
+ node_type = UNIVERSAL_SELECTOR;
199
201
  } else {
200
202
  this.lexer.restore_position(saved);
201
203
  return null;
202
204
  }
203
205
  let node = this.create_node(node_type, selector_start, this.lexer.token_end);
204
- this.arena.set_content_start(node, namespace_start);
206
+ this.arena.set_content_start_delta(node, namespace_start - selector_start);
205
207
  this.arena.set_content_length(node, namespace_length);
206
208
  return node;
207
209
  }
@@ -214,7 +216,7 @@ class SelectorParser {
214
216
  if (node !== null) return node;
215
217
  this.lexer.pos = end;
216
218
  }
217
- return this.create_node(NODE_SELECTOR_TYPE, start, end);
219
+ return this.create_node(TYPE_SELECTOR, start, end);
218
220
  }
219
221
  // Parse universal selector or namespace selector (*|E or *|*)
220
222
  // Called when we've seen a * DELIM token
@@ -225,7 +227,7 @@ class SelectorParser {
225
227
  if (node !== null) return node;
226
228
  this.lexer.pos = end;
227
229
  }
228
- return this.create_node(NODE_SELECTOR_UNIVERSAL, start, end);
230
+ return this.create_node(UNIVERSAL_SELECTOR, start, end);
229
231
  }
230
232
  // Parse empty namespace selector (|E or |*)
231
233
  // Called when we've seen a | DELIM token at the start
@@ -238,7 +240,7 @@ class SelectorParser {
238
240
  let has_whitespace = false;
239
241
  while (this.lexer.pos < this.selector_end) {
240
242
  let ch = this.source.charCodeAt(this.lexer.pos);
241
- if (is_whitespace(ch)) {
243
+ if (ch === CHAR_SPACE || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
242
244
  has_whitespace = true;
243
245
  this.lexer.pos++;
244
246
  } else {
@@ -250,20 +252,20 @@ class SelectorParser {
250
252
  if (this.lexer.token_type === TOKEN_DELIM) {
251
253
  let ch = this.source.charCodeAt(this.lexer.token_start);
252
254
  if (is_combinator(ch)) {
253
- return this.create_node(NODE_SELECTOR_COMBINATOR, this.lexer.token_start, this.lexer.token_end);
255
+ return this.create_node(COMBINATOR, this.lexer.token_start, this.lexer.token_end);
254
256
  }
255
257
  }
256
258
  if (has_whitespace) {
257
259
  this.lexer.pos = whitespace_start;
258
260
  while (this.lexer.pos < this.selector_end) {
259
261
  let ch = this.source.charCodeAt(this.lexer.pos);
260
- if (is_whitespace(ch)) {
262
+ if (ch === CHAR_SPACE || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
261
263
  this.lexer.pos++;
262
264
  } else {
263
265
  break;
264
266
  }
265
267
  }
266
- return this.create_node(NODE_SELECTOR_COMBINATOR, whitespace_start, this.lexer.pos);
268
+ return this.create_node(COMBINATOR, whitespace_start, this.lexer.pos);
267
269
  }
268
270
  this.lexer.pos = whitespace_start;
269
271
  return null;
@@ -276,7 +278,7 @@ class SelectorParser {
276
278
  this.lexer.restore_position(saved);
277
279
  return null;
278
280
  }
279
- return this.create_node(NODE_SELECTOR_CLASS, dot_pos, this.lexer.token_end);
281
+ return this.create_node(CLASS_SELECTOR, dot_pos, this.lexer.token_end);
280
282
  }
281
283
  // Parse attribute selector ([attr], [attr=value], etc.)
282
284
  parse_attribute_selector(start) {
@@ -298,7 +300,7 @@ class SelectorParser {
298
300
  }
299
301
  }
300
302
  }
301
- let node = this.create_node(NODE_SELECTOR_ATTRIBUTE, start, end);
303
+ let node = this.create_node(ATTRIBUTE_SELECTOR, start, end);
302
304
  this.parse_attribute_content(node, content_start, content_end);
303
305
  return node;
304
306
  }
@@ -313,14 +315,14 @@ class SelectorParser {
313
315
  let value_start = -1;
314
316
  let value_end = -1;
315
317
  while (name_end < end) {
316
- let ch2 = this.source.charCodeAt(name_end);
317
- if (is_whitespace(ch2) || ch2 === CHAR_EQUALS || ch2 === CHAR_TILDE || ch2 === CHAR_PIPE || ch2 === CHAR_CARET || ch2 === CHAR_DOLLAR || ch2 === CHAR_ASTERISK) {
318
+ let ch3 = this.source.charCodeAt(name_end);
319
+ if (is_whitespace(ch3) || ch3 === CHAR_EQUALS || ch3 === CHAR_TILDE || ch3 === CHAR_PIPE || ch3 === CHAR_CARET || ch3 === CHAR_DOLLAR || ch3 === CHAR_ASTERISK) {
318
320
  break;
319
321
  }
320
322
  name_end++;
321
323
  }
322
324
  if (name_end > name_start) {
323
- this.arena.set_content_start(node, name_start);
325
+ this.arena.set_content_start_delta(node, name_start - this.arena.get_start_offset(node));
324
326
  this.arena.set_content_length(node, name_end - name_start);
325
327
  }
326
328
  let pos = skip_whitespace_and_comments_forward(this.source, name_end, end);
@@ -330,22 +332,23 @@ class SelectorParser {
330
332
  return;
331
333
  }
332
334
  let ch1 = this.source.charCodeAt(pos);
335
+ let ch2 = pos + 1 < end ? this.source.charCodeAt(pos + 1) : 0;
333
336
  if (ch1 === CHAR_EQUALS) {
334
337
  operator_end = pos + 1;
335
338
  this.arena.set_attr_operator(node, ATTR_OPERATOR_EQUAL);
336
- } else if (ch1 === CHAR_TILDE && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
339
+ } else if (ch1 === CHAR_TILDE && ch2 === CHAR_EQUALS) {
337
340
  operator_end = pos + 2;
338
341
  this.arena.set_attr_operator(node, ATTR_OPERATOR_TILDE_EQUAL);
339
- } else if (ch1 === CHAR_PIPE && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
342
+ } else if (ch1 === CHAR_PIPE && ch2 === CHAR_EQUALS) {
340
343
  operator_end = pos + 2;
341
344
  this.arena.set_attr_operator(node, ATTR_OPERATOR_PIPE_EQUAL);
342
- } else if (ch1 === CHAR_CARET && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
345
+ } else if (ch1 === CHAR_CARET && ch2 === CHAR_EQUALS) {
343
346
  operator_end = pos + 2;
344
347
  this.arena.set_attr_operator(node, ATTR_OPERATOR_CARET_EQUAL);
345
- } else if (ch1 === CHAR_DOLLAR && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
348
+ } else if (ch1 === CHAR_DOLLAR && ch2 === CHAR_EQUALS) {
346
349
  operator_end = pos + 2;
347
350
  this.arena.set_attr_operator(node, ATTR_OPERATOR_DOLLAR_EQUAL);
348
- } else if (ch1 === CHAR_ASTERISK && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
351
+ } else if (ch1 === CHAR_ASTERISK && ch2 === CHAR_EQUALS) {
349
352
  operator_end = pos + 2;
350
353
  this.arena.set_attr_operator(node, ATTR_OPERATOR_STAR_EQUAL);
351
354
  } else {
@@ -388,7 +391,7 @@ class SelectorParser {
388
391
  value_end = pos;
389
392
  }
390
393
  if (value_end > value_start) {
391
- this.arena.set_value_start(node, value_start);
394
+ this.arena.set_value_start_delta(node, value_start - this.arena.get_start_offset(node));
392
395
  this.arena.set_value_length(node, value_end - value_start);
393
396
  }
394
397
  pos = skip_whitespace_and_comments_forward(this.source, value_end, end);
@@ -416,12 +419,8 @@ class SelectorParser {
416
419
  this.lexer.next_token_fast(false);
417
420
  let token_type = this.lexer.token_type;
418
421
  if (token_type === TOKEN_IDENT) {
419
- let node = this.create_node(
420
- is_pseudo_element ? NODE_SELECTOR_PSEUDO_ELEMENT : NODE_SELECTOR_PSEUDO_CLASS,
421
- start,
422
- this.lexer.token_end
423
- );
424
- this.arena.set_content_start(node, this.lexer.token_start);
422
+ let node = this.create_node(is_pseudo_element ? PSEUDO_ELEMENT_SELECTOR : PSEUDO_CLASS_SELECTOR, start, this.lexer.token_end);
423
+ this.arena.set_content_start_delta(node, this.lexer.token_start - start);
425
424
  this.arena.set_content_length(node, this.lexer.token_end - this.lexer.token_start);
426
425
  if (is_vendor_prefixed(this.source, this.lexer.token_start, this.lexer.token_end)) {
427
426
  this.arena.set_flag(node, FLAG_VENDOR_PREFIXED);
@@ -459,8 +458,8 @@ class SelectorParser {
459
458
  }
460
459
  }
461
460
  }
462
- let node = this.create_node(is_pseudo_element ? NODE_SELECTOR_PSEUDO_ELEMENT : NODE_SELECTOR_PSEUDO_CLASS, start, end);
463
- this.arena.set_content_start(node, func_name_start);
461
+ let node = this.create_node(is_pseudo_element ? PSEUDO_ELEMENT_SELECTOR : PSEUDO_CLASS_SELECTOR, start, end);
462
+ this.arena.set_content_start_delta(node, func_name_start - start);
464
463
  this.arena.set_content_length(node, func_name_end - func_name_start);
465
464
  this.arena.set_flag(node, FLAG_HAS_PARENS);
466
465
  if (is_vendor_prefixed(this.source, func_name_start, func_name_end)) {
@@ -516,7 +515,7 @@ class SelectorParser {
516
515
  continue;
517
516
  }
518
517
  if (token_type === TOKEN_STRING || token_type === TOKEN_IDENT) {
519
- let lang_node = this.create_node(NODE_SELECTOR_LANG, token_start, token_end);
518
+ let lang_node = this.create_node(LANG_SELECTOR, token_start, token_end);
520
519
  if (first_child === null) {
521
520
  first_child = lang_node;
522
521
  }
@@ -554,11 +553,13 @@ class SelectorParser {
554
553
  let selector_list = this.parse_selector_list();
555
554
  this.selector_end = saved_selector_end;
556
555
  this.lexer.restore_position(saved);
557
- let of_node = this.arena.create_node();
558
- this.arena.set_type(of_node, NODE_SELECTOR_NTH_OF);
559
- this.arena.set_start_offset(of_node, start);
560
- this.arena.set_length(of_node, end - start);
561
- this.arena.set_start_line(of_node, this.lexer.line);
556
+ let of_node = this.arena.create_node(
557
+ NTH_OF_SELECTOR,
558
+ start,
559
+ end - start,
560
+ this.lexer.line,
561
+ 1
562
+ );
562
563
  if (anplusb_node !== null && selector_list !== null) {
563
564
  this.arena.set_first_child(of_node, anplusb_node);
564
565
  this.arena.set_last_child(of_node, selector_list);
@@ -587,13 +588,14 @@ class SelectorParser {
587
588
  return -1;
588
589
  }
589
590
  create_node(type, start, end) {
590
- let node = this.arena.create_node();
591
- this.arena.set_type(node, type);
592
- this.arena.set_start_offset(node, start);
593
- this.arena.set_length(node, end - start);
594
- this.arena.set_start_line(node, this.lexer.line);
595
- this.arena.set_start_column(node, this.lexer.column);
596
- this.arena.set_content_start(node, start);
591
+ let node = this.arena.create_node(
592
+ type,
593
+ start,
594
+ end - start,
595
+ this.lexer.line,
596
+ this.lexer.column
597
+ );
598
+ this.arena.set_content_start_delta(node, 0);
597
599
  this.arena.set_content_length(node, end - start);
598
600
  return node;
599
601
  }
@@ -607,11 +609,7 @@ function parse_selector(source) {
607
609
  const selector_parser = new SelectorParser(arena, source);
608
610
  const selector_index = selector_parser.parse_selector(0, source.length);
609
611
  if (selector_index === null) {
610
- const empty = arena.create_node();
611
- arena.set_type(empty, NODE_SELECTOR_LIST);
612
- arena.set_start_offset(empty, 0);
613
- arena.set_length(empty, 0);
614
- arena.set_start_line(empty, 1);
612
+ const empty = arena.create_node(SELECTOR_LIST, 0, 0, 1, 1);
615
613
  return new CSSNode(arena, source, empty);
616
614
  }
617
615
  return new CSSNode(arena, source, selector_index);
@@ -14,43 +14,3 @@ export declare function parse_dimension(text: string): {
14
14
  value: number;
15
15
  unit: string;
16
16
  };
17
- /**
18
- * Skip whitespace forward from a position
19
- *
20
- * @param source - The source string
21
- * @param pos - Starting position
22
- * @param end - End boundary (exclusive)
23
- * @returns New position after skipping whitespace
24
- */
25
- export declare function skip_whitespace_forward(source: string, pos: number, end: number): number;
26
- /**
27
- * Skip whitespace and comments forward from a position
28
- *
29
- * @param source - The source string
30
- * @param pos - Starting position
31
- * @param end - End boundary (exclusive)
32
- * @returns New position after skipping whitespace/comments
33
- */
34
- export declare function skip_whitespace_and_comments_forward(source: string, pos: number, end: number): number;
35
- /**
36
- * Skip whitespace and comments backward from a position
37
- *
38
- * @param source - The source string
39
- * @param pos - Starting position (exclusive, scanning backward from pos-1)
40
- * @param start - Start boundary (inclusive, won't go before this)
41
- * @returns New position after skipping whitespace/comments backward
42
- */
43
- export declare function skip_whitespace_and_comments_backward(source: string, pos: number, start: number): number;
44
- /**
45
- * Trim whitespace and comments from both ends of a string range
46
- *
47
- * @param source - The source string
48
- * @param start - Start offset in source
49
- * @param end - End offset in source
50
- * @returns [trimmed_start, trimmed_end] or null if all whitespace/comments
51
- *
52
- * Skips whitespace (space, tab, newline, CR, FF) and CSS comments from both ends
53
- * of the specified range. Returns the trimmed boundaries or null if the range
54
- * contains only whitespace and comments.
55
- */
56
- export declare function trim_boundaries(source: string, start: number, end: number): [number, number] | null;
@@ -0,0 +1,92 @@
1
+ import { is_digit, CHAR_PERIOD, CHAR_MINUS_HYPHEN, CHAR_PLUS, is_whitespace, CHAR_FORWARD_SLASH, CHAR_ASTERISK } from './string-utils.js';
2
+
3
+ function parse_dimension(text) {
4
+ let num_end = 0;
5
+ for (let i = 0; i < text.length; i++) {
6
+ let ch = text.charCodeAt(i);
7
+ if (ch === 101 || ch === 69) {
8
+ if (i + 1 < text.length) {
9
+ let next_ch = text.charCodeAt(i + 1);
10
+ if (is_digit(next_ch)) {
11
+ num_end = i + 1;
12
+ continue;
13
+ }
14
+ if ((next_ch === 43 || next_ch === 45) && i + 2 < text.length) {
15
+ let afterSign = text.charCodeAt(i + 2);
16
+ if (is_digit(afterSign)) {
17
+ num_end = i + 1;
18
+ continue;
19
+ }
20
+ }
21
+ }
22
+ break;
23
+ }
24
+ if (is_digit(ch) || ch === CHAR_PERIOD || ch === CHAR_MINUS_HYPHEN || ch === CHAR_PLUS) {
25
+ num_end = i + 1;
26
+ } else {
27
+ break;
28
+ }
29
+ }
30
+ let num_str = text.substring(0, num_end);
31
+ let unit = text.substring(num_end);
32
+ let value = num_str ? parseFloat(num_str) : 0;
33
+ return { value, unit };
34
+ }
35
+ function skip_whitespace_forward(source, pos, end) {
36
+ while (pos < end && is_whitespace(source.charCodeAt(pos))) {
37
+ pos++;
38
+ }
39
+ return pos;
40
+ }
41
+ function skip_whitespace_and_comments_forward(source, pos, end) {
42
+ while (pos < end) {
43
+ let ch = source.charCodeAt(pos);
44
+ if (is_whitespace(ch)) {
45
+ pos++;
46
+ continue;
47
+ }
48
+ if (ch === CHAR_FORWARD_SLASH && pos + 1 < end && source.charCodeAt(pos + 1) === CHAR_ASTERISK) {
49
+ pos += 2;
50
+ while (pos < end) {
51
+ if (source.charCodeAt(pos) === CHAR_ASTERISK && pos + 1 < end && source.charCodeAt(pos + 1) === CHAR_FORWARD_SLASH) {
52
+ pos += 2;
53
+ break;
54
+ }
55
+ pos++;
56
+ }
57
+ continue;
58
+ }
59
+ break;
60
+ }
61
+ return pos;
62
+ }
63
+ function skip_whitespace_and_comments_backward(source, pos, start) {
64
+ while (pos > start) {
65
+ let ch = source.charCodeAt(pos - 1);
66
+ if (is_whitespace(ch)) {
67
+ pos--;
68
+ continue;
69
+ }
70
+ if (pos >= 2 && ch === CHAR_FORWARD_SLASH && source.charCodeAt(pos - 2) === CHAR_ASTERISK) {
71
+ pos -= 2;
72
+ while (pos > start) {
73
+ if (pos >= 2 && source.charCodeAt(pos - 2) === CHAR_FORWARD_SLASH && source.charCodeAt(pos - 1) === CHAR_ASTERISK) {
74
+ pos -= 2;
75
+ break;
76
+ }
77
+ pos--;
78
+ }
79
+ continue;
80
+ }
81
+ break;
82
+ }
83
+ return pos;
84
+ }
85
+ function trim_boundaries(source, start, end) {
86
+ start = skip_whitespace_and_comments_forward(source, start, end);
87
+ end = skip_whitespace_and_comments_backward(source, end, start);
88
+ if (start >= end) return null;
89
+ return [start, end];
90
+ }
91
+
92
+ export { parse_dimension, skip_whitespace_and_comments_backward, skip_whitespace_and_comments_forward, skip_whitespace_forward, trim_boundaries };
@@ -1,20 +1,4 @@
1
- import { CSSDataArena } from './arena';
2
1
  import { CSSNode } from './css-node';
3
- export declare class ValueParser {
4
- private lexer;
5
- private arena;
6
- private source;
7
- private value_end;
8
- constructor(arena: CSSDataArena, source: string);
9
- parse_value(start: number, end: number): number[];
10
- private is_whitespace_token;
11
- private parse_value_node;
12
- private create_node;
13
- private create_operator_node;
14
- private parse_operator_node;
15
- private parse_function_node;
16
- private parse_parenthesis_node;
17
- }
18
2
  /**
19
3
  * Parse a CSS declaration value string and return an array of value AST nodes
20
4
  * @param value_string - The CSS value to parse (e.g., "1px solid red")
@@ -1,5 +1,8 @@
1
- import { L as Lexer, y as TOKEN_EOF, t as TOKEN_LEFT_PAREN, 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, u as TOKEN_RIGHT_PAREN } from './lexer-CtBKgfVv.js';
2
- import { Z as CSSDataArena, C as CSSNode, ab as is_whitespace, u as NODE_VALUE_OPERATOR, s as NODE_VALUE_COLOR, r as NODE_VALUE_STRING, q as NODE_VALUE_DIMENSION, p as NODE_VALUE_NUMBER, o as NODE_VALUE_KEYWORD, a5 as CHAR_PLUS, ao as CHAR_MINUS_HYPHEN, a8 as CHAR_ASTERISK, ap as CHAR_FORWARD_SLASH, t as NODE_VALUE_FUNCTION, v as NODE_VALUE_PARENTHESIS } from './css-node-GOEvp2OO.js';
1
+ import { Lexer } from './lexer.js';
2
+ import { CSSDataArena, OPERATOR, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, URL, FUNCTION, PARENTHESIS } from './arena.js';
3
+ import { TOKEN_EOF, TOKEN_LEFT_PAREN, TOKEN_COMMA, TOKEN_DELIM, TOKEN_FUNCTION, TOKEN_HASH, TOKEN_STRING, TOKEN_DIMENSION, TOKEN_PERCENTAGE, TOKEN_NUMBER, TOKEN_IDENT, TOKEN_RIGHT_PAREN } from './token-types.js';
4
+ import { is_whitespace, CHAR_PLUS, CHAR_MINUS_HYPHEN, CHAR_ASTERISK, CHAR_FORWARD_SLASH } from './string-utils.js';
5
+ import { CSSNode } from './css-node.js';
3
6
 
4
7
  class ValueParser {
5
8
  lexer;
@@ -24,7 +27,7 @@ class ValueParser {
24
27
  if (this.lexer.token_start >= this.value_end) break;
25
28
  let token_type = this.lexer.token_type;
26
29
  if (token_type === TOKEN_EOF) break;
27
- if (this.is_whitespace_token()) {
30
+ if (this.is_whitespace_inline()) {
28
31
  continue;
29
32
  }
30
33
  let node = this.parse_value_node();
@@ -34,13 +37,11 @@ class ValueParser {
34
37
  }
35
38
  return nodes;
36
39
  }
37
- is_whitespace_token() {
38
- let start = this.lexer.token_start;
39
- let end = this.lexer.token_end;
40
- if (start >= end) return false;
41
- for (let i = start; i < end; i++) {
42
- let ch = this.source.charCodeAt(i);
43
- if (!is_whitespace(ch)) {
40
+ // Helper to check if token is all whitespace (inline for hot paths)
41
+ is_whitespace_inline() {
42
+ if (this.lexer.token_start >= this.lexer.token_end) return false;
43
+ for (let i = this.lexer.token_start; i < this.lexer.token_end; i++) {
44
+ if (!is_whitespace(this.source.charCodeAt(i))) {
44
45
  return false;
45
46
  }
46
47
  }
@@ -52,22 +53,22 @@ class ValueParser {
52
53
  let end = this.lexer.token_end;
53
54
  switch (token_type) {
54
55
  case TOKEN_IDENT:
55
- return this.create_node(NODE_VALUE_KEYWORD, start, end);
56
+ return this.create_node(IDENTIFIER, start, end);
56
57
  case TOKEN_NUMBER:
57
- return this.create_node(NODE_VALUE_NUMBER, start, end);
58
+ return this.create_node(NUMBER, start, end);
58
59
  case TOKEN_PERCENTAGE:
59
60
  case TOKEN_DIMENSION:
60
- return this.create_node(NODE_VALUE_DIMENSION, start, end);
61
+ return this.create_node(DIMENSION, start, end);
61
62
  case TOKEN_STRING:
62
- return this.create_node(NODE_VALUE_STRING, start, end);
63
+ return this.create_node(STRING, start, end);
63
64
  case TOKEN_HASH:
64
- return this.create_node(NODE_VALUE_COLOR, start, end);
65
+ return this.create_node(HASH, start, end);
65
66
  case TOKEN_FUNCTION:
66
67
  return this.parse_function_node(start, end);
67
68
  case TOKEN_DELIM:
68
69
  return this.parse_operator_node(start, end);
69
70
  case TOKEN_COMMA:
70
- return this.create_node(NODE_VALUE_OPERATOR, start, end);
71
+ return this.create_node(OPERATOR, start, end);
71
72
  case TOKEN_LEFT_PAREN:
72
73
  return this.parse_parenthesis_node(start, end);
73
74
  default:
@@ -75,16 +76,18 @@ class ValueParser {
75
76
  }
76
77
  }
77
78
  create_node(node_type, start, end) {
78
- let node = this.arena.create_node();
79
- this.arena.set_type(node, node_type);
80
- this.arena.set_start_offset(node, start);
81
- this.arena.set_length(node, end - start);
82
- this.arena.set_content_start(node, start);
79
+ let node = this.arena.create_node(
80
+ node_type,
81
+ start,
82
+ end - start,
83
+ this.lexer.line,
84
+ this.lexer.column
85
+ );
83
86
  this.arena.set_content_length(node, end - start);
84
87
  return node;
85
88
  }
86
89
  create_operator_node(start, end) {
87
- return this.create_node(NODE_VALUE_OPERATOR, start, end);
90
+ return this.create_node(OPERATOR, start, end);
88
91
  }
89
92
  parse_operator_node(start, end) {
90
93
  let ch = this.source.charCodeAt(start);
@@ -94,17 +97,22 @@ class ValueParser {
94
97
  return null;
95
98
  }
96
99
  parse_function_node(start, end) {
97
- let node = this.arena.create_node();
98
- this.arena.set_type(node, NODE_VALUE_FUNCTION);
99
- this.arena.set_start_offset(node, start);
100
100
  let name_end = end - 1;
101
- this.arena.set_content_start(node, start);
102
- this.arena.set_content_length(node, name_end - start);
103
101
  let func_name = this.source.substring(start, name_end).toLowerCase();
102
+ let node = this.arena.create_node(
103
+ func_name === "url" ? URL : FUNCTION,
104
+ start,
105
+ 0,
106
+ // length unknown yet
107
+ this.lexer.line,
108
+ this.lexer.column
109
+ );
110
+ this.arena.set_content_start_delta(node, 0);
111
+ this.arena.set_content_length(node, name_end - start);
104
112
  if (func_name === "url" || func_name === "src") {
105
113
  let save_pos = this.lexer.save_position();
106
114
  this.lexer.next_token_fast(false);
107
- while (this.is_whitespace_token() && this.lexer.pos < this.value_end) {
115
+ while (this.is_whitespace_inline() && this.lexer.pos < this.value_end) {
108
116
  this.lexer.next_token_fast(false);
109
117
  }
110
118
  let first_token_type = this.lexer.token_type;
@@ -130,7 +138,7 @@ class ValueParser {
130
138
  }
131
139
  }
132
140
  this.arena.set_length(node, func_end2 - start);
133
- this.arena.set_value_start(node, content_start2);
141
+ this.arena.set_value_start_delta(node, content_start2 - start);
134
142
  this.arena.set_value_length(node, content_end2 - content_start2);
135
143
  return node;
136
144
  }
@@ -155,28 +163,27 @@ class ValueParser {
155
163
  break;
156
164
  }
157
165
  }
158
- if (this.is_whitespace_token()) continue;
166
+ if (this.is_whitespace_inline()) continue;
159
167
  let arg_node = this.parse_value_node();
160
168
  if (arg_node !== null) {
161
169
  args.push(arg_node);
162
170
  }
163
171
  }
164
172
  this.arena.set_length(node, func_end - start);
165
- this.arena.set_value_start(node, content_start);
173
+ this.arena.set_value_start_delta(node, content_start - start);
166
174
  this.arena.set_value_length(node, content_end - content_start);
167
- if (args.length > 0) {
168
- this.arena.set_first_child(node, args[0]);
169
- this.arena.set_last_child(node, args[args.length - 1]);
170
- for (let i = 0; i < args.length - 1; i++) {
171
- this.arena.set_next_sibling(args[i], args[i + 1]);
172
- }
173
- }
175
+ this.arena.append_children(node, args);
174
176
  return node;
175
177
  }
176
178
  parse_parenthesis_node(start, end) {
177
- let node = this.arena.create_node();
178
- this.arena.set_type(node, NODE_VALUE_PARENTHESIS);
179
- this.arena.set_start_offset(node, start);
179
+ let node = this.arena.create_node(
180
+ PARENTHESIS,
181
+ start,
182
+ 0,
183
+ // length unknown yet
184
+ this.lexer.line,
185
+ this.lexer.column
186
+ );
180
187
  let children = [];
181
188
  let paren_depth = 1;
182
189
  let paren_end = end;
@@ -192,20 +199,14 @@ class ValueParser {
192
199
  break;
193
200
  }
194
201
  }
195
- if (this.is_whitespace_token()) continue;
202
+ if (this.is_whitespace_inline()) continue;
196
203
  let child_node = this.parse_value_node();
197
204
  if (child_node !== null) {
198
205
  children.push(child_node);
199
206
  }
200
207
  }
201
208
  this.arena.set_length(node, paren_end - start);
202
- if (children.length > 0) {
203
- this.arena.set_first_child(node, children[0]);
204
- this.arena.set_last_child(node, children[children.length - 1]);
205
- for (let i = 0; i < children.length - 1; i++) {
206
- this.arena.set_next_sibling(children[i], children[i + 1]);
207
- }
208
- }
209
+ this.arena.append_children(node, children);
209
210
  return node;
210
211
  }
211
212
  }