@projectwallace/css-parser 0.6.3 → 0.6.5

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/README.md CHANGED
@@ -7,13 +7,14 @@
7
7
 
8
8
  Built for speed and efficiency, this parser handles large CSS files with minimal memory overhead and blazing-fast parse times. Designed with a data-oriented architecture using a single contiguous memory arena for zero allocations during parsing.
9
9
 
10
+ This parser was heavily influenced by [CSSTree](https://github.com/csstree/csstree), one of the most robust CSS parsers available. Some of the parsing mechanics are taken from CSSTree, as well as some of the performance mechanics, but a lot of things are very different which is why this isn't a direct fork.
11
+
10
12
  ## Features
11
13
 
12
14
  - **Modern CSS support** - CSS Nesting, `:is()`, `:where()`, `:has()`, `@layer`, `@container`
13
15
  - **Error recovery** - Continues parsing on malformed CSS
14
- - **Comment preservation** - Comments stored as first-class AST nodes
15
16
  - **Location tracking** - Line, column, offset, and length for all nodes
16
- - **Built-in vendor prefix detection** - Automatic detection of `-webkit-`, `-moz-`, etc. for selectors, values, properties and more
17
+ - **Performance** - Low memory usage and excellent parsing speed
17
18
 
18
19
  ## Installation
19
20
 
@@ -57,14 +58,12 @@ for (const rule of ast) {
57
58
 
58
59
  ## Performance
59
60
 
60
- - **Tiny install size**
61
+ - **Small install size** (~200kB)
61
62
  - **Zero allocations during parsing** - all memory allocated upfront based on real world heuristics, which also helps prevent garbage collection running often
62
- - **Cache-friendly data layout** - contiguous memory for sequential access powered by concepts or Data Oriented Design
63
+ - **Cache-friendly data layout** - contiguous memory for sequential access powered by concepts of Data Oriented Design
63
64
  - **First-class comment and location support** - while still being performant because analysis requires constant access to lines and columns
64
65
  - **No syntax validation** - focusing only on the raw data we can skip expensive syntax files and MDN data syncs
65
66
 
66
- This parser was heavily influenced by [CSSTree](https://github.com/csstree/csstree), one of the most robust CSS parsers available.
67
-
68
67
  ## Documentation
69
68
 
70
69
  See [API.md](./API.md) for complete documentation of all parser functions and options.
@@ -72,6 +71,7 @@ See [API.md](./API.md) for complete documentation of all parser functions and op
72
71
  ## Non-goals
73
72
 
74
73
  - **No syntax validation** - this parser does not try to validate your CSS structure. Everything can be anything
74
+ - **No custom syntax support** - we're leaving the era of CSS preprocessors so we only focus on CSS
75
75
 
76
76
  ## License
77
77
 
@@ -420,6 +420,47 @@ function trim_boundaries(source, start, end) {
420
420
  return [start, end];
421
421
  }
422
422
 
423
+ const TYPE_NAMES = {
424
+ [NODE_STYLESHEET]: "stylesheet",
425
+ [NODE_STYLE_RULE]: "rule",
426
+ [NODE_AT_RULE]: "atrule",
427
+ [NODE_DECLARATION]: "declaration",
428
+ [NODE_SELECTOR]: "selector",
429
+ [NODE_COMMENT]: "comment",
430
+ [NODE_BLOCK]: "block",
431
+ [NODE_VALUE_KEYWORD]: "keyword",
432
+ [NODE_VALUE_NUMBER]: "number",
433
+ [NODE_VALUE_DIMENSION]: "dimension",
434
+ [NODE_VALUE_STRING]: "string",
435
+ [NODE_VALUE_COLOR]: "color",
436
+ [NODE_VALUE_FUNCTION]: "function",
437
+ [NODE_VALUE_OPERATOR]: "operator",
438
+ [NODE_VALUE_PARENTHESIS]: "parenthesis",
439
+ [NODE_SELECTOR_LIST]: "selectorlist",
440
+ [NODE_SELECTOR_TYPE]: "type-selector",
441
+ [NODE_SELECTOR_CLASS]: "class-selector",
442
+ [NODE_SELECTOR_ID]: "id-selector",
443
+ [NODE_SELECTOR_ATTRIBUTE]: "attribute-selector",
444
+ [NODE_SELECTOR_PSEUDO_CLASS]: "pseudoclass-selector",
445
+ [NODE_SELECTOR_PSEUDO_ELEMENT]: "pseudoelement-selector",
446
+ [NODE_SELECTOR_COMBINATOR]: "selector-combinator",
447
+ [NODE_SELECTOR_UNIVERSAL]: "universal-selector",
448
+ [NODE_SELECTOR_NESTING]: "nesting-selector",
449
+ [NODE_SELECTOR_NTH]: "nth-selector",
450
+ [NODE_SELECTOR_NTH_OF]: "nth-of-selector",
451
+ [NODE_SELECTOR_LANG]: "lang-selector",
452
+ [NODE_PRELUDE_MEDIA_QUERY]: "media-query",
453
+ [NODE_PRELUDE_MEDIA_FEATURE]: "media-feature",
454
+ [NODE_PRELUDE_MEDIA_TYPE]: "media-type",
455
+ [NODE_PRELUDE_CONTAINER_QUERY]: "container-query",
456
+ [NODE_PRELUDE_SUPPORTS_QUERY]: "supports-query",
457
+ [NODE_PRELUDE_LAYER_NAME]: "layer-name",
458
+ [NODE_PRELUDE_IDENTIFIER]: "identifier",
459
+ [NODE_PRELUDE_OPERATOR]: "operator",
460
+ [NODE_PRELUDE_IMPORT_URL]: "import-url",
461
+ [NODE_PRELUDE_IMPORT_LAYER]: "import-layer",
462
+ [NODE_PRELUDE_IMPORT_SUPPORTS]: "import-supports"
463
+ };
423
464
  class CSSNode {
424
465
  arena;
425
466
  source;
@@ -437,6 +478,10 @@ class CSSNode {
437
478
  get type() {
438
479
  return this.arena.get_type(this.index);
439
480
  }
481
+ // Get node type as human-readable string
482
+ get type_name() {
483
+ return TYPE_NAMES[this.type] || "unknown";
484
+ }
440
485
  // Get the full text of this node from source
441
486
  get text() {
442
487
  let start = this.arena.get_start_offset(this.index);
@@ -640,7 +685,7 @@ class CSSNode {
640
685
  let start = this.arena.get_content_start(this.index);
641
686
  return this.source.substring(start, start + len);
642
687
  }
643
- // Get the 'b' coefficient from An+B expression (e.g., "1" from "2n+1")
688
+ // Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1")
644
689
  get nth_b() {
645
690
  if (this.type !== NODE_SELECTOR_NTH) return null;
646
691
  let len = this.arena.get_value_length(this.index);
@@ -656,14 +701,40 @@ class CSSNode {
656
701
  }
657
702
  if (ch === CHAR_MINUS_HYPHEN) {
658
703
  value = "-" + value;
704
+ } else if (ch === CHAR_PLUS) {
705
+ value = "+" + value;
659
706
  }
660
707
  break;
661
708
  }
662
- if (value.charCodeAt(0) === CHAR_PLUS) {
663
- return value.substring(1);
664
- }
665
709
  return value;
666
710
  }
711
+ // --- Pseudo-Class Nth-Of Helpers (for NODE_SELECTOR_NTH_OF) ---
712
+ // Get the An+B formula node from :nth-child(2n+1 of .foo)
713
+ get nth() {
714
+ if (this.type !== NODE_SELECTOR_NTH_OF) return null;
715
+ return this.first_child;
716
+ }
717
+ // Get the selector list from :nth-child(2n+1 of .foo)
718
+ get selector() {
719
+ if (this.type !== NODE_SELECTOR_NTH_OF) return null;
720
+ let first = this.first_child;
721
+ return first ? first.next_sibling : null;
722
+ }
723
+ // --- Pseudo-Class Selector List Helper ---
724
+ // Get selector list from pseudo-class functions
725
+ // Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
726
+ get selector_list() {
727
+ if (this.type !== NODE_SELECTOR_PSEUDO_CLASS) return null;
728
+ let child = this.first_child;
729
+ if (!child) return null;
730
+ if (child.type === NODE_SELECTOR_LIST) {
731
+ return child;
732
+ }
733
+ if (child.type === NODE_SELECTOR_NTH_OF) {
734
+ return child.selector;
735
+ }
736
+ return null;
737
+ }
667
738
  }
668
739
 
669
- export { FLAG_HAS_DECLARATIONS as $, ATTR_OPERATOR_NONE as A, NODE_SELECTOR_ATTRIBUTE as B, CSSNode as C, NODE_SELECTOR_PSEUDO_CLASS as D, NODE_SELECTOR_PSEUDO_ELEMENT as E, NODE_SELECTOR_COMBINATOR as F, NODE_SELECTOR_UNIVERSAL as G, NODE_SELECTOR_NESTING as H, NODE_SELECTOR_NTH as I, NODE_SELECTOR_NTH_OF as J, NODE_SELECTOR_LANG as K, NODE_PRELUDE_MEDIA_QUERY as L, NODE_PRELUDE_MEDIA_FEATURE as M, NODE_STYLE_RULE as N, NODE_PRELUDE_MEDIA_TYPE as O, NODE_PRELUDE_CONTAINER_QUERY as P, NODE_PRELUDE_SUPPORTS_QUERY as Q, NODE_PRELUDE_LAYER_NAME as R, NODE_PRELUDE_IDENTIFIER as S, NODE_PRELUDE_OPERATOR as T, NODE_PRELUDE_IMPORT_URL as U, NODE_PRELUDE_IMPORT_LAYER as V, NODE_PRELUDE_IMPORT_SUPPORTS as W, FLAG_IMPORTANT as X, CSSDataArena as Y, FLAG_HAS_BLOCK as Z, NODE_BLOCK as _, ATTR_OPERATOR_EQUAL as a, is_vendor_prefixed as a0, FLAG_VENDOR_PREFIXED as a1, trim_boundaries as a2, CHAR_GREATER_THAN as a3, CHAR_PLUS as a4, CHAR_TILDE as a5, CHAR_PERIOD as a6, CHAR_ASTERISK as a7, CHAR_AMPERSAND as a8, is_whitespace as a9, is_combinator as aa, skip_whitespace_and_comments_forward as ab, skip_whitespace_and_comments_backward as ac, CHAR_EQUALS as ad, CHAR_PIPE as ae, CHAR_CARET as af, CHAR_DOLLAR as ag, CHAR_SINGLE_QUOTE as ah, CHAR_DOUBLE_QUOTE as ai, CHAR_COLON as aj, FLAG_HAS_PARENS as ak, skip_whitespace_forward as al, str_equals as am, CHAR_MINUS_HYPHEN as an, CHAR_FORWARD_SLASH as ao, ATTR_OPERATOR_TILDE_EQUAL as b, ATTR_OPERATOR_PIPE_EQUAL as c, ATTR_OPERATOR_CARET_EQUAL as d, ATTR_OPERATOR_DOLLAR_EQUAL as e, ATTR_OPERATOR_STAR_EQUAL as f, ATTR_FLAG_NONE as g, ATTR_FLAG_CASE_INSENSITIVE as h, ATTR_FLAG_CASE_SENSITIVE as i, NODE_AT_RULE as j, NODE_COMMENT as k, NODE_DECLARATION as l, NODE_SELECTOR as m, NODE_STYLESHEET as n, NODE_VALUE_KEYWORD as o, NODE_VALUE_NUMBER as p, NODE_VALUE_DIMENSION as q, NODE_VALUE_STRING as r, NODE_VALUE_COLOR as s, NODE_VALUE_FUNCTION as t, NODE_VALUE_OPERATOR as u, NODE_VALUE_PARENTHESIS as v, NODE_SELECTOR_LIST as w, NODE_SELECTOR_TYPE as x, NODE_SELECTOR_CLASS as y, NODE_SELECTOR_ID as z };
740
+ export { NODE_BLOCK as $, ATTR_OPERATOR_NONE as A, NODE_SELECTOR_ATTRIBUTE as B, CSSNode as C, NODE_SELECTOR_PSEUDO_CLASS as D, NODE_SELECTOR_PSEUDO_ELEMENT as E, NODE_SELECTOR_COMBINATOR as F, NODE_SELECTOR_UNIVERSAL as G, NODE_SELECTOR_NESTING as H, NODE_SELECTOR_NTH as I, NODE_SELECTOR_NTH_OF as J, NODE_SELECTOR_LANG as K, NODE_PRELUDE_MEDIA_QUERY as L, NODE_PRELUDE_MEDIA_FEATURE as M, NODE_STYLE_RULE as N, NODE_PRELUDE_MEDIA_TYPE as O, NODE_PRELUDE_CONTAINER_QUERY as P, NODE_PRELUDE_SUPPORTS_QUERY as Q, NODE_PRELUDE_LAYER_NAME as R, NODE_PRELUDE_IDENTIFIER as S, TYPE_NAMES as T, NODE_PRELUDE_OPERATOR as U, NODE_PRELUDE_IMPORT_URL as V, NODE_PRELUDE_IMPORT_LAYER as W, NODE_PRELUDE_IMPORT_SUPPORTS as X, FLAG_IMPORTANT as Y, CSSDataArena as Z, FLAG_HAS_BLOCK as _, ATTR_OPERATOR_EQUAL as a, FLAG_HAS_DECLARATIONS as a0, is_vendor_prefixed as a1, FLAG_VENDOR_PREFIXED as a2, trim_boundaries as a3, CHAR_GREATER_THAN as a4, CHAR_PLUS as a5, CHAR_TILDE as a6, CHAR_PERIOD as a7, CHAR_ASTERISK as a8, CHAR_AMPERSAND as a9, CHAR_PIPE as aa, is_whitespace as ab, is_combinator as ac, skip_whitespace_and_comments_forward as ad, skip_whitespace_and_comments_backward as ae, CHAR_EQUALS as af, CHAR_CARET as ag, CHAR_DOLLAR as ah, CHAR_SINGLE_QUOTE as ai, CHAR_DOUBLE_QUOTE as aj, CHAR_COLON as ak, FLAG_HAS_PARENS as al, skip_whitespace_forward as am, str_equals as an, CHAR_MINUS_HYPHEN as ao, CHAR_FORWARD_SLASH as ap, ATTR_OPERATOR_TILDE_EQUAL as b, ATTR_OPERATOR_PIPE_EQUAL as c, ATTR_OPERATOR_CARET_EQUAL as d, ATTR_OPERATOR_DOLLAR_EQUAL as e, ATTR_OPERATOR_STAR_EQUAL as f, ATTR_FLAG_NONE as g, ATTR_FLAG_CASE_INSENSITIVE as h, ATTR_FLAG_CASE_SENSITIVE as i, NODE_AT_RULE as j, NODE_COMMENT as k, NODE_DECLARATION as l, NODE_SELECTOR as m, NODE_STYLESHEET as n, NODE_VALUE_KEYWORD as o, NODE_VALUE_NUMBER as p, NODE_VALUE_DIMENSION as q, NODE_VALUE_STRING as r, NODE_VALUE_COLOR as s, NODE_VALUE_FUNCTION as t, NODE_VALUE_OPERATOR as u, NODE_VALUE_PARENTHESIS as v, NODE_SELECTOR_LIST as w, NODE_SELECTOR_TYPE as x, NODE_SELECTOR_CLASS as y, NODE_SELECTOR_ID as z };
@@ -1,5 +1,6 @@
1
1
  import type { CSSDataArena } from './arena';
2
2
  import { NODE_STYLESHEET, NODE_STYLE_RULE, NODE_AT_RULE, NODE_DECLARATION, NODE_SELECTOR, NODE_COMMENT, NODE_BLOCK, NODE_VALUE_KEYWORD, NODE_VALUE_NUMBER, NODE_VALUE_DIMENSION, NODE_VALUE_STRING, NODE_VALUE_COLOR, NODE_VALUE_FUNCTION, NODE_VALUE_OPERATOR, NODE_VALUE_PARENTHESIS, NODE_SELECTOR_LIST, NODE_SELECTOR_TYPE, NODE_SELECTOR_CLASS, NODE_SELECTOR_ID, NODE_SELECTOR_ATTRIBUTE, NODE_SELECTOR_PSEUDO_CLASS, NODE_SELECTOR_PSEUDO_ELEMENT, NODE_SELECTOR_COMBINATOR, NODE_SELECTOR_UNIVERSAL, NODE_SELECTOR_NESTING, NODE_SELECTOR_NTH, NODE_SELECTOR_NTH_OF, NODE_SELECTOR_LANG, NODE_PRELUDE_MEDIA_QUERY, NODE_PRELUDE_MEDIA_FEATURE, NODE_PRELUDE_MEDIA_TYPE, NODE_PRELUDE_CONTAINER_QUERY, NODE_PRELUDE_SUPPORTS_QUERY, NODE_PRELUDE_LAYER_NAME, NODE_PRELUDE_IDENTIFIER, NODE_PRELUDE_OPERATOR, NODE_PRELUDE_IMPORT_URL, NODE_PRELUDE_IMPORT_LAYER, NODE_PRELUDE_IMPORT_SUPPORTS } from './arena';
3
+ export declare const TYPE_NAMES: Record<number, string>;
3
4
  export type CSSNodeType = typeof NODE_STYLESHEET | typeof NODE_STYLE_RULE | typeof NODE_AT_RULE | typeof NODE_DECLARATION | typeof NODE_SELECTOR | typeof NODE_COMMENT | typeof NODE_BLOCK | typeof NODE_VALUE_KEYWORD | typeof NODE_VALUE_NUMBER | typeof NODE_VALUE_DIMENSION | typeof NODE_VALUE_STRING | typeof NODE_VALUE_COLOR | typeof NODE_VALUE_FUNCTION | typeof NODE_VALUE_OPERATOR | typeof NODE_VALUE_PARENTHESIS | typeof NODE_SELECTOR_LIST | typeof NODE_SELECTOR_TYPE | typeof NODE_SELECTOR_CLASS | typeof NODE_SELECTOR_ID | typeof NODE_SELECTOR_ATTRIBUTE | typeof NODE_SELECTOR_PSEUDO_CLASS | typeof NODE_SELECTOR_PSEUDO_ELEMENT | typeof NODE_SELECTOR_COMBINATOR | typeof NODE_SELECTOR_UNIVERSAL | typeof NODE_SELECTOR_NESTING | typeof NODE_SELECTOR_NTH | typeof NODE_SELECTOR_NTH_OF | typeof NODE_SELECTOR_LANG | typeof NODE_PRELUDE_MEDIA_QUERY | typeof NODE_PRELUDE_MEDIA_FEATURE | typeof NODE_PRELUDE_MEDIA_TYPE | typeof NODE_PRELUDE_CONTAINER_QUERY | typeof NODE_PRELUDE_SUPPORTS_QUERY | typeof NODE_PRELUDE_LAYER_NAME | typeof NODE_PRELUDE_IDENTIFIER | typeof NODE_PRELUDE_OPERATOR | typeof NODE_PRELUDE_IMPORT_URL | typeof NODE_PRELUDE_IMPORT_LAYER | typeof NODE_PRELUDE_IMPORT_SUPPORTS;
4
5
  export declare class CSSNode {
5
6
  private arena;
@@ -8,6 +9,7 @@ export declare class CSSNode {
8
9
  constructor(arena: CSSDataArena, source: string, index: number);
9
10
  get_index(): number;
10
11
  get type(): CSSNodeType;
12
+ get type_name(): string;
11
13
  get text(): string;
12
14
  get name(): string;
13
15
  get property(): string;
@@ -38,4 +40,7 @@ export declare class CSSNode {
38
40
  [Symbol.iterator](): Iterator<CSSNode>;
39
41
  get nth_a(): string | null;
40
42
  get nth_b(): string | null;
43
+ get nth(): CSSNode | null;
44
+ get selector(): CSSNode | null;
45
+ get selector_list(): CSSNode | null;
41
46
  }
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { parse_value } from './parse-value';
5
5
  export { tokenize } from './tokenize';
6
6
  export { walk, traverse } from './walk';
7
7
  export { type ParserOptions } from './parse';
8
- export { CSSNode, type CSSNodeType } from './css-node';
8
+ export { CSSNode, type CSSNodeType, TYPE_NAMES } from './css-node';
9
9
  export type { LexerPosition } from './lexer';
10
10
  export { ATTR_OPERATOR_NONE, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_FLAG_NONE, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, } from './arena';
11
11
  export { NODE_STYLE_RULE, NODE_AT_RULE, NODE_COMMENT, NODE_DECLARATION, NODE_SELECTOR, NODE_STYLESHEET, NODE_VALUE_KEYWORD, NODE_VALUE_NUMBER, NODE_VALUE_DIMENSION, NODE_VALUE_STRING, NODE_VALUE_COLOR, NODE_VALUE_FUNCTION, NODE_VALUE_OPERATOR, NODE_VALUE_PARENTHESIS, NODE_SELECTOR_LIST, NODE_SELECTOR_TYPE, NODE_SELECTOR_CLASS, NODE_SELECTOR_ID, NODE_SELECTOR_ATTRIBUTE, NODE_SELECTOR_PSEUDO_CLASS, NODE_SELECTOR_PSEUDO_ELEMENT, NODE_SELECTOR_COMBINATOR, NODE_SELECTOR_UNIVERSAL, NODE_SELECTOR_NESTING, NODE_SELECTOR_NTH, NODE_SELECTOR_NTH_OF, NODE_SELECTOR_LANG, NODE_PRELUDE_MEDIA_QUERY, NODE_PRELUDE_MEDIA_FEATURE, NODE_PRELUDE_MEDIA_TYPE, NODE_PRELUDE_CONTAINER_QUERY, NODE_PRELUDE_SUPPORTS_QUERY, NODE_PRELUDE_LAYER_NAME, NODE_PRELUDE_IDENTIFIER, NODE_PRELUDE_OPERATOR, NODE_PRELUDE_IMPORT_URL, NODE_PRELUDE_IMPORT_LAYER, NODE_PRELUDE_IMPORT_SUPPORTS, FLAG_IMPORTANT, } from './parse';
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ export { parse_selector } from './parse-selector.js';
3
3
  export { parse_atrule_prelude } from './parse-atrule-prelude.js';
4
4
  export { parse_value } from './parse-value.js';
5
5
  export { tokenize } from './tokenize.js';
6
- export { h as ATTR_FLAG_CASE_INSENSITIVE, i as ATTR_FLAG_CASE_SENSITIVE, g as ATTR_FLAG_NONE, d as ATTR_OPERATOR_CARET_EQUAL, e as ATTR_OPERATOR_DOLLAR_EQUAL, a as ATTR_OPERATOR_EQUAL, A as ATTR_OPERATOR_NONE, c as ATTR_OPERATOR_PIPE_EQUAL, f as ATTR_OPERATOR_STAR_EQUAL, b as ATTR_OPERATOR_TILDE_EQUAL, C as CSSNode, X as FLAG_IMPORTANT, j as NODE_AT_RULE, k as NODE_COMMENT, l as NODE_DECLARATION, P as NODE_PRELUDE_CONTAINER_QUERY, S as NODE_PRELUDE_IDENTIFIER, V as NODE_PRELUDE_IMPORT_LAYER, W as NODE_PRELUDE_IMPORT_SUPPORTS, U as NODE_PRELUDE_IMPORT_URL, R as NODE_PRELUDE_LAYER_NAME, M as NODE_PRELUDE_MEDIA_FEATURE, L as NODE_PRELUDE_MEDIA_QUERY, O as NODE_PRELUDE_MEDIA_TYPE, T as NODE_PRELUDE_OPERATOR, Q as NODE_PRELUDE_SUPPORTS_QUERY, m as NODE_SELECTOR, B as NODE_SELECTOR_ATTRIBUTE, y as NODE_SELECTOR_CLASS, F as NODE_SELECTOR_COMBINATOR, z as NODE_SELECTOR_ID, K as NODE_SELECTOR_LANG, w as NODE_SELECTOR_LIST, H as NODE_SELECTOR_NESTING, I as NODE_SELECTOR_NTH, J as NODE_SELECTOR_NTH_OF, D as NODE_SELECTOR_PSEUDO_CLASS, E as NODE_SELECTOR_PSEUDO_ELEMENT, x as NODE_SELECTOR_TYPE, G as NODE_SELECTOR_UNIVERSAL, n as NODE_STYLESHEET, N as NODE_STYLE_RULE, s as NODE_VALUE_COLOR, q as NODE_VALUE_DIMENSION, t as NODE_VALUE_FUNCTION, o as NODE_VALUE_KEYWORD, p as NODE_VALUE_NUMBER, u as NODE_VALUE_OPERATOR, v as NODE_VALUE_PARENTHESIS, r as NODE_VALUE_STRING } from './css-node-aIMm9_Cb.js';
6
+ export { h as ATTR_FLAG_CASE_INSENSITIVE, i as ATTR_FLAG_CASE_SENSITIVE, g as ATTR_FLAG_NONE, d as ATTR_OPERATOR_CARET_EQUAL, e as ATTR_OPERATOR_DOLLAR_EQUAL, a as ATTR_OPERATOR_EQUAL, A as ATTR_OPERATOR_NONE, c as ATTR_OPERATOR_PIPE_EQUAL, f as ATTR_OPERATOR_STAR_EQUAL, b as ATTR_OPERATOR_TILDE_EQUAL, C as CSSNode, Y as FLAG_IMPORTANT, j as NODE_AT_RULE, k as NODE_COMMENT, l as NODE_DECLARATION, P as NODE_PRELUDE_CONTAINER_QUERY, S as NODE_PRELUDE_IDENTIFIER, W as NODE_PRELUDE_IMPORT_LAYER, X as NODE_PRELUDE_IMPORT_SUPPORTS, V as NODE_PRELUDE_IMPORT_URL, R as NODE_PRELUDE_LAYER_NAME, M as NODE_PRELUDE_MEDIA_FEATURE, L as NODE_PRELUDE_MEDIA_QUERY, O as NODE_PRELUDE_MEDIA_TYPE, U as NODE_PRELUDE_OPERATOR, Q as NODE_PRELUDE_SUPPORTS_QUERY, m as NODE_SELECTOR, B as NODE_SELECTOR_ATTRIBUTE, y as NODE_SELECTOR_CLASS, F as NODE_SELECTOR_COMBINATOR, z as NODE_SELECTOR_ID, K as NODE_SELECTOR_LANG, w as NODE_SELECTOR_LIST, H as NODE_SELECTOR_NESTING, I as NODE_SELECTOR_NTH, J as NODE_SELECTOR_NTH_OF, D as NODE_SELECTOR_PSEUDO_CLASS, E as NODE_SELECTOR_PSEUDO_ELEMENT, x as NODE_SELECTOR_TYPE, G as NODE_SELECTOR_UNIVERSAL, n as NODE_STYLESHEET, N as NODE_STYLE_RULE, s as NODE_VALUE_COLOR, q as NODE_VALUE_DIMENSION, t as NODE_VALUE_FUNCTION, o as NODE_VALUE_KEYWORD, p as NODE_VALUE_NUMBER, u as NODE_VALUE_OPERATOR, v as NODE_VALUE_PARENTHESIS, r as NODE_VALUE_STRING, T as TYPE_NAMES } from './css-node-BpZTUiy6.js';
7
7
  export { b as TOKEN_AT_KEYWORD, e as TOKEN_BAD_STRING, g as TOKEN_BAD_URL, n as TOKEN_CDC, m as TOKEN_CDO, o as TOKEN_COLON, q as TOKEN_COMMA, x as TOKEN_COMMENT, h as TOKEN_DELIM, k as TOKEN_DIMENSION, y as TOKEN_EOF, a as TOKEN_FUNCTION, c as TOKEN_HASH, T as TOKEN_IDENT, v as TOKEN_LEFT_BRACE, r as TOKEN_LEFT_BRACKET, t as TOKEN_LEFT_PAREN, i as TOKEN_NUMBER, j as TOKEN_PERCENTAGE, w as TOKEN_RIGHT_BRACE, s as TOKEN_RIGHT_BRACKET, u as TOKEN_RIGHT_PAREN, p as TOKEN_SEMICOLON, d as TOKEN_STRING, f as TOKEN_URL, l as TOKEN_WHITESPACE } from './lexer-CtBKgfVv.js';
8
8
 
9
9
  function walk(node, callback, depth = 0) {
@@ -1,5 +1,5 @@
1
1
  import { L as Lexer, T as TOKEN_IDENT, h as TOKEN_DELIM, k as TOKEN_DIMENSION, i as TOKEN_NUMBER } from './lexer-CtBKgfVv.js';
2
- import { an as CHAR_MINUS_HYPHEN, a4 as CHAR_PLUS, al as skip_whitespace_forward, I as NODE_SELECTOR_NTH, C as CSSNode, Y as CSSDataArena } from './css-node-aIMm9_Cb.js';
2
+ import { ao as CHAR_MINUS_HYPHEN, a5 as CHAR_PLUS, am as skip_whitespace_forward, I as NODE_SELECTOR_NTH, C as CSSNode, Z as CSSDataArena } from './css-node-BpZTUiy6.js';
3
3
 
4
4
  class ANplusBParser {
5
5
  lexer;
@@ -1,5 +1,5 @@
1
1
  import { L as Lexer, q as TOKEN_COMMA, T as TOKEN_IDENT, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN, l as TOKEN_WHITESPACE, f as TOKEN_URL, a as TOKEN_FUNCTION, d as TOKEN_STRING, y as TOKEN_EOF } from './lexer-CtBKgfVv.js';
2
- import { Y as CSSDataArena, C as CSSNode, am as str_equals, T as NODE_PRELUDE_OPERATOR, O as NODE_PRELUDE_MEDIA_TYPE, L as NODE_PRELUDE_MEDIA_QUERY, M as NODE_PRELUDE_MEDIA_FEATURE, a2 as trim_boundaries, S as NODE_PRELUDE_IDENTIFIER, P as NODE_PRELUDE_CONTAINER_QUERY, Q as NODE_PRELUDE_SUPPORTS_QUERY, R as NODE_PRELUDE_LAYER_NAME, U as NODE_PRELUDE_IMPORT_URL, V as NODE_PRELUDE_IMPORT_LAYER, W as NODE_PRELUDE_IMPORT_SUPPORTS, al as skip_whitespace_forward } from './css-node-aIMm9_Cb.js';
2
+ import { Z as CSSDataArena, C as CSSNode, an as str_equals, U as NODE_PRELUDE_OPERATOR, O as NODE_PRELUDE_MEDIA_TYPE, L as NODE_PRELUDE_MEDIA_QUERY, M as NODE_PRELUDE_MEDIA_FEATURE, a3 as trim_boundaries, S as NODE_PRELUDE_IDENTIFIER, P as NODE_PRELUDE_CONTAINER_QUERY, Q as NODE_PRELUDE_SUPPORTS_QUERY, R as NODE_PRELUDE_LAYER_NAME, V as NODE_PRELUDE_IMPORT_URL, W as NODE_PRELUDE_IMPORT_LAYER, X as NODE_PRELUDE_IMPORT_SUPPORTS, am as skip_whitespace_forward } from './css-node-BpZTUiy6.js';
3
3
 
4
4
  class AtRulePreludeParser {
5
5
  lexer;
@@ -11,6 +11,10 @@ export declare class SelectorParser {
11
11
  private parse_complex_selector;
12
12
  private parse_compound_selector;
13
13
  private parse_simple_selector;
14
+ private parse_namespace_local_part;
15
+ private parse_type_or_namespace_selector;
16
+ private parse_universal_or_namespace_selector;
17
+ private parse_empty_namespace_selector;
14
18
  private try_parse_combinator;
15
19
  private parse_class_selector;
16
20
  private parse_attribute_selector;
@@ -1,5 +1,5 @@
1
1
  import { L as Lexer, q as TOKEN_COMMA, h as TOKEN_DELIM, y as TOKEN_EOF, l as TOKEN_WHITESPACE, a as TOKEN_FUNCTION, o as TOKEN_COLON, r as TOKEN_LEFT_BRACKET, c as TOKEN_HASH, T as TOKEN_IDENT, s as TOKEN_RIGHT_BRACKET, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN, d as TOKEN_STRING } from './lexer-CtBKgfVv.js';
2
- import { Y as CSSDataArena, w as NODE_SELECTOR_LIST, C as CSSNode, m as NODE_SELECTOR, a3 as CHAR_GREATER_THAN, a4 as CHAR_PLUS, a5 as CHAR_TILDE, F as NODE_SELECTOR_COMBINATOR, a6 as CHAR_PERIOD, a7 as CHAR_ASTERISK, G as NODE_SELECTOR_UNIVERSAL, a8 as CHAR_AMPERSAND, H as NODE_SELECTOR_NESTING, z as NODE_SELECTOR_ID, x as NODE_SELECTOR_TYPE, a9 as is_whitespace, aa as is_combinator, y as NODE_SELECTOR_CLASS, B as NODE_SELECTOR_ATTRIBUTE, ab as skip_whitespace_and_comments_forward, ac as skip_whitespace_and_comments_backward, ad as CHAR_EQUALS, ae as CHAR_PIPE, af as CHAR_CARET, ag as CHAR_DOLLAR, A as ATTR_OPERATOR_NONE, g as ATTR_FLAG_NONE, a as ATTR_OPERATOR_EQUAL, b as ATTR_OPERATOR_TILDE_EQUAL, c as ATTR_OPERATOR_PIPE_EQUAL, d as ATTR_OPERATOR_CARET_EQUAL, e as ATTR_OPERATOR_DOLLAR_EQUAL, f as ATTR_OPERATOR_STAR_EQUAL, ah as CHAR_SINGLE_QUOTE, ai as CHAR_DOUBLE_QUOTE, h as ATTR_FLAG_CASE_INSENSITIVE, i as ATTR_FLAG_CASE_SENSITIVE, aj as CHAR_COLON, E as NODE_SELECTOR_PSEUDO_ELEMENT, D as NODE_SELECTOR_PSEUDO_CLASS, a0 as is_vendor_prefixed, a1 as FLAG_VENDOR_PREFIXED, ak as FLAG_HAS_PARENS, K as NODE_SELECTOR_LANG, al as skip_whitespace_forward, J as NODE_SELECTOR_NTH_OF } from './css-node-aIMm9_Cb.js';
2
+ import { Z as CSSDataArena, w as NODE_SELECTOR_LIST, C as CSSNode, m as NODE_SELECTOR, a4 as CHAR_GREATER_THAN, a5 as CHAR_PLUS, a6 as CHAR_TILDE, F as NODE_SELECTOR_COMBINATOR, a7 as CHAR_PERIOD, a8 as CHAR_ASTERISK, a9 as CHAR_AMPERSAND, H as NODE_SELECTOR_NESTING, aa as CHAR_PIPE, z as NODE_SELECTOR_ID, x as NODE_SELECTOR_TYPE, G as NODE_SELECTOR_UNIVERSAL, ab as is_whitespace, ac as is_combinator, y as NODE_SELECTOR_CLASS, B as NODE_SELECTOR_ATTRIBUTE, ad as skip_whitespace_and_comments_forward, ae as skip_whitespace_and_comments_backward, af as CHAR_EQUALS, ag as CHAR_CARET, ah as CHAR_DOLLAR, A as ATTR_OPERATOR_NONE, g as ATTR_FLAG_NONE, a as ATTR_OPERATOR_EQUAL, b as ATTR_OPERATOR_TILDE_EQUAL, c as ATTR_OPERATOR_PIPE_EQUAL, d as ATTR_OPERATOR_CARET_EQUAL, e as ATTR_OPERATOR_DOLLAR_EQUAL, f as ATTR_OPERATOR_STAR_EQUAL, ai as CHAR_SINGLE_QUOTE, aj as CHAR_DOUBLE_QUOTE, h as ATTR_FLAG_CASE_INSENSITIVE, i as ATTR_FLAG_CASE_SENSITIVE, ak as CHAR_COLON, E as NODE_SELECTOR_PSEUDO_ELEMENT, D as NODE_SELECTOR_PSEUDO_CLASS, a1 as is_vendor_prefixed, a2 as FLAG_VENDOR_PREFIXED, al as FLAG_HAS_PARENS, K as NODE_SELECTOR_LANG, am as skip_whitespace_forward, J as NODE_SELECTOR_NTH_OF } from './css-node-BpZTUiy6.js';
3
3
  import { ANplusBParser } from './parse-anplusb.js';
4
4
 
5
5
  class SelectorParser {
@@ -158,7 +158,7 @@ class SelectorParser {
158
158
  let end = this.lexer.token_end;
159
159
  switch (token_type) {
160
160
  case TOKEN_IDENT:
161
- return this.create_node(NODE_SELECTOR_TYPE, start, end);
161
+ return this.parse_type_or_namespace_selector(start, end);
162
162
  case TOKEN_HASH:
163
163
  return this.create_node(NODE_SELECTOR_ID, start, end);
164
164
  case TOKEN_DELIM:
@@ -166,9 +166,11 @@ class SelectorParser {
166
166
  if (ch === CHAR_PERIOD) {
167
167
  return this.parse_class_selector(start);
168
168
  } else if (ch === CHAR_ASTERISK) {
169
- return this.create_node(NODE_SELECTOR_UNIVERSAL, start, end);
169
+ return this.parse_universal_or_namespace_selector(start, end);
170
170
  } else if (ch === CHAR_AMPERSAND) {
171
171
  return this.create_node(NODE_SELECTOR_NESTING, start, end);
172
+ } else if (ch === CHAR_PIPE) {
173
+ return this.parse_empty_namespace_selector(start);
172
174
  }
173
175
  return null;
174
176
  case TOKEN_LEFT_BRACKET:
@@ -184,6 +186,52 @@ class SelectorParser {
184
186
  return null;
185
187
  }
186
188
  }
189
+ // Parse the local part after | in a namespace selector (E or *)
190
+ // Returns the node type (TYPE or UNIVERSAL) or null if invalid
191
+ parse_namespace_local_part(selector_start, namespace_start, namespace_length) {
192
+ const saved = this.lexer.save_position();
193
+ this.lexer.next_token_fast(false);
194
+ let node_type;
195
+ if (this.lexer.token_type === TOKEN_IDENT) {
196
+ node_type = NODE_SELECTOR_TYPE;
197
+ } else if (this.lexer.token_type === TOKEN_DELIM && this.source.charCodeAt(this.lexer.token_start) === CHAR_ASTERISK) {
198
+ node_type = NODE_SELECTOR_UNIVERSAL;
199
+ } else {
200
+ this.lexer.restore_position(saved);
201
+ return null;
202
+ }
203
+ let node = this.create_node(node_type, selector_start, this.lexer.token_end);
204
+ this.arena.set_content_start(node, namespace_start);
205
+ this.arena.set_content_length(node, namespace_length);
206
+ return node;
207
+ }
208
+ // Parse type selector or namespace selector (ns|E or ns|*)
209
+ // Called when we've seen an IDENT token
210
+ parse_type_or_namespace_selector(start, end) {
211
+ if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_PIPE) {
212
+ this.lexer.pos++;
213
+ let node = this.parse_namespace_local_part(start, start, end - start);
214
+ if (node !== null) return node;
215
+ this.lexer.pos = end;
216
+ }
217
+ return this.create_node(NODE_SELECTOR_TYPE, start, end);
218
+ }
219
+ // Parse universal selector or namespace selector (*|E or *|*)
220
+ // Called when we've seen a * DELIM token
221
+ parse_universal_or_namespace_selector(start, end) {
222
+ if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === CHAR_PIPE) {
223
+ this.lexer.pos++;
224
+ let node = this.parse_namespace_local_part(start, start, end - start);
225
+ if (node !== null) return node;
226
+ this.lexer.pos = end;
227
+ }
228
+ return this.create_node(NODE_SELECTOR_UNIVERSAL, start, end);
229
+ }
230
+ // Parse empty namespace selector (|E or |*)
231
+ // Called when we've seen a | DELIM token at the start
232
+ parse_empty_namespace_selector(start) {
233
+ return this.parse_namespace_local_part(start, start, 1);
234
+ }
187
235
  // Parse combinator (>, +, ~, or descendant space)
188
236
  try_parse_combinator() {
189
237
  let whitespace_start = this.lexer.pos;
@@ -1,5 +1,5 @@
1
1
  import { L as Lexer, y as TOKEN_EOF, t as TOKEN_LEFT_PAREN, q as TOKEN_COMMA, h as TOKEN_DELIM, a as TOKEN_FUNCTION, c as TOKEN_HASH, d as TOKEN_STRING, k as TOKEN_DIMENSION, j as TOKEN_PERCENTAGE, i as TOKEN_NUMBER, T as TOKEN_IDENT, u as TOKEN_RIGHT_PAREN } from './lexer-CtBKgfVv.js';
2
- import { Y as CSSDataArena, C as CSSNode, a9 as is_whitespace, u as NODE_VALUE_OPERATOR, s as NODE_VALUE_COLOR, r as NODE_VALUE_STRING, q as NODE_VALUE_DIMENSION, p as NODE_VALUE_NUMBER, o as NODE_VALUE_KEYWORD, a4 as CHAR_PLUS, an as CHAR_MINUS_HYPHEN, a7 as CHAR_ASTERISK, ao as CHAR_FORWARD_SLASH, t as NODE_VALUE_FUNCTION, v as NODE_VALUE_PARENTHESIS } from './css-node-aIMm9_Cb.js';
2
+ import { Z as CSSDataArena, C as CSSNode, ab as is_whitespace, u as NODE_VALUE_OPERATOR, s as NODE_VALUE_COLOR, r as NODE_VALUE_STRING, q as NODE_VALUE_DIMENSION, p as NODE_VALUE_NUMBER, o as NODE_VALUE_KEYWORD, a5 as CHAR_PLUS, ao as CHAR_MINUS_HYPHEN, a8 as CHAR_ASTERISK, ap as CHAR_FORWARD_SLASH, t as NODE_VALUE_FUNCTION, v as NODE_VALUE_PARENTHESIS } from './css-node-BpZTUiy6.js';
3
3
 
4
4
  class ValueParser {
5
5
  lexer;
package/dist/parse.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { L as Lexer, y as TOKEN_EOF, b as TOKEN_AT_KEYWORD, v as TOKEN_LEFT_BRACE, w as TOKEN_RIGHT_BRACE, T as TOKEN_IDENT, o as TOKEN_COLON, p as TOKEN_SEMICOLON, h as TOKEN_DELIM } from './lexer-CtBKgfVv.js';
2
- import { Y as CSSDataArena, n as NODE_STYLESHEET, C as CSSNode, N as NODE_STYLE_RULE, Z as FLAG_HAS_BLOCK, _ as NODE_BLOCK, $ as FLAG_HAS_DECLARATIONS, w as NODE_SELECTOR_LIST, l as NODE_DECLARATION, a0 as is_vendor_prefixed, a1 as FLAG_VENDOR_PREFIXED, a2 as trim_boundaries, X as FLAG_IMPORTANT, j as NODE_AT_RULE } from './css-node-aIMm9_Cb.js';
3
- export { k as NODE_COMMENT, P as NODE_PRELUDE_CONTAINER_QUERY, S as NODE_PRELUDE_IDENTIFIER, V as NODE_PRELUDE_IMPORT_LAYER, W as NODE_PRELUDE_IMPORT_SUPPORTS, U as NODE_PRELUDE_IMPORT_URL, R as NODE_PRELUDE_LAYER_NAME, M as NODE_PRELUDE_MEDIA_FEATURE, L as NODE_PRELUDE_MEDIA_QUERY, O as NODE_PRELUDE_MEDIA_TYPE, T as NODE_PRELUDE_OPERATOR, Q as NODE_PRELUDE_SUPPORTS_QUERY, m as NODE_SELECTOR, B as NODE_SELECTOR_ATTRIBUTE, y as NODE_SELECTOR_CLASS, F as NODE_SELECTOR_COMBINATOR, z as NODE_SELECTOR_ID, K as NODE_SELECTOR_LANG, H as NODE_SELECTOR_NESTING, I as NODE_SELECTOR_NTH, J as NODE_SELECTOR_NTH_OF, D as NODE_SELECTOR_PSEUDO_CLASS, E as NODE_SELECTOR_PSEUDO_ELEMENT, x as NODE_SELECTOR_TYPE, G as NODE_SELECTOR_UNIVERSAL, s as NODE_VALUE_COLOR, q as NODE_VALUE_DIMENSION, t as NODE_VALUE_FUNCTION, o as NODE_VALUE_KEYWORD, p as NODE_VALUE_NUMBER, u as NODE_VALUE_OPERATOR, v as NODE_VALUE_PARENTHESIS, r as NODE_VALUE_STRING } from './css-node-aIMm9_Cb.js';
2
+ import { Z as CSSDataArena, n as NODE_STYLESHEET, C as CSSNode, N as NODE_STYLE_RULE, _ as FLAG_HAS_BLOCK, $ as NODE_BLOCK, a0 as FLAG_HAS_DECLARATIONS, w as NODE_SELECTOR_LIST, l as NODE_DECLARATION, a1 as is_vendor_prefixed, a2 as FLAG_VENDOR_PREFIXED, a3 as trim_boundaries, Y as FLAG_IMPORTANT, j as NODE_AT_RULE } from './css-node-BpZTUiy6.js';
3
+ export { k as NODE_COMMENT, P as NODE_PRELUDE_CONTAINER_QUERY, S as NODE_PRELUDE_IDENTIFIER, W as NODE_PRELUDE_IMPORT_LAYER, X as NODE_PRELUDE_IMPORT_SUPPORTS, V as NODE_PRELUDE_IMPORT_URL, R as NODE_PRELUDE_LAYER_NAME, M as NODE_PRELUDE_MEDIA_FEATURE, L as NODE_PRELUDE_MEDIA_QUERY, O as NODE_PRELUDE_MEDIA_TYPE, U as NODE_PRELUDE_OPERATOR, Q as NODE_PRELUDE_SUPPORTS_QUERY, m as NODE_SELECTOR, B as NODE_SELECTOR_ATTRIBUTE, y as NODE_SELECTOR_CLASS, F as NODE_SELECTOR_COMBINATOR, z as NODE_SELECTOR_ID, K as NODE_SELECTOR_LANG, H as NODE_SELECTOR_NESTING, I as NODE_SELECTOR_NTH, J as NODE_SELECTOR_NTH_OF, D as NODE_SELECTOR_PSEUDO_CLASS, E as NODE_SELECTOR_PSEUDO_ELEMENT, x as NODE_SELECTOR_TYPE, G as NODE_SELECTOR_UNIVERSAL, s as NODE_VALUE_COLOR, q as NODE_VALUE_DIMENSION, t as NODE_VALUE_FUNCTION, o as NODE_VALUE_KEYWORD, p as NODE_VALUE_NUMBER, u as NODE_VALUE_OPERATOR, v as NODE_VALUE_PARENTHESIS, r as NODE_VALUE_STRING } from './css-node-BpZTUiy6.js';
4
4
  import { ValueParser } from './parse-value.js';
5
5
  import { SelectorParser } from './parse-selector.js';
6
6
  import { AtRulePreludeParser } from './parse-atrule-prelude.js';
@@ -346,8 +346,8 @@ class Parser {
346
346
  }
347
347
  if (this.peek_type() === TOKEN_RIGHT_BRACE) {
348
348
  let block_end = this.lexer.token_start;
349
- this.next_token();
350
349
  last_end = this.lexer.token_end;
350
+ this.next_token();
351
351
  this.arena.set_length(block_node, block_end - block_start);
352
352
  } else {
353
353
  this.arena.set_length(block_node, last_end - block_start);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-parser",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "High-performance CSS lexer and parser, optimized for CSS inspection and analysis",
5
5
  "author": "Bart Veneman <bat@projectwallace.com>",
6
6
  "license": "MIT",