@projectwallace/css-parser 0.18.1 → 0.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { B as SUPPORTS_QUERY, C as NESTING_SELECTOR, D as OPERATOR, E as NUMBER, F as SELECTOR_LIST, G as VALUE, H as UNICODE_RANGE, I as STRING, L as STYLESHEET, M as PSEUDO_ELEMENT_SELECTOR, N as RAW, O as PARENTHESIS, P as SELECTOR, R as STYLE_RULE, S as MEDIA_TYPE, T as NTH_SELECTOR, U as UNIVERSAL_SELECTOR, V as TYPE_SELECTOR, W as URL, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as MEDIA_FEATURE, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PSEUDO_CLASS_SELECTOR, k as PRELUDE_OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as LANG_SELECTOR, w as NTH_OF_SELECTOR, x as MEDIA_QUERY, y as LAYER_NAME, z as SUPPORTS_DECLARATION } from "./arena-Bx5vp6Sl.js";
2
2
  import { NODE_TYPES } from "./constants.js";
3
- import { A as TOKEN_URL, C as TOKEN_PERCENTAGE, D as TOKEN_SEMICOLON, E as TOKEN_RIGHT_PAREN, M as Token, N as TokenType, O as TOKEN_STRING, S as TOKEN_NUMBER, T as TOKEN_RIGHT_BRACKET, _ as TOKEN_HASH, a as TOKEN_AT_KEYWORD, b as TOKEN_LEFT_BRACKET, c as TOKEN_CDC, d as TOKEN_COMMA, f as TOKEN_COMMENT, g as TOKEN_FUNCTION, h as TOKEN_EOF, i as tokenize, j as TOKEN_WHITESPACE, k as TOKEN_UNICODE_RANGE, l as TOKEN_CDO, m as TOKEN_DIMENSION, o as TOKEN_BAD_STRING, p as TOKEN_DELIM, r as LexerPosition, s as TOKEN_BAD_URL, t as CommentInfo, u as TOKEN_COLON, v as TOKEN_IDENT, w as TOKEN_RIGHT_BRACE, x as TOKEN_LEFT_PAREN, y as TOKEN_LEFT_BRACE } from "./tokenize-DeZYFa9R.js";
3
+ import { A as TOKEN_URL, C as TOKEN_PERCENTAGE, D as TOKEN_SEMICOLON, E as TOKEN_RIGHT_PAREN, M as Token, N as TokenType, O as TOKEN_STRING, S as TOKEN_NUMBER, T as TOKEN_RIGHT_BRACKET, _ as TOKEN_HASH, a as TOKEN_AT_KEYWORD, b as TOKEN_LEFT_BRACKET, c as TOKEN_CDC, d as TOKEN_COMMA, f as TOKEN_COMMENT, g as TOKEN_FUNCTION, h as TOKEN_EOF, i as tokenize, j as TOKEN_WHITESPACE, k as TOKEN_UNICODE_RANGE, l as TOKEN_CDO, m as TOKEN_DIMENSION, o as TOKEN_BAD_STRING, p as TOKEN_DELIM, r as LexerPosition, s as TOKEN_BAD_URL, t as CommentInfo, u as TOKEN_COLON, v as TOKEN_IDENT, w as TOKEN_RIGHT_BRACE, x as TOKEN_LEFT_PAREN, y as TOKEN_LEFT_BRACE } from "./tokenize-CDEnYcVG.js";
4
4
  import { $ as is_class_selector, A as PreludeSelectorList, At as is_stylesheet, B as SupportsDeclaration, Bt as TYPE_NAMES, C as NestingSelector, Ct as is_pseudo_class_selector, D as Operator, Dt as is_selector, E as Number, Et as is_rule, F as Selector, Ft as is_universal_selector, G as UniversalSelector, H as ToPlain, I as SelectorList, It as is_url, J as WithChildren, K as Url, L as SelectorNode, Lt as is_value, M as PseudoElementSelector, Mt as is_supports_query, N as Raw, Nt as is_type_selector, O as Parenthesis, Ot as is_selector_list, P as Rule, Pt as is_unicode_range, Q as is_block, R as String, Rt as CloneOptions, S as MediaType, St as is_prelude_selectorlist, T as NthSelector, Tt as is_raw, U as TypeSelector, V as SupportsQuery, W as UnicodeRange, X as is_atrule_prelude, Y as is_atrule, Z as is_attribute_selector, _ as Identifier, _t as is_nth_selector, a as Block, at as is_feature_range, b as MediaFeature, bt as is_parenthesis, c as Combinator, ct as is_id_selector, d as Declaration, dt as is_layer_name, et as is_combinator, f as Dimension, ft as is_media_feature, g as IdSelector, gt as is_nth_of_selector, h as Hash, ht as is_nesting_selector, i as AttributeSelector, it as is_dimension, j as PseudoClassSelector, jt as is_supports_declaration, k as PreludeOperator, kt as is_string, l as Comment, lt as is_identifier, m as Function, mt as is_media_type, n as Atrule, nt as is_container_query, o as CSSNode, ot as is_function, p as FeatureRange, pt as is_media_query, q as Value, r as AtrulePrelude, rt as is_declaration, s as ClassSelector, st as is_hash, t as AnyNode, tt as is_comment, u as ContainerQuery, ut as is_lang_selector, v as LangSelector, vt as is_number, w as NthOfSelector, wt as is_pseudo_element_selector, x as MediaQuery, xt as is_prelude_operator, y as LayerName, yt as is_operator, z as StyleSheet, zt as PlainCSSNode } from "./node-types-D41wr72y.js";
5
5
  import { ParserOptions, parse } from "./parse.js";
6
6
  import { parse_selector, parse_selector_list } from "./parse-selector.js";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as TOKEN_RIGHT_BRACKET, D as TOKEN_UNICODE_RANGE, E as TOKEN_STRING, O as TOKEN_URL, S as TOKEN_RIGHT_BRACE, T as TOKEN_SEMICOLON, _ as TOKEN_LEFT_BRACE, a as TOKEN_BAD_URL, b as TOKEN_NUMBER, c as TOKEN_COLON, d as TOKEN_DELIM, f as TOKEN_DIMENSION, g as TOKEN_IDENT, h as TOKEN_HASH, i as TOKEN_BAD_STRING, k as TOKEN_WHITESPACE, l as TOKEN_COMMA, m as TOKEN_FUNCTION, n as tokenize, o as TOKEN_CDC, p as TOKEN_EOF, r as TOKEN_AT_KEYWORD, s as TOKEN_CDO, u as TOKEN_COMMENT, v as TOKEN_LEFT_BRACKET, w as TOKEN_RIGHT_PAREN, x as TOKEN_PERCENTAGE, y as TOKEN_LEFT_PAREN } from "./tokenize-BiRLfVbm.js";
1
+ import { C as TOKEN_RIGHT_BRACKET, D as TOKEN_UNICODE_RANGE, E as TOKEN_STRING, O as TOKEN_URL, S as TOKEN_RIGHT_BRACE, T as TOKEN_SEMICOLON, _ as TOKEN_LEFT_BRACE, a as TOKEN_BAD_URL, b as TOKEN_NUMBER, c as TOKEN_COLON, d as TOKEN_DELIM, f as TOKEN_DIMENSION, g as TOKEN_IDENT, h as TOKEN_HASH, i as TOKEN_BAD_STRING, k as TOKEN_WHITESPACE, l as TOKEN_COMMA, m as TOKEN_FUNCTION, n as tokenize, o as TOKEN_CDC, p as TOKEN_EOF, r as TOKEN_AT_KEYWORD, s as TOKEN_CDO, u as TOKEN_COMMENT, v as TOKEN_LEFT_BRACKET, w as TOKEN_RIGHT_PAREN, x as TOKEN_PERCENTAGE, y as TOKEN_LEFT_PAREN } from "./tokenize-DK24ACbY.js";
2
2
  import { A as PSEUDO_CLASS_SELECTOR, B as TYPE_SELECTOR, C as NESTING_SELECTOR, D as OPERATOR, E as NUMBER, F as STRING, H as UNIVERSAL_SELECTOR, I as STYLESHEET, L as STYLE_RULE, M as RAW, N as SELECTOR, O as PARENTHESIS, P as SELECTOR_LIST, R as SUPPORTS_DECLARATION, S as MEDIA_TYPE, T as NTH_SELECTOR, U as URL, V as UNICODE_RANGE, W as VALUE, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as MEDIA_FEATURE, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PSEUDO_ELEMENT_SELECTOR, k as PRELUDE_OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as LANG_SELECTOR, w as NTH_OF_SELECTOR, x as MEDIA_QUERY, y as LAYER_NAME, z as SUPPORTS_QUERY } from "./arena-CFwM4uQs.js";
3
3
  import { c as str_starts_with, i as is_vendor_prefixed, o as str_equals, r as is_custom, s as str_index_of, t as parse_dimension } from "./parse-dimension-ouWXNHdx.js";
4
4
  import { n as TYPE_NAMES } from "./css-node-Cf8K9nWd.js";
@@ -1,4 +1,4 @@
1
- import { t as Lexer } from "./tokenize-BiRLfVbm.js";
1
+ import { t as Lexer } from "./tokenize-DK24ACbY.js";
2
2
  import { l as CSSDataArena } from "./arena-CFwM4uQs.js";
3
3
  import { o as str_equals, s as str_index_of } from "./parse-dimension-ouWXNHdx.js";
4
4
  import { t as CSSNode } from "./css-node-Cf8K9nWd.js";
@@ -15,6 +15,7 @@ declare class AtRulePreludeParser {
15
15
  private parse_media_query_list;
16
16
  private create_node;
17
17
  private is_and_or_not;
18
+ private scan_matching_paren;
18
19
  private parse_function_condition;
19
20
  private parse_single_media_query;
20
21
  private parse_media_feature;
@@ -1,10 +1,10 @@
1
- import { t as Lexer } from "./tokenize-BiRLfVbm.js";
1
+ import { t as Lexer } from "./tokenize-DK24ACbY.js";
2
2
  import { l as CSSDataArena } from "./arena-CFwM4uQs.js";
3
3
  import { l as strip_vendor_prefix, o as str_equals } from "./parse-dimension-ouWXNHdx.js";
4
4
  import { t as CSSNode } from "./css-node-Cf8K9nWd.js";
5
5
  import { i as trim_boundaries, n as skip_whitespace_and_comments_forward } from "./parse-utils-D7srNG13.js";
6
6
  import { SelectorParser } from "./parse-selector.js";
7
- import { t as ValueNodeParser } from "./value-node-parser-CZ47v3hp.js";
7
+ import { t as ValueNodeParser } from "./value-node-parser-jRoWXgOr.js";
8
8
  //#region src/parse-atrule-prelude.ts
9
9
  /** @internal */
10
10
  var AtRulePreludeParser = class {
@@ -70,25 +70,32 @@ var AtRulePreludeParser = class {
70
70
  is_and_or_not(str) {
71
71
  return str_equals("and", str) || str_equals("or", str) || str_equals("not", str);
72
72
  }
73
+ scan_matching_paren() {
74
+ let depth = 1;
75
+ let content_end = this.lexer.pos;
76
+ let close_end = this.lexer.token_end;
77
+ while (this.lexer.pos < this.prelude_end && depth > 0) {
78
+ let token_type = this.next_token();
79
+ if (token_type === 21 || token_type === 2) depth++;
80
+ else if (token_type === 22) {
81
+ depth--;
82
+ if (depth === 0) {
83
+ content_end = this.lexer.token_start;
84
+ close_end = this.lexer.token_end;
85
+ }
86
+ } else if (token_type === 26) break;
87
+ }
88
+ return [
89
+ content_end,
90
+ close_end,
91
+ depth === 0
92
+ ];
93
+ }
73
94
  parse_function_condition() {
74
95
  let func_name = this.source.substring(this.lexer.token_start, this.lexer.token_end - 1);
75
96
  let func_start = this.lexer.token_start;
76
97
  let content_start = this.lexer.token_end;
77
- let paren_depth = 1;
78
- let func_end = this.lexer.token_end;
79
- let content_end = content_start;
80
- while (this.lexer.pos < this.prelude_end && paren_depth > 0) {
81
- this.next_token();
82
- let inner_token = this.lexer.token_type;
83
- if (inner_token === 21 || inner_token === 2) paren_depth++;
84
- else if (inner_token === 22) {
85
- paren_depth--;
86
- if (paren_depth === 0) {
87
- content_end = this.lexer.token_start;
88
- func_end = this.lexer.token_end;
89
- }
90
- } else if (inner_token === 26) break;
91
- }
98
+ let [content_end, func_end] = this.scan_matching_paren();
92
99
  let func_node = this.create_node(15, func_start, func_end);
93
100
  this.arena.set_content_start_delta(func_node, 0);
94
101
  this.arena.set_content_length(func_node, func_name.length);
@@ -148,17 +155,9 @@ var AtRulePreludeParser = class {
148
155
  }
149
156
  parse_media_feature() {
150
157
  let feature_start = this.lexer.token_start;
151
- let depth = 1;
152
158
  let content_start = this.lexer.pos;
153
- while (this.lexer.pos < this.prelude_end && depth > 0) {
154
- this.next_token();
155
- let token_type = this.lexer.token_type;
156
- if (token_type === 21 || token_type === 2) depth++;
157
- else if (token_type === 22) depth--;
158
- }
159
- if (depth !== 0) return null;
160
- let content_end = this.lexer.token_start;
161
- let feature_end = this.lexer.token_end;
159
+ let [content_end, feature_end, matched] = this.scan_matching_paren();
160
+ if (!matched) return null;
162
161
  let has_comparison = false;
163
162
  let i = content_start;
164
163
  while (i < content_end) {
@@ -241,17 +240,9 @@ var AtRulePreludeParser = class {
241
240
  let token_type = this.lexer.token_type;
242
241
  if (token_type === 21) {
243
242
  let feature_start = this.lexer.token_start;
244
- let depth = 1;
245
243
  let content_start = this.lexer.pos;
246
- while (this.lexer.pos < this.prelude_end && depth > 0) {
247
- this.next_token();
248
- let inner_token_type = this.lexer.token_type;
249
- if (inner_token_type === 21 || inner_token_type === 2) depth++;
250
- else if (inner_token_type === 22) depth--;
251
- }
252
- if (depth === 0) {
253
- let content_end = this.lexer.token_start;
254
- let feature_end = this.lexer.token_end;
244
+ let [content_end, feature_end, matched] = this.scan_matching_paren();
245
+ if (matched) {
255
246
  let query = this.create_node(36, feature_start, feature_end);
256
247
  let trimmed = trim_boundaries(this.source, content_start, content_end);
257
248
  if (trimmed) {
@@ -392,15 +383,8 @@ var AtRulePreludeParser = class {
392
383
  let url_start = this.lexer.token_start;
393
384
  let url_end = this.lexer.token_end;
394
385
  if (this.lexer.token_type === 2) {
395
- let paren_depth = 1;
396
- while (this.lexer.pos < this.prelude_end && paren_depth > 0) {
397
- let tokenType = this.next_token();
398
- if (tokenType === 21 || tokenType === 2) paren_depth++;
399
- else if (tokenType === 22) {
400
- paren_depth--;
401
- if (paren_depth === 0) url_end = this.lexer.token_end;
402
- } else if (tokenType === 26) break;
403
- }
386
+ let [, close_end] = this.scan_matching_paren();
387
+ url_end = close_end;
404
388
  }
405
389
  return this.create_node(18, url_start, url_end);
406
390
  }
@@ -417,17 +401,10 @@ var AtRulePreludeParser = class {
417
401
  let content_length = 0;
418
402
  if (this.lexer.token_type === 2) {
419
403
  content_start = this.lexer.pos;
420
- let paren_depth = 1;
421
- while (this.lexer.pos < this.prelude_end && paren_depth > 0) {
422
- let tokenType = this.next_token();
423
- if (tokenType === 21 || tokenType === 2) paren_depth++;
424
- else if (tokenType === 22) {
425
- paren_depth--;
426
- if (paren_depth === 0) {
427
- content_length = this.lexer.token_start - content_start;
428
- layer_end = this.lexer.token_end;
429
- }
430
- } else if (tokenType === 26) break;
404
+ let [content_end, close_end, matched] = this.scan_matching_paren();
405
+ if (matched) {
406
+ content_length = content_end - content_start;
407
+ layer_end = close_end;
431
408
  }
432
409
  }
433
410
  let layer_node = this.create_node(37, layer_start, layer_end);
@@ -448,23 +425,11 @@ var AtRulePreludeParser = class {
448
425
  const saved = this.lexer.save_position();
449
426
  this.next_token();
450
427
  if (this.lexer.token_type === 2) {
451
- if (str_equals("supports", this.source.substring(this.lexer.token_start, this.lexer.token_end - 1))) {
428
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end - 1);
429
+ if (str_equals("supports", text)) {
452
430
  let supports_start = this.lexer.token_start;
453
431
  let content_start = this.lexer.token_end;
454
- let paren_depth = 1;
455
- let supports_end = this.lexer.token_end;
456
- let content_end = content_start;
457
- while (this.lexer.pos < this.prelude_end && paren_depth > 0) {
458
- let tokenType = this.next_token();
459
- if (tokenType === 21 || tokenType === 2) paren_depth++;
460
- else if (tokenType === 22) {
461
- paren_depth--;
462
- if (paren_depth === 0) {
463
- content_end = this.lexer.token_start;
464
- supports_end = this.lexer.token_end;
465
- }
466
- } else if (tokenType === 26) break;
467
- }
432
+ let [content_end, supports_end] = this.scan_matching_paren();
468
433
  let supports_node = this.create_node(36, supports_start, supports_end);
469
434
  let trimmed = trim_boundaries(this.source, content_start, content_end);
470
435
  if (trimmed) {
@@ -526,14 +491,7 @@ var AtRulePreludeParser = class {
526
491
  this.next_token();
527
492
  let paren_start = this.lexer.token_start;
528
493
  let content_start = this.lexer.pos;
529
- let depth = 1;
530
- while (this.lexer.pos < this.prelude_end && depth > 0) {
531
- this.next_token();
532
- if (this.lexer.token_type === 21 || this.lexer.token_type === 2) depth++;
533
- else if (this.lexer.token_type === 22) depth--;
534
- }
535
- let content_end = this.lexer.token_start;
536
- let paren_end = this.lexer.token_end;
494
+ let [content_end, paren_end] = this.scan_matching_paren();
537
495
  let scope_node = this.create_node(41, paren_start, paren_end);
538
496
  let trimmed = trim_boundaries(this.source, content_start, content_end);
539
497
  if (trimmed) {
@@ -543,7 +501,8 @@ var AtRulePreludeParser = class {
543
501
  nodes.push(scope_node);
544
502
  } else if (token_type === 1) {
545
503
  this.next_token();
546
- if (str_equals("to", this.source.substring(this.lexer.token_start, this.lexer.token_end))) nodes.push(this.create_node(38, this.lexer.token_start, this.lexer.token_end));
504
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
505
+ if (str_equals("to", text)) nodes.push(this.create_node(38, this.lexer.token_start, this.lexer.token_end));
547
506
  } else this.next_token();
548
507
  }
549
508
  return nodes;
@@ -1,5 +1,5 @@
1
1
  import { l as CSSDataArena } from "./arena-Bx5vp6Sl.js";
2
- import { n as Lexer } from "./tokenize-DeZYFa9R.js";
2
+ import { n as Lexer } from "./tokenize-CDEnYcVG.js";
3
3
  import { d as Declaration } from "./node-types-D41wr72y.js";
4
4
  //#region src/parse-declaration.d.ts
5
5
  /** @internal */
@@ -1,8 +1,8 @@
1
- import { t as Lexer } from "./tokenize-BiRLfVbm.js";
1
+ import { t as Lexer } from "./tokenize-DK24ACbY.js";
2
2
  import { l as CSSDataArena } from "./arena-CFwM4uQs.js";
3
3
  import { i as is_vendor_prefixed } from "./parse-dimension-ouWXNHdx.js";
4
4
  import { t as CSSNode } from "./css-node-Cf8K9nWd.js";
5
- import { i as trim_boundaries } from "./parse-utils-D7srNG13.js";
5
+ import { i as trim_boundaries, t as skip_whitespace_and_comments_backward } from "./parse-utils-D7srNG13.js";
6
6
  import { ValueParser } from "./parse-value.js";
7
7
  //#region src/parse-declaration.ts
8
8
  /** @internal */
@@ -70,39 +70,70 @@ var DeclarationParser = class {
70
70
  lexer.restore_position(has_delimiter_prefix ? initial_saved : saved);
71
71
  return null;
72
72
  }
73
- lexer.next_token_fast(true);
73
+ lexer.pos = lexer.token_end;
74
+ lexer.skip_whitespace_in_range(this.source.length);
74
75
  let declaration = this.arena.create_node(4, prop_start, 0, decl_line, decl_column);
75
76
  this.arena.set_content_start_delta(declaration, 0);
76
77
  this.arena.set_content_length(declaration, prop_end - prop_start);
77
- let value_start = lexer.token_start;
78
- let value_start_line = lexer.token_line;
79
- let value_start_column = lexer.token_column;
78
+ let value_start = lexer.pos;
79
+ let value_start_line = lexer.line;
80
+ let value_start_column = lexer.column;
80
81
  let value_end = value_start;
81
82
  let has_important = false;
82
- let last_end = lexer.token_end;
83
+ let last_end = value_start;
83
84
  let paren_depth = 0;
84
- while (lexer.token_type !== 26 && lexer.token_start < end) {
85
- let token_type = lexer.token_type;
86
- if (token_type === 21 || token_type === 2) paren_depth++;
87
- else if (token_type === 22) paren_depth--;
88
- if (token_type === 17 && paren_depth === 0) break;
89
- if (token_type === 24 && paren_depth === 0) break;
90
- if (token_type === 23) {
85
+ while (true) {
86
+ let stop_ch = lexer.skip_to_declaration_stop(end);
87
+ if (stop_ch === 40) {
88
+ paren_depth++;
89
+ lexer.pos++;
90
+ continue;
91
+ }
92
+ if (stop_ch === 41) {
93
+ paren_depth--;
94
+ lexer.pos++;
95
+ continue;
96
+ }
97
+ if (stop_ch === 59 && paren_depth === 0) {
98
+ value_end = skip_whitespace_and_comments_backward(this.source, lexer.pos, value_start);
99
+ lexer.next_token_fast(false);
100
+ break;
101
+ }
102
+ if (stop_ch === 125 && paren_depth === 0) {
103
+ if (lexer.pos === value_start) {
104
+ last_end = lexer.pos + 1;
105
+ value_end = value_start;
106
+ } else {
107
+ last_end = skip_whitespace_and_comments_backward(this.source, lexer.pos, value_start);
108
+ value_end = last_end;
109
+ }
110
+ lexer.next_token_fast(false);
111
+ break;
112
+ }
113
+ if (stop_ch === 123) {
91
114
  lexer.restore_position(saved);
92
115
  return null;
93
116
  }
94
- if (token_type === 9 && this.source[lexer.token_start] === "!") {
95
- value_end = lexer.token_start;
117
+ if (stop_ch === 33) {
118
+ value_end = lexer.pos;
119
+ lexer.pos++;
96
120
  if (lexer.next_token_fast(true) === 1) {
97
121
  has_important = true;
98
122
  last_end = lexer.token_end;
99
123
  lexer.next_token_fast(true);
100
124
  break;
101
125
  }
126
+ last_end = lexer.token_end;
127
+ value_end = last_end;
128
+ continue;
102
129
  }
103
- last_end = lexer.token_end;
104
- value_end = last_end;
105
- lexer.next_token_fast(true);
130
+ if (stop_ch === 0) {
131
+ last_end = skip_whitespace_and_comments_backward(this.source, lexer.pos, value_start);
132
+ value_end = last_end;
133
+ lexer.next_token_fast(false);
134
+ break;
135
+ }
136
+ lexer.pos++;
106
137
  }
107
138
  let trimmed = trim_boundaries(this.source, value_start, value_end);
108
139
  if (trimmed) {
@@ -141,7 +172,10 @@ var DeclarationParser = class {
141
172
  function parse_declaration(source) {
142
173
  const arena = new CSSDataArena(CSSDataArena.capacity_for_source(source.length));
143
174
  const decl_index = new DeclarationParser(arena, source).parse_declaration(0, source.length);
144
- if (decl_index === null) return new CSSNode(arena, source, arena.create_node(4, 0, 0, 1, 1));
175
+ if (decl_index === null) {
176
+ const empty = arena.create_node(4, 0, 0, 1, 1);
177
+ return new CSSNode(arena, source, empty);
178
+ }
145
179
  return new CSSNode(arena, source, decl_index);
146
180
  }
147
181
  //#endregion
@@ -30,7 +30,6 @@ declare class SelectorParser {
30
30
  private parse_nth_expression;
31
31
  private find_of_keyword;
32
32
  private create_node;
33
- private create_node_at;
34
33
  private skip_whitespace;
35
34
  }
36
35
  declare function parse_selector_list(source: string): SelectorList;
@@ -1,4 +1,4 @@
1
- import { t as Lexer } from "./tokenize-BiRLfVbm.js";
1
+ import { t as Lexer } from "./tokenize-DK24ACbY.js";
2
2
  import { l as CSSDataArena } from "./arena-CFwM4uQs.js";
3
3
  import { a as is_whitespace, n as is_combinator, o as str_equals } from "./parse-dimension-ouWXNHdx.js";
4
4
  import { t as CSSNode } from "./css-node-Cf8K9nWd.js";
@@ -69,7 +69,7 @@ var SelectorParser = class {
69
69
  if (this.lexer.token_type === 9) {
70
70
  let ch = this.source.charCodeAt(this.lexer.token_start);
71
71
  if (ch === 62 || ch === 43 || ch === 126) {
72
- let combinator = this.create_node_at(27, this.lexer.token_start, this.lexer.token_end, this.lexer.token_line, this.lexer.token_column);
72
+ let combinator = this.create_node(27, this.lexer.token_start, this.lexer.token_end);
73
73
  first_component = combinator;
74
74
  chain_tail = combinator;
75
75
  this.skip_whitespace();
@@ -205,12 +205,13 @@ var SelectorParser = class {
205
205
  }
206
206
  this.lexer.next_token_fast(false);
207
207
  if (this.lexer.token_type === 9) {
208
- 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);
208
+ let ch = this.source.charCodeAt(this.lexer.token_start);
209
+ if (is_combinator(ch)) return this.create_node(27, this.lexer.token_start, this.lexer.token_end);
209
210
  }
210
211
  if (has_whitespace) {
211
212
  this.lexer.restore_position(saved_whitespace_start);
212
213
  this.skip_whitespace();
213
- return this.create_node_at(27, saved_whitespace_start.pos, this.lexer.pos, saved_whitespace_start.line, saved_whitespace_start.column);
214
+ return this.create_node(27, saved_whitespace_start.pos, this.lexer.pos, saved_whitespace_start.line, saved_whitespace_start.column);
214
215
  }
215
216
  this.lexer.restore_position(saved_whitespace_start);
216
217
  return null;
@@ -293,7 +294,8 @@ var SelectorParser = class {
293
294
  else pos++;
294
295
  }
295
296
  } else while (pos < end) {
296
- if (is_whitespace(this.source.charCodeAt(pos))) break;
297
+ let c = this.source.charCodeAt(pos);
298
+ if (is_whitespace(c)) break;
297
299
  pos++;
298
300
  }
299
301
  value_end = pos;
@@ -433,44 +435,23 @@ var SelectorParser = class {
433
435
  }
434
436
  return -1;
435
437
  }
436
- create_node(type, start, end) {
437
- let node = this.arena.create_node(type, start, end - start, this.lexer.token_line, this.lexer.token_column);
438
- this.arena.set_content_start_delta(node, 0);
439
- this.arena.set_content_length(node, end - start);
440
- return node;
441
- }
442
- create_node_at(type, start, end, line, column) {
438
+ create_node(type, start, end, line = this.lexer.token_line, column = this.lexer.token_column) {
443
439
  let node = this.arena.create_node(type, start, end - start, line, column);
444
440
  this.arena.set_content_start_delta(node, 0);
445
441
  this.arena.set_content_length(node, end - start);
446
442
  return node;
447
443
  }
448
444
  skip_whitespace() {
449
- while (this.lexer.pos < this.selector_end) {
450
- let ch = this.source.charCodeAt(this.lexer.pos);
451
- if (is_whitespace(ch)) {
452
- this.lexer.advance();
453
- continue;
454
- }
455
- if (ch === 47 && this.lexer.pos + 1 < this.selector_end && this.source.charCodeAt(this.lexer.pos + 1) === 42) {
456
- this.lexer.advance(2);
457
- while (this.lexer.pos < this.selector_end) {
458
- if (this.source.charCodeAt(this.lexer.pos) === 42 && this.lexer.pos + 1 < this.selector_end && this.source.charCodeAt(this.lexer.pos + 1) === 47) {
459
- this.lexer.advance(2);
460
- break;
461
- }
462
- this.lexer.advance();
463
- }
464
- continue;
465
- }
466
- break;
467
- }
445
+ this.lexer.skip_whitespace_in_range(this.selector_end);
468
446
  }
469
447
  };
470
448
  function parse_selector_list(source) {
471
449
  const arena = new CSSDataArena(CSSDataArena.capacity_for_source(source.length));
472
450
  const selector_index = new SelectorParser(arena, source).parse_selector(0, source.length);
473
- if (selector_index === null) return new CSSNode(arena, source, arena.create_node(20, 0, 0, 1, 1));
451
+ if (selector_index === null) {
452
+ const empty = arena.create_node(20, 0, 0, 1, 1);
453
+ return new CSSNode(arena, source, empty);
454
+ }
474
455
  return new CSSNode(arena, source, selector_index);
475
456
  }
476
457
  function parse_selector(source) {
@@ -1,6 +1,6 @@
1
1
  import { l as CSSDataArena } from "./arena-CFwM4uQs.js";
2
2
  import { t as CSSNode } from "./css-node-Cf8K9nWd.js";
3
- import { t as ValueNodeParser } from "./value-node-parser-CZ47v3hp.js";
3
+ import { t as ValueNodeParser } from "./value-node-parser-jRoWXgOr.js";
4
4
  //#region src/parse-value.ts
5
5
  /** @internal */
6
6
  var ValueParser = class {
@@ -28,7 +28,8 @@ var ValueParser = class {
28
28
  */
29
29
  function parse_value(value_string) {
30
30
  const arena = new CSSDataArena(CSSDataArena.capacity_for_source(value_string.length));
31
- return new CSSNode(arena, value_string, new ValueParser(arena, value_string).parse_value(0, value_string.length, 1, 1));
31
+ const value_node_index = new ValueParser(arena, value_string).parse_value(0, value_string.length, 1, 1);
32
+ return new CSSNode(arena, value_string, value_node_index);
32
33
  }
33
34
  //#endregion
34
35
  export { ValueParser, parse_value };
package/dist/parse.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { l as CSSDataArena } from "./arena-Bx5vp6Sl.js";
2
- import { t as CommentInfo } from "./tokenize-DeZYFa9R.js";
2
+ import { t as CommentInfo } from "./tokenize-CDEnYcVG.js";
3
3
  import { z as StyleSheet } from "./node-types-D41wr72y.js";
4
4
  //#region src/parse.d.ts
5
5
  interface ParserOptions {
@@ -27,6 +27,7 @@ declare class Parser {
27
27
  private is_eof;
28
28
  parse(): StyleSheet;
29
29
  private parse_rule;
30
+ private parse_block_children;
30
31
  private parse_style_rule;
31
32
  private parse_selector;
32
33
  private parse_declaration;
package/dist/parse.js CHANGED
@@ -1,7 +1,7 @@
1
- import { t as Lexer } from "./tokenize-BiRLfVbm.js";
1
+ import { t as Lexer } from "./tokenize-DK24ACbY.js";
2
2
  import { l as CSSDataArena } from "./arena-CFwM4uQs.js";
3
3
  import { t as CSSNode } from "./css-node-Cf8K9nWd.js";
4
- import { i as trim_boundaries } from "./parse-utils-D7srNG13.js";
4
+ import { i as trim_boundaries, t as skip_whitespace_and_comments_backward } from "./parse-utils-D7srNG13.js";
5
5
  import { SelectorParser } from "./parse-selector.js";
6
6
  import { AtRulePreludeParser } from "./parse-atrule-prelude.js";
7
7
  import { DeclarationParser } from "./parse-declaration.js";
@@ -79,35 +79,24 @@ var Parser = class {
79
79
  if (this.peek_type() === 3) return this.parse_atrule();
80
80
  return this.parse_style_rule();
81
81
  }
82
- parse_style_rule() {
83
- if (this.is_eof()) return null;
84
- let rule_start = this.lexer.token_start;
85
- let rule_line = this.lexer.token_line;
86
- let rule_column = this.lexer.token_column;
87
- let style_rule = this.arena.create_node(2, rule_start, 0, rule_line, rule_column);
88
- let selector = this.parse_selector();
89
- if (this.peek_type() !== 23) return null;
90
- let block_start = this.lexer.token_end;
91
- this.next_token();
92
- this.arena.set_flag(style_rule, 8);
93
- let block_line = this.lexer.token_line;
94
- let block_column = this.lexer.token_column;
95
- let block_node = this.arena.create_node(7, block_start, 0, block_line, block_column);
82
+ parse_block_children(owner, declarations_only) {
96
83
  let first_child = 0;
97
84
  let last_child = 0;
98
85
  while (!this.is_eof()) {
99
86
  let token_type = this.peek_type();
100
87
  if (token_type === 24) break;
101
88
  let child = null;
102
- if (token_type === 3) {
89
+ if (!declarations_only && token_type === 3) {
103
90
  child = this.parse_atrule();
104
91
  if (child === null) this.next_token();
105
92
  } else {
106
93
  child = this.parse_declaration();
107
- if (child === null) {
94
+ if (child !== null) this.arena.set_flag(owner, 32);
95
+ else if (declarations_only) this.next_token();
96
+ else {
108
97
  child = this.parse_style_rule();
109
98
  if (child === null) this.next_token();
110
- } else this.arena.set_flag(style_rule, 32);
99
+ }
111
100
  }
112
101
  if (child !== null) {
113
102
  if (first_child === 0) first_child = child;
@@ -115,6 +104,23 @@ var Parser = class {
115
104
  last_child = child;
116
105
  }
117
106
  }
107
+ return first_child;
108
+ }
109
+ parse_style_rule() {
110
+ if (this.is_eof()) return null;
111
+ let rule_start = this.lexer.token_start;
112
+ let rule_line = this.lexer.token_line;
113
+ let rule_column = this.lexer.token_column;
114
+ let style_rule = this.arena.create_node(2, rule_start, 0, rule_line, rule_column);
115
+ let selector = this.parse_selector();
116
+ if (this.peek_type() !== 23) return null;
117
+ let block_start = this.lexer.token_end;
118
+ this.next_token();
119
+ this.arena.set_flag(style_rule, 8);
120
+ let block_line = this.lexer.token_line;
121
+ let block_column = this.lexer.token_column;
122
+ let block_node = this.arena.create_node(7, block_start, 0, block_line, block_column);
123
+ let first_child = this.parse_block_children(style_rule, false);
118
124
  let block_end = this.lexer.token_start;
119
125
  let rule_end = this.lexer.token_end;
120
126
  if (this.peek_type() === 24) {
@@ -138,8 +144,9 @@ var Parser = class {
138
144
  let selector_line = this.lexer.token_line;
139
145
  let selector_column = this.lexer.token_column;
140
146
  let last_end = this.lexer.token_end;
141
- while (!this.is_eof() && this.peek_type() !== 23) {
142
- last_end = this.lexer.token_end;
147
+ if (this.peek_type() !== 23) {
148
+ this.lexer.skip_to_unquoted(123);
149
+ last_end = skip_whitespace_and_comments_backward(this.source, this.lexer.pos, selector_start);
143
150
  this.next_token();
144
151
  }
145
152
  if (this.parse_selectors_enabled && this.selector_parser) {
@@ -151,13 +158,11 @@ var Parser = class {
151
158
  }
152
159
  parse_declaration() {
153
160
  const token_type = this.peek_type();
154
- if (token_type === 1 || token_type === 3 || token_type === 4) return this.declaration_parser.parse_declaration_with_lexer(this.lexer, this.source.length);
155
161
  if (token_type === 9 || token_type === 21 || token_type === 22 || token_type === 19 || token_type === 20 || token_type === 18 || token_type === 16) {
156
162
  const char_code = this.source.charCodeAt(this.lexer.token_start);
157
163
  if (char_code === 46 || char_code === 62 || char_code === 43 || char_code === 126 || char_code === 38) return null;
158
- return this.declaration_parser.parse_declaration_with_lexer(this.lexer, this.source.length);
159
- }
160
- return null;
164
+ } else if (token_type !== 1 && token_type !== 3 && token_type !== 4) return null;
165
+ return this.declaration_parser.parse_declaration_with_lexer(this.lexer, this.source.length);
161
166
  }
162
167
  parse_atrule() {
163
168
  if (this.peek_type() !== 3) return null;
@@ -200,39 +205,8 @@ var Parser = class {
200
205
  let block_line = this.lexer.token_line;
201
206
  let block_column = this.lexer.token_column;
202
207
  let block_node = this.arena.create_node(7, block_start, 0, block_line, block_column);
203
- let has_declarations = this.atrule_has_declarations(at_rule_name);
204
- let first_child = 0;
205
- let last_child = 0;
206
- if (has_declarations) while (!this.is_eof()) {
207
- if (this.peek_type() === 24) break;
208
- let declaration = this.parse_declaration();
209
- if (declaration === null) this.next_token();
210
- else {
211
- if (first_child === 0) first_child = declaration;
212
- else this.arena.set_next_sibling(last_child, declaration);
213
- last_child = declaration;
214
- }
215
- }
216
- else while (!this.is_eof()) {
217
- let token_type = this.peek_type();
218
- if (token_type === 24) break;
219
- let child = null;
220
- if (token_type === 3) {
221
- child = this.parse_atrule();
222
- if (child === null) this.next_token();
223
- } else {
224
- child = this.parse_declaration();
225
- if (child === null) {
226
- child = this.parse_style_rule();
227
- if (child === null) this.next_token();
228
- }
229
- }
230
- if (child !== null) {
231
- if (first_child === 0) first_child = child;
232
- else this.arena.set_next_sibling(last_child, child);
233
- last_child = child;
234
- }
235
- }
208
+ let declarations_only = this.atrule_has_declarations(at_rule_name);
209
+ let first_child = this.parse_block_children(at_rule, declarations_only);
236
210
  if (this.peek_type() === 24) {
237
211
  let block_end = this.lexer.token_start;
238
212
  last_end = this.lexer.token_end;
@@ -72,6 +72,28 @@ declare class Lexer {
72
72
  seek(pos: number, line: number, column?: number): void;
73
73
  next_token_fast(skip_whitespace?: boolean): TokenType;
74
74
  private _scan_newlines;
75
+ private _skip_comment;
76
+ /**
77
+ * Fast-forward to the next `target` char that isn't inside a string or comment, without
78
+ * fully tokenizing what's in between — used by callers that only need to know *where a
79
+ * construct ends* (e.g. a selector's `{`), since the sub-parser for that construct
80
+ * re-tokenizes the span properly afterward.
81
+ *
82
+ * Strings go through consume_string and comments through _skip_comment, so escapes and
83
+ * on_comment callbacks behave identically to real tokenization. Ordinary characters cost
84
+ * one SKIP_SCAN_INTERESTING lookup each, same as consume_ident_or_function's inner loop.
85
+ *
86
+ * Returns true with this.pos at target, or false with this.pos at source_length.
87
+ */
88
+ skip_to_unquoted(target: number): boolean;
89
+ /**
90
+ * Same approach as skip_to_unquoted, but stops at any of `; { } ( ) !` — the stop
91
+ * characters parse-declaration.ts's value scan needs (paren depth, statement end,
92
+ * !important). The caller decides what each one means; this only locates the next one.
93
+ *
94
+ * Returns the stop character's code (this.pos left at it), or 0 at `end` if none found.
95
+ */
96
+ skip_to_declaration_stop(end: number): number;
75
97
  consume_whitespace(start_line: number, start_column: number): TokenType;
76
98
  consume_string(quote: number, start_line: number, start_column: number): TokenType;
77
99
  consume_hex_escape(): void;
@@ -22,9 +22,6 @@ function is_hex_digit(ch) {
22
22
  function is_alpha(ch) {
23
23
  return ch < 128 && (char_types[ch] & CHAR_ALPHA) !== 0;
24
24
  }
25
- function is_whitespace(ch) {
26
- return ch < 128 && (char_types[ch] & 8) !== 0;
27
- }
28
25
  function is_ident_start(ch) {
29
26
  if (ch >= 128) return true;
30
27
  if (ch === 95) return true;
@@ -95,6 +92,20 @@ const CHAR_QUESTION_MARK = 63;
95
92
  const CHAR_CARRIAGE_RETURN = 13;
96
93
  const CHAR_LINE_FEED = 10;
97
94
  const CHAR_FORM_FEED = 12;
95
+ const SKIP_SCAN_INTERESTING = /* @__PURE__ */ new Uint8Array(128);
96
+ SKIP_SCAN_INTERESTING[CHAR_DOUBLE_QUOTE] = 1;
97
+ SKIP_SCAN_INTERESTING[CHAR_SINGLE_QUOTE] = 1;
98
+ SKIP_SCAN_INTERESTING[CHAR_FORWARD_SLASH] = 1;
99
+ SKIP_SCAN_INTERESTING[CHAR_LINE_FEED] = 1;
100
+ SKIP_SCAN_INTERESTING[CHAR_CARRIAGE_RETURN] = 1;
101
+ SKIP_SCAN_INTERESTING[CHAR_FORM_FEED] = 1;
102
+ const DECL_VALUE_STOP = /* @__PURE__ */ new Uint8Array(128);
103
+ DECL_VALUE_STOP[CHAR_SEMICOLON] = 1;
104
+ DECL_VALUE_STOP[CHAR_LEFT_BRACE] = 1;
105
+ DECL_VALUE_STOP[CHAR_RIGHT_BRACE] = 1;
106
+ DECL_VALUE_STOP[CHAR_LEFT_PAREN] = 1;
107
+ DECL_VALUE_STOP[CHAR_RIGHT_PAREN] = 1;
108
+ DECL_VALUE_STOP[CHAR_EXCLAMATION] = 1;
98
109
  /** @internal */
99
110
  var Lexer = class {
100
111
  source;
@@ -134,15 +145,23 @@ var Lexer = class {
134
145
  const source = this.source;
135
146
  const source_length = source.length;
136
147
  while (true) {
137
- if (skip_whitespace) while (this.pos < source_length) {
138
- let ch = source.charCodeAt(this.pos);
139
- if (ch >= 128 || (char_types[ch] & 24) === 0) break;
140
- this.pos++;
141
- if ((char_types[ch] & 16) !== 0) {
142
- if (ch === CHAR_CARRIAGE_RETURN && this.pos < source_length && source.charCodeAt(this.pos) === CHAR_LINE_FEED) this.pos++;
143
- this._line++;
144
- this._line_offset = this.pos;
148
+ if (skip_whitespace) {
149
+ let pos = this.pos;
150
+ let line = this._line;
151
+ let line_offset = this._line_offset;
152
+ while (pos < source_length) {
153
+ let ch = source.charCodeAt(pos);
154
+ if (ch >= 128 || (char_types[ch] & 24) === 0) break;
155
+ pos++;
156
+ if ((char_types[ch] & 16) !== 0) {
157
+ if (ch === CHAR_CARRIAGE_RETURN && pos < source_length && source.charCodeAt(pos) === CHAR_LINE_FEED) pos++;
158
+ line++;
159
+ line_offset = pos;
160
+ }
145
161
  }
162
+ this.pos = pos;
163
+ this._line = line;
164
+ this._line_offset = line_offset;
146
165
  }
147
166
  if (this.pos >= source_length) return this.make_token(26, this.pos, this.pos);
148
167
  let ch = source.charCodeAt(this.pos);
@@ -180,57 +199,33 @@ var Lexer = class {
180
199
  }
181
200
  if (ch < 128 && (char_types[ch] & 24) !== 0) return this.consume_whitespace(start_line, start_column);
182
201
  if (ch === CHAR_FORWARD_SLASH && this.pos + 1 < source_length && source.charCodeAt(this.pos + 1) === CHAR_ASTERISK) {
183
- let comment_start = start;
184
- let comment_line = start_line;
185
- let comment_column = start_column;
186
- this.pos += 2;
187
- let end_idx = source.indexOf("*/", this.pos);
188
- if (end_idx < 0) {
189
- this._scan_newlines(this.pos, source_length);
190
- this.pos = source_length;
191
- } else {
192
- this._scan_newlines(this.pos, end_idx);
193
- this.pos = end_idx + 2;
194
- }
195
- if (this.on_comment) this.on_comment({
196
- start: comment_start,
197
- end: this.pos,
198
- length: this.pos - comment_start,
199
- line: comment_line,
200
- column: comment_column
201
- });
202
+ this._skip_comment();
202
203
  continue;
203
204
  }
204
- if (ch === CHAR_DOUBLE_QUOTE || ch === CHAR_SINGLE_QUOTE) return this.consume_string(ch, start_line, start_column);
205
- if (ch < 128 && (char_types[ch] & 2) !== 0) return this.consume_number(start_line, start_column);
206
- if (ch === CHAR_DOT) {
205
+ if (is_ident_start(ch)) return this.consume_ident_or_function(start_line, start_column);
206
+ if (ch === CHAR_HYPHEN) {
207
207
  let next = this.pos + 1 < source_length ? source.charCodeAt(this.pos + 1) : 0;
208
- if (next < 128 && (char_types[next] & 2) !== 0) return this.consume_number(start_line, start_column);
209
- }
210
- if (ch === CHAR_LESS_THAN && this.pos + 3 < source_length) {
211
- if (source.charCodeAt(this.pos + 1) === CHAR_EXCLAMATION && source.charCodeAt(this.pos + 2) === CHAR_HYPHEN && source.charCodeAt(this.pos + 3) === CHAR_HYPHEN) {
212
- this.pos += 4;
213
- return this.make_token(14, start, this.pos, start_line, start_column);
214
- }
215
- }
216
- if (ch === CHAR_HYPHEN && this.pos + 2 < source_length) {
217
- if (source.charCodeAt(this.pos + 1) === CHAR_HYPHEN && source.charCodeAt(this.pos + 2) === CHAR_GREATER_THAN) {
208
+ if (next === CHAR_HYPHEN && this.pos + 2 < source_length && source.charCodeAt(this.pos + 2) === CHAR_GREATER_THAN) {
218
209
  this.pos += 3;
219
210
  return this.make_token(15, start, this.pos, start_line, start_column);
220
211
  }
221
- }
222
- if (ch === CHAR_AT_SIGN) return this.consume_at_keyword(start_line, start_column);
223
- if (ch === CHAR_HASH) return this.consume_hash(start_line, start_column);
224
- if (is_ident_start(ch)) return this.consume_ident_or_function(start_line, start_column);
225
- if (ch === CHAR_HYPHEN) {
226
- let next = this.pos + 1 < source_length ? source.charCodeAt(this.pos + 1) : 0;
227
212
  if (is_ident_start(next) || next === CHAR_HYPHEN) return this.consume_ident_or_function(start_line, start_column);
213
+ if (next < 128 && (char_types[next] & 2) !== 0) return this.consume_number(start_line, start_column);
214
+ if (next === CHAR_DOT) {
215
+ let next2 = this.pos + 2 < source_length ? source.charCodeAt(this.pos + 2) : 0;
216
+ if (next2 < 128 && (char_types[next2] & 2) !== 0) return this.consume_number(start_line, start_column);
217
+ }
228
218
  }
229
219
  if (ch === CHAR_BACKSLASH) {
230
220
  let next = this.pos + 1 < source_length ? source.charCodeAt(this.pos + 1) : 0;
231
221
  if (next !== 0 && !is_newline(next)) return this.consume_ident_or_function(start_line, start_column);
232
222
  }
233
- if (ch === CHAR_HYPHEN || ch === CHAR_PLUS) {
223
+ if (ch < 128 && (char_types[ch] & 2) !== 0) return this.consume_number(start_line, start_column);
224
+ if (ch === CHAR_DOT) {
225
+ let next = this.pos + 1 < source_length ? source.charCodeAt(this.pos + 1) : 0;
226
+ if (next < 128 && (char_types[next] & 2) !== 0) return this.consume_number(start_line, start_column);
227
+ }
228
+ if (ch === CHAR_PLUS) {
234
229
  let next = this.pos + 1 < source_length ? source.charCodeAt(this.pos + 1) : 0;
235
230
  if (next < 128 && (char_types[next] & 2) !== 0) return this.consume_number(start_line, start_column);
236
231
  if (next === CHAR_DOT) {
@@ -238,6 +233,15 @@ var Lexer = class {
238
233
  if (next2 < 128 && (char_types[next2] & 2) !== 0) return this.consume_number(start_line, start_column);
239
234
  }
240
235
  }
236
+ if (ch === CHAR_DOUBLE_QUOTE || ch === CHAR_SINGLE_QUOTE) return this.consume_string(ch, start_line, start_column);
237
+ if (ch === CHAR_AT_SIGN) return this.consume_at_keyword(start_line, start_column);
238
+ if (ch === CHAR_HASH) return this.consume_hash(start_line, start_column);
239
+ if (ch === CHAR_LESS_THAN && this.pos + 3 < source_length) {
240
+ if (source.charCodeAt(this.pos + 1) === CHAR_EXCLAMATION && source.charCodeAt(this.pos + 2) === CHAR_HYPHEN && source.charCodeAt(this.pos + 3) === CHAR_HYPHEN) {
241
+ this.pos += 4;
242
+ return this.make_token(14, start, this.pos, start_line, start_column);
243
+ }
244
+ }
241
245
  this.pos++;
242
246
  return this.make_token(9, start, this.pos, start_line, start_column);
243
247
  }
@@ -259,21 +263,140 @@ var Lexer = class {
259
263
  }
260
264
  }
261
265
  }
266
+ _skip_comment() {
267
+ const source = this.source;
268
+ const source_length = source.length;
269
+ let comment_start = this.pos;
270
+ let comment_line = this._line;
271
+ let comment_column = this.pos - this._line_offset + 1;
272
+ this.pos += 2;
273
+ let end_idx = source.indexOf("*/", this.pos);
274
+ if (end_idx < 0) {
275
+ this._scan_newlines(this.pos, source_length);
276
+ this.pos = source_length;
277
+ } else {
278
+ this._scan_newlines(this.pos, end_idx);
279
+ this.pos = end_idx + 2;
280
+ }
281
+ if (this.on_comment) this.on_comment({
282
+ start: comment_start,
283
+ end: this.pos,
284
+ length: this.pos - comment_start,
285
+ line: comment_line,
286
+ column: comment_column
287
+ });
288
+ }
289
+ /**
290
+ * Fast-forward to the next `target` char that isn't inside a string or comment, without
291
+ * fully tokenizing what's in between — used by callers that only need to know *where a
292
+ * construct ends* (e.g. a selector's `{`), since the sub-parser for that construct
293
+ * re-tokenizes the span properly afterward.
294
+ *
295
+ * Strings go through consume_string and comments through _skip_comment, so escapes and
296
+ * on_comment callbacks behave identically to real tokenization. Ordinary characters cost
297
+ * one SKIP_SCAN_INTERESTING lookup each, same as consume_ident_or_function's inner loop.
298
+ *
299
+ * Returns true with this.pos at target, or false with this.pos at source_length.
300
+ */
301
+ skip_to_unquoted(target) {
302
+ const source = this.source;
303
+ const source_length = source.length;
304
+ let pos = this.pos;
305
+ while (pos < source_length) {
306
+ let ch = source.charCodeAt(pos);
307
+ if (ch === target) {
308
+ this.pos = pos;
309
+ return true;
310
+ }
311
+ if (ch >= 128 || SKIP_SCAN_INTERESTING[ch] === 0) {
312
+ pos++;
313
+ continue;
314
+ }
315
+ if (ch === CHAR_DOUBLE_QUOTE || ch === CHAR_SINGLE_QUOTE) {
316
+ this.pos = pos;
317
+ this.consume_string(ch, this._line, 1);
318
+ pos = this.pos;
319
+ continue;
320
+ }
321
+ if (ch === CHAR_FORWARD_SLASH) {
322
+ if (pos + 1 < source_length && source.charCodeAt(pos + 1) === CHAR_ASTERISK) {
323
+ this.pos = pos;
324
+ this._skip_comment();
325
+ pos = this.pos;
326
+ } else pos++;
327
+ continue;
328
+ }
329
+ pos++;
330
+ if (ch === CHAR_CARRIAGE_RETURN && pos < source_length && source.charCodeAt(pos) === CHAR_LINE_FEED) pos++;
331
+ this._line++;
332
+ this._line_offset = pos;
333
+ }
334
+ this.pos = pos;
335
+ return false;
336
+ }
337
+ /**
338
+ * Same approach as skip_to_unquoted, but stops at any of `; { } ( ) !` — the stop
339
+ * characters parse-declaration.ts's value scan needs (paren depth, statement end,
340
+ * !important). The caller decides what each one means; this only locates the next one.
341
+ *
342
+ * Returns the stop character's code (this.pos left at it), or 0 at `end` if none found.
343
+ */
344
+ skip_to_declaration_stop(end) {
345
+ const source = this.source;
346
+ let pos = this.pos;
347
+ while (pos < end) {
348
+ let ch = source.charCodeAt(pos);
349
+ if (ch < 128 && DECL_VALUE_STOP[ch] !== 0) {
350
+ this.pos = pos;
351
+ return ch;
352
+ }
353
+ if (ch >= 128 || SKIP_SCAN_INTERESTING[ch] === 0) {
354
+ pos++;
355
+ continue;
356
+ }
357
+ if (ch === CHAR_DOUBLE_QUOTE || ch === CHAR_SINGLE_QUOTE) {
358
+ this.pos = pos;
359
+ this.consume_string(ch, this._line, 1);
360
+ pos = this.pos;
361
+ continue;
362
+ }
363
+ if (ch === CHAR_FORWARD_SLASH) {
364
+ if (pos + 1 < end && source.charCodeAt(pos + 1) === CHAR_ASTERISK) {
365
+ this.pos = pos;
366
+ this._skip_comment();
367
+ pos = this.pos;
368
+ } else pos++;
369
+ continue;
370
+ }
371
+ pos++;
372
+ if (ch === CHAR_CARRIAGE_RETURN && pos < end && source.charCodeAt(pos) === CHAR_LINE_FEED) pos++;
373
+ this._line++;
374
+ this._line_offset = pos;
375
+ }
376
+ this.pos = pos;
377
+ return 0;
378
+ }
262
379
  consume_whitespace(start_line, start_column) {
263
380
  const source = this.source;
264
381
  const source_length = source.length;
265
382
  let start = this.pos;
266
- while (this.pos < source_length) {
267
- let ch = source.charCodeAt(this.pos);
383
+ let pos = start;
384
+ let line = this._line;
385
+ let line_offset = this._line_offset;
386
+ while (pos < source_length) {
387
+ let ch = source.charCodeAt(pos);
268
388
  if (ch >= 128 || (char_types[ch] & 24) === 0) break;
269
- this.pos++;
389
+ pos++;
270
390
  if ((char_types[ch] & 16) !== 0) {
271
- if (ch === CHAR_CARRIAGE_RETURN && this.pos < source_length && source.charCodeAt(this.pos) === CHAR_LINE_FEED) this.pos++;
272
- this._line++;
273
- this._line_offset = this.pos;
391
+ if (ch === CHAR_CARRIAGE_RETURN && pos < source_length && source.charCodeAt(pos) === CHAR_LINE_FEED) pos++;
392
+ line++;
393
+ line_offset = pos;
274
394
  }
275
395
  }
276
- return this.make_token(13, start, this.pos, start_line, start_column);
396
+ this.pos = pos;
397
+ this._line = line;
398
+ this._line_offset = line_offset;
399
+ return this.make_token(13, start, pos, start_line, start_column);
277
400
  }
278
401
  consume_string(quote, start_line, start_column) {
279
402
  const source = this.source;
@@ -330,69 +453,75 @@ var Lexer = class {
330
453
  const source = this.source;
331
454
  const source_length = source.length;
332
455
  let start = this.pos;
333
- let ch = source.charCodeAt(this.pos);
334
- if (ch === CHAR_PLUS || ch === CHAR_HYPHEN) this.pos++;
335
- while (this.pos < source_length) {
336
- let ch = source.charCodeAt(this.pos);
456
+ let pos = start;
457
+ let ch = source.charCodeAt(pos);
458
+ if (ch === CHAR_PLUS || ch === CHAR_HYPHEN) pos++;
459
+ while (pos < source_length) {
460
+ let ch = source.charCodeAt(pos);
337
461
  if (ch >= 128 || (char_types[ch] & 2) === 0) break;
338
- this.pos++;
462
+ pos++;
339
463
  }
340
- if (this.pos < source_length && source.charCodeAt(this.pos) === CHAR_DOT && this.pos + 1 < source_length) {
341
- let next = source.charCodeAt(this.pos + 1);
464
+ if (pos < source_length && source.charCodeAt(pos) === CHAR_DOT && pos + 1 < source_length) {
465
+ let next = source.charCodeAt(pos + 1);
342
466
  if (next < 128 && (char_types[next] & 2) !== 0) {
343
- this.pos++;
344
- while (this.pos < source_length) {
345
- let ch = source.charCodeAt(this.pos);
467
+ pos++;
468
+ while (pos < source_length) {
469
+ let ch = source.charCodeAt(pos);
346
470
  if (ch >= 128 || (char_types[ch] & 2) === 0) break;
347
- this.pos++;
471
+ pos++;
348
472
  }
349
473
  }
350
474
  }
351
- if (this.pos < source_length) {
352
- let ch = source.charCodeAt(this.pos);
475
+ if (pos < source_length) {
476
+ let ch = source.charCodeAt(pos);
353
477
  if (ch === CHAR_LOWERCASE_E || ch === CHAR_UPPERCASE_E) {
354
- let next = this.pos + 1 < source_length ? source.charCodeAt(this.pos + 1) : 0;
478
+ let next = pos + 1 < source_length ? source.charCodeAt(pos + 1) : 0;
355
479
  let is_next_digit = next < 128 && (char_types[next] & 2) !== 0;
356
- let next2 = this.pos + 2 < source_length ? source.charCodeAt(this.pos + 2) : 0;
480
+ let next2 = pos + 2 < source_length ? source.charCodeAt(pos + 2) : 0;
357
481
  let is_next2_digit = next2 < 128 && (char_types[next2] & 2) !== 0;
358
482
  if (is_next_digit || (next === CHAR_PLUS || next === CHAR_HYPHEN) && is_next2_digit) {
359
- this.pos++;
360
- if (this.pos < source_length) {
361
- let sign = source.charCodeAt(this.pos);
362
- if (sign === CHAR_PLUS || sign === CHAR_HYPHEN) this.pos++;
483
+ pos++;
484
+ if (pos < source_length) {
485
+ let sign = source.charCodeAt(pos);
486
+ if (sign === CHAR_PLUS || sign === CHAR_HYPHEN) pos++;
363
487
  }
364
- while (this.pos < source_length) {
365
- let ch = source.charCodeAt(this.pos);
488
+ while (pos < source_length) {
489
+ let ch = source.charCodeAt(pos);
366
490
  if (ch >= 128 || (char_types[ch] & 2) === 0) break;
367
- this.pos++;
491
+ pos++;
368
492
  }
369
493
  }
370
494
  }
371
495
  }
372
- if (this.pos < source_length) {
373
- let ch = source.charCodeAt(this.pos);
496
+ if (pos < source_length) {
497
+ let ch = source.charCodeAt(pos);
374
498
  if (ch === CHAR_PERCENT) {
375
- this.pos++;
376
- return this.make_token(11, start, this.pos, start_line, start_column);
499
+ pos++;
500
+ this.pos = pos;
501
+ return this.make_token(11, start, pos, start_line, start_column);
377
502
  }
378
- if (is_ident_start(ch) || ch === CHAR_HYPHEN && is_ident_start(this.pos + 1 < source_length ? source.charCodeAt(this.pos + 1) : 0)) {
379
- while (this.pos < source_length) {
380
- let ch = source.charCodeAt(this.pos);
503
+ if (is_ident_start(ch) || ch === CHAR_HYPHEN && is_ident_start(pos + 1 < source_length ? source.charCodeAt(pos + 1) : 0)) {
504
+ while (pos < source_length) {
505
+ let ch = source.charCodeAt(pos);
381
506
  if (ch < 128 && (char_types[ch] & 32) === 0) break;
382
- this.pos++;
507
+ pos++;
383
508
  }
384
- return this.make_token(12, start, this.pos, start_line, start_column);
509
+ this.pos = pos;
510
+ return this.make_token(12, start, pos, start_line, start_column);
385
511
  }
386
512
  }
387
- return this.make_token(10, start, this.pos, start_line, start_column);
513
+ this.pos = pos;
514
+ return this.make_token(10, start, pos, start_line, start_column);
388
515
  }
389
516
  consume_ident_or_function(start_line, start_column) {
390
517
  const source = this.source;
391
518
  const source_length = source.length;
392
519
  let start = this.pos;
393
- while (this.pos < source_length) {
394
- let ch = source.charCodeAt(this.pos);
520
+ let pos = start;
521
+ while (pos < source_length) {
522
+ let ch = source.charCodeAt(pos);
395
523
  if (ch === CHAR_BACKSLASH) {
524
+ this.pos = pos;
396
525
  if (this.pos + 1 >= source_length) break;
397
526
  let next = source.charCodeAt(this.pos + 1);
398
527
  if (is_newline(next)) break;
@@ -408,9 +537,11 @@ var Lexer = class {
408
537
  if (ws < 128 && (char_types[ws] & 24) !== 0) this.advance();
409
538
  }
410
539
  } else this.pos++;
411
- } else if (ch >= 128 || (char_types[ch] & 32) !== 0) this.pos++;
540
+ pos = this.pos;
541
+ } else if (ch >= 128 || (char_types[ch] & 32) !== 0) pos++;
412
542
  else break;
413
543
  }
544
+ this.pos = pos;
414
545
  if (this.pos - start === 1) {
415
546
  let first_ch = source.charCodeAt(start);
416
547
  if ((first_ch === CHAR_LOWERCASE_U || first_ch === CHAR_UPPERCASE_U) && this.pos < source_length && source.charCodeAt(this.pos) === CHAR_PLUS) return this.consume_unicode_range(start, start_line, start_column);
@@ -456,25 +587,27 @@ var Lexer = class {
456
587
  const source = this.source;
457
588
  const source_length = source.length;
458
589
  let start = this.pos;
459
- this.pos++;
460
- while (this.pos < source_length) {
461
- let ch = source.charCodeAt(this.pos);
590
+ let pos = start + 1;
591
+ while (pos < source_length) {
592
+ let ch = source.charCodeAt(pos);
462
593
  if (ch < 128 && (char_types[ch] & 32) === 0) break;
463
- this.pos++;
594
+ pos++;
464
595
  }
465
- return this.make_token(3, start, this.pos, start_line, start_column);
596
+ this.pos = pos;
597
+ return this.make_token(3, start, pos, start_line, start_column);
466
598
  }
467
599
  consume_hash(start_line, start_column) {
468
600
  const source = this.source;
469
601
  const source_length = source.length;
470
602
  let start = this.pos;
471
- this.pos++;
472
- while (this.pos < source_length) {
473
- let ch = source.charCodeAt(this.pos);
603
+ let pos = start + 1;
604
+ while (pos < source_length) {
605
+ let ch = source.charCodeAt(pos);
474
606
  if (ch < 128 && (char_types[ch] & 32) === 0) break;
475
- this.pos++;
607
+ pos++;
476
608
  }
477
- return this.make_token(4, start, this.pos, start_line, start_column);
609
+ this.pos = pos;
610
+ return this.make_token(4, start, pos, start_line, start_column);
478
611
  }
479
612
  advance(count = 1) {
480
613
  if (count === 1) {
@@ -563,7 +696,7 @@ var Lexer = class {
563
696
  skip_whitespace_in_range(end) {
564
697
  while (this.pos < end) {
565
698
  let ch = this.source.charCodeAt(this.pos);
566
- if (is_whitespace(ch)) {
699
+ if (ch < 128 && (char_types[ch] & 24) !== 0) {
567
700
  this.advance();
568
701
  continue;
569
702
  }
@@ -1,2 +1,2 @@
1
- import { i as tokenize, n as Lexer, r as LexerPosition, t as CommentInfo } from "./tokenize-DeZYFa9R.js";
1
+ import { i as tokenize, n as Lexer, r as LexerPosition, t as CommentInfo } from "./tokenize-CDEnYcVG.js";
2
2
  export { CommentInfo, Lexer, LexerPosition, tokenize };
package/dist/tokenize.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as tokenize, t as Lexer } from "./tokenize-BiRLfVbm.js";
1
+ import { n as tokenize, t as Lexer } from "./tokenize-DK24ACbY.js";
2
2
  export { Lexer, tokenize };
@@ -1,4 +1,4 @@
1
- import { t as Lexer } from "./tokenize-BiRLfVbm.js";
1
+ import { t as Lexer } from "./tokenize-DK24ACbY.js";
2
2
  import "./arena-CFwM4uQs.js";
3
3
  import { a as is_whitespace, o as str_equals } from "./parse-dimension-ouWXNHdx.js";
4
4
  //#region src/value-node-parser.ts
@@ -63,12 +63,9 @@ var ValueNodeParser = class {
63
63
  this.arena.set_content_length(node, end - start);
64
64
  return node;
65
65
  }
66
- create_operator_node(start, end) {
67
- return this.create_node(16, start, end);
68
- }
69
66
  parse_operator_node(start, end) {
70
67
  let ch = this.source.charCodeAt(start);
71
- if (ch === 43 || ch === 45 || ch === 42 || ch === 47) return this.create_operator_node(start, end);
68
+ if (ch === 43 || ch === 45 || ch === 42 || ch === 47) return this.create_node(16, start, end);
72
69
  return null;
73
70
  }
74
71
  parse_function_node(start, end) {
package/dist/walk.js CHANGED
@@ -10,21 +10,8 @@ const BREAK = Symbol("BREAK");
10
10
  * not tree depth.
11
11
  */
12
12
  function walk(node, callback, depth = 0) {
13
- const result = callback(node, depth);
14
- if (result === BREAK) return false;
15
- if (result === SKIP) return true;
16
13
  const impl = node;
17
- const arena = impl.__get_arena();
18
- const source = impl.__get_source();
19
- const index = impl.__get_index();
20
- const type = arena.get_type(index);
21
- const child_depth = type === 2 || type === 3 ? depth + 1 : depth;
22
- let child = arena.get_first_child(index);
23
- while (child !== 0) {
24
- if (!_walk(arena, source, child, callback, child_depth)) return false;
25
- child = arena.get_next_sibling(child);
26
- }
27
- return true;
14
+ return _walk(impl.__get_arena(), impl.__get_source(), impl.__get_index(), callback, depth);
28
15
  }
29
16
  function _walk(arena, source, index, callback, depth) {
30
17
  const result = callback(new CSSNode(arena, source, index), depth);
@@ -45,21 +32,8 @@ const NOOP = function() {};
45
32
  * SKIP in enter skips children (leave still runs); BREAK stops (leave doesn't run).
46
33
  */
47
34
  function traverse(node, { enter = NOOP, leave = NOOP } = {}) {
48
- const enter_result = enter(node);
49
- if (enter_result === BREAK) return false;
50
- if (enter_result !== SKIP) {
51
- const impl = node;
52
- const arena = impl.__get_arena();
53
- const source = impl.__get_source();
54
- const index = impl.__get_index();
55
- let child = arena.get_first_child(index);
56
- while (child !== 0) {
57
- if (!_traverse(arena, source, child, enter, leave)) return false;
58
- child = arena.get_next_sibling(child);
59
- }
60
- }
61
- if (leave(node) === BREAK) return false;
62
- return true;
35
+ const impl = node;
36
+ return _traverse(impl.__get_arena(), impl.__get_source(), impl.__get_index(), enter, leave);
63
37
  }
64
38
  function _traverse(arena, source, index, enter, leave) {
65
39
  const node = new CSSNode(arena, source, index);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-parser",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "High-performance CSS lexer and parser, optimized for CSS inspection and analysis",
5
5
  "keywords": [
6
6
  "ast",
@@ -82,7 +82,7 @@
82
82
  "bootstrap": "^5.3.8",
83
83
  "css-tree": "^3.1.0",
84
84
  "knip": "^6.18.0",
85
- "oxfmt": "^0.59.0",
85
+ "oxfmt": "^0.60.0",
86
86
  "oxlint": "^1.71.0",
87
87
  "postcss": "^8.5.15",
88
88
  "publint": "^0.3.21",
@@ -103,7 +103,7 @@
103
103
  "test-build": "pnpm run build && vitest run --config vitest.config.build.ts",
104
104
  "build": "tsdown",
105
105
  "benchmark": "pnpm run build && node --expose-gc benchmark/index.ts",
106
- "lint": "oxlint --config .oxlintrc.json && oxfmt --check",
106
+ "lint": "oxlint --config .oxlintrc.json; oxfmt --check",
107
107
  "check": "tsc --noEmit",
108
108
  "knip": "knip",
109
109
  "precommit": "pnpm run test --run; pnpm run lint; pnpm run check"