@projectwallace/css-parser 0.18.8 → 0.18.11

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