@projectwallace/css-parser 0.12.1 → 0.12.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.
@@ -2,7 +2,7 @@ import { Lexer } from './tokenize.js';
2
2
  import { NTH_SELECTOR, CSSDataArena } from './arena.js';
3
3
  import { TOKEN_IDENT, TOKEN_DELIM, TOKEN_DIMENSION, TOKEN_NUMBER } from './token-types.js';
4
4
  import { str_equals, CHAR_MINUS_HYPHEN, CHAR_PLUS, str_index_of } from './string-utils.js';
5
- import { skip_whitespace_forward } from './parse-utils.js';
5
+ import { skip_whitespace_and_comments_forward } from './parse-utils.js';
6
6
  import { CSSNode } from './css-node.js';
7
7
 
8
8
  class ANplusBParser {
@@ -186,7 +186,7 @@ class ANplusBParser {
186
186
  return null;
187
187
  }
188
188
  skip_whitespace() {
189
- this.lexer.pos = skip_whitespace_forward(this.source, this.lexer.pos, this.expr_end);
189
+ this.lexer.pos = skip_whitespace_and_comments_forward(this.source, this.lexer.pos, this.expr_end);
190
190
  }
191
191
  create_anplusb_node(start, a_start, a_end, b_start, b_end) {
192
192
  const node = this.arena.create_node(NTH_SELECTOR, start, this.lexer.pos - start, this.lexer.line, 1);
@@ -2,7 +2,7 @@ import { Lexer } from './tokenize.js';
2
2
  import { CSSDataArena, PRELUDE_OPERATOR, MEDIA_TYPE, MEDIA_QUERY, MEDIA_FEATURE, FUNCTION, IDENTIFIER, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, STRING, URL, DIMENSION, NUMBER, FEATURE_RANGE } from './arena.js';
3
3
  import { TOKEN_COMMA, TOKEN_IDENT, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_FUNCTION, TOKEN_EOF, TOKEN_WHITESPACE, TOKEN_STRING, TOKEN_URL, TOKEN_DIMENSION, TOKEN_PERCENTAGE, TOKEN_NUMBER } from './token-types.js';
4
4
  import { strip_vendor_prefix, str_equals, CHAR_LESS_THAN, CHAR_GREATER_THAN, CHAR_EQUALS, CHAR_COLON, is_whitespace } from './string-utils.js';
5
- import { trim_boundaries, skip_whitespace_forward } from './parse-utils.js';
5
+ import { skip_whitespace_and_comments_forward, trim_boundaries } from './parse-utils.js';
6
6
  import { CSSNode } from './css-node.js';
7
7
 
8
8
  class AtRulePreludeParser {
@@ -134,23 +134,31 @@ class AtRulePreludeParser {
134
134
  let content_end = this.lexer.token_start;
135
135
  let feature_end = this.lexer.token_end;
136
136
  let has_comparison = false;
137
- for (let i = content_start; i < content_end; i++) {
137
+ let i = content_start;
138
+ while (i < content_end) {
139
+ i = skip_whitespace_and_comments_forward(this.source, i, content_end);
140
+ if (i >= content_end) break;
138
141
  let ch = this.source.charCodeAt(i);
139
142
  if (ch === CHAR_LESS_THAN || ch === CHAR_GREATER_THAN || ch === CHAR_EQUALS) {
140
143
  has_comparison = true;
141
144
  break;
142
145
  }
146
+ i++;
143
147
  }
144
148
  if (has_comparison) {
145
149
  return this.parse_feature_range(feature_start, feature_end, content_start, content_end);
146
150
  }
147
151
  let feature = this.create_node(MEDIA_FEATURE, feature_start, feature_end);
148
152
  let colon_pos = -1;
149
- for (let i = content_start; i < content_end; i++) {
150
- if (this.source.charCodeAt(i) === CHAR_COLON) {
151
- colon_pos = i;
153
+ let j = content_start;
154
+ while (j < content_end) {
155
+ j = skip_whitespace_and_comments_forward(this.source, j, content_end);
156
+ if (j >= content_end) break;
157
+ if (this.source.charCodeAt(j) === CHAR_COLON) {
158
+ colon_pos = j;
152
159
  break;
153
160
  }
161
+ j++;
154
162
  }
155
163
  if (colon_pos !== -1) {
156
164
  let name_trimmed = trim_boundaries(this.source, content_start, colon_pos);
@@ -458,9 +466,9 @@ class AtRulePreludeParser {
458
466
  this.lexer.restore_position(saved);
459
467
  return null;
460
468
  }
461
- // Helper: Skip whitespace
469
+ // Helper: Skip whitespace and comments
462
470
  skip_whitespace() {
463
- this.lexer.pos = skip_whitespace_forward(this.source, this.lexer.pos, this.prelude_end);
471
+ this.lexer.pos = skip_whitespace_and_comments_forward(this.source, this.lexer.pos, this.prelude_end);
464
472
  }
465
473
  // Helper: Peek at next token type without consuming
466
474
  peek_token_type() {
@@ -524,7 +532,7 @@ class AtRulePreludeParser {
524
532
  let feature_name_end = -1;
525
533
  let pos = content_start;
526
534
  while (pos < content_end) {
527
- pos = skip_whitespace_forward(this.source, pos, content_end);
535
+ pos = skip_whitespace_and_comments_forward(this.source, pos, content_end);
528
536
  if (pos >= content_end) break;
529
537
  let ch = this.source.charCodeAt(pos);
530
538
  if (ch === CHAR_LESS_THAN || ch === CHAR_GREATER_THAN || ch === CHAR_EQUALS) {
@@ -222,22 +222,30 @@ class SelectorParser {
222
222
  // Parse type selector or namespace selector (ns|E or ns|*)
223
223
  // Called when we've seen an IDENT token
224
224
  parse_type_or_namespace_selector(start, end) {
225
+ const saved = this.lexer.save_position();
226
+ this.skip_whitespace();
225
227
  if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_PIPE) {
226
228
  this.lexer.pos++;
227
229
  let node = this.parse_namespace_local_part(start, start, end - start);
228
230
  if (node !== null) return node;
229
231
  this.lexer.pos = end;
232
+ } else {
233
+ this.lexer.restore_position(saved);
230
234
  }
231
235
  return this.create_node(TYPE_SELECTOR, start, end);
232
236
  }
233
237
  // Parse universal selector or namespace selector (*|E or *|*)
234
238
  // Called when we've seen a * DELIM token
235
239
  parse_universal_or_namespace_selector(start, end) {
240
+ const saved = this.lexer.save_position();
241
+ this.skip_whitespace();
236
242
  if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_PIPE) {
237
243
  this.lexer.pos++;
238
244
  let node = this.parse_namespace_local_part(start, start, end - start);
239
245
  if (node !== null) return node;
240
246
  this.lexer.pos = end;
247
+ } else {
248
+ this.lexer.restore_position(saved);
241
249
  }
242
250
  return this.create_node(UNIVERSAL_SELECTOR, start, end);
243
251
  }
@@ -420,10 +428,14 @@ class SelectorParser {
420
428
  // Parse pseudo-class or pseudo-element (:hover, ::before)
421
429
  parse_pseudo(start) {
422
430
  const saved = this.lexer.save_position();
431
+ const saved_ws = this.lexer.save_position();
432
+ this.skip_whitespace();
423
433
  let is_pseudo_element = false;
424
434
  if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_COLON) {
425
435
  is_pseudo_element = true;
426
436
  this.lexer.pos++;
437
+ } else {
438
+ this.lexer.restore_position(saved_ws);
427
439
  }
428
440
  this.lexer.next_token_fast(false);
429
441
  let token_type = this.lexer.token_type;
@@ -564,16 +576,22 @@ class SelectorParser {
564
576
  return anplusb_parser.parse_anplusb(start, end, this.lexer.line);
565
577
  }
566
578
  }
567
- // Find the position of standalone "of" keyword
579
+ // Find the position of standalone "of" keyword (case-insensitive)
568
580
  find_of_keyword(start, end) {
569
- for (let i = start; i < end - 1; i++) {
570
- if (this.source.charCodeAt(i) === 111 && this.source.charCodeAt(i + 1) === 102) {
581
+ let i = start;
582
+ while (i < end - 1) {
583
+ i = skip_whitespace_and_comments_forward(this.source, i, end);
584
+ if (i >= end - 1) break;
585
+ let ch1 = this.source.charCodeAt(i);
586
+ let ch2 = this.source.charCodeAt(i + 1);
587
+ if ((ch1 === 111 || ch1 === 79) && (ch2 === 102 || ch2 === 70)) {
571
588
  let before_ok = i === start || is_whitespace(this.source.charCodeAt(i - 1));
572
589
  let after_ok = i + 2 >= end || is_whitespace(this.source.charCodeAt(i + 2));
573
590
  if (before_ok && after_ok) {
574
591
  return i;
575
592
  }
576
593
  }
594
+ i++;
577
595
  }
578
596
  return -1;
579
597
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-parser",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "High-performance CSS lexer and parser, optimized for CSS inspection and analysis",
5
5
  "author": "Bart Veneman <bart@projectwallace.com>",
6
6
  "license": "MIT",