@projectwallace/css-parser 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/arena.d.ts CHANGED
@@ -14,6 +14,7 @@ export declare const FUNCTION = 15;
14
14
  export declare const OPERATOR = 16;
15
15
  export declare const PARENTHESIS = 17;
16
16
  export declare const URL = 18;
17
+ export declare const VALUE = 19;
17
18
  export declare const SELECTOR_LIST = 20;
18
19
  export declare const TYPE_SELECTOR = 21;
19
20
  export declare const CLASS_SELECTOR = 22;
@@ -35,6 +36,7 @@ export declare const SUPPORTS_QUERY = 36;
35
36
  export declare const LAYER_NAME = 37;
36
37
  export declare const PRELUDE_OPERATOR = 38;
37
38
  export declare const FEATURE_RANGE = 39;
39
+ export declare const AT_RULE_PRELUDE = 40;
38
40
  export declare const FLAG_IMPORTANT: number;
39
41
  export declare const FLAG_HAS_ERROR: number;
40
42
  export declare const FLAG_LENGTH_OVERFLOW: number;
package/dist/arena.js CHANGED
@@ -15,6 +15,7 @@ const FUNCTION = 15;
15
15
  const OPERATOR = 16;
16
16
  const PARENTHESIS = 17;
17
17
  const URL = 18;
18
+ const VALUE = 19;
18
19
  const SELECTOR_LIST = 20;
19
20
  const TYPE_SELECTOR = 21;
20
21
  const CLASS_SELECTOR = 22;
@@ -36,6 +37,7 @@ const SUPPORTS_QUERY = 36;
36
37
  const LAYER_NAME = 37;
37
38
  const PRELUDE_OPERATOR = 38;
38
39
  const FEATURE_RANGE = 39;
40
+ const AT_RULE_PRELUDE = 40;
39
41
  const FLAG_IMPORTANT = 1 << 0;
40
42
  const FLAG_HAS_ERROR = 1 << 1;
41
43
  const FLAG_LENGTH_OVERFLOW = 1 << 2;
@@ -67,7 +69,9 @@ class CSSDataArena {
67
69
  // Growth multiplier when capacity is exceeded
68
70
  static GROWTH_FACTOR = 1.3;
69
71
  // Estimated nodes per KB of CSS (based on real-world data)
70
- static NODES_PER_KB = 270;
72
+ // Increased from 270 to 325 to account for VALUE wrapper nodes
73
+ // (~20% of nodes are declarations, +1 VALUE node per declaration = +20% nodes)
74
+ static NODES_PER_KB = 325;
71
75
  // Buffer to avoid frequent growth (15%)
72
76
  static CAPACITY_BUFFER = 1.2;
73
77
  constructor(initial_capacity = 1024) {
@@ -281,4 +285,4 @@ class CSSDataArena {
281
285
  }
282
286
  }
283
287
 
284
- export { ATTRIBUTE_SELECTOR, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_NONE, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, AT_RULE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, CSSDataArena, DECLARATION, DIMENSION, FEATURE_RANGE, FLAG_BROWSERHACK, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, FLAG_HAS_ERROR, FLAG_HAS_PARENS, FLAG_IMPORTANT, FLAG_LENGTH_OVERFLOW, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PARENTHESIS, PRELUDE_OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, STRING, STYLESHEET, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, UNIVERSAL_SELECTOR, URL };
288
+ export { ATTRIBUTE_SELECTOR, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_NONE, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, AT_RULE, AT_RULE_PRELUDE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, CSSDataArena, DECLARATION, DIMENSION, FEATURE_RANGE, FLAG_BROWSERHACK, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, FLAG_HAS_ERROR, FLAG_HAS_PARENS, FLAG_IMPORTANT, FLAG_LENGTH_OVERFLOW, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PARENTHESIS, PRELUDE_OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, STRING, STYLESHEET, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, UNIVERSAL_SELECTOR, URL, VALUE };
@@ -1,3 +1,9 @@
1
+ export declare let CHAR_ALPHA: number;
2
+ export declare let CHAR_DIGIT: number;
3
+ export declare let CHAR_HEX: number;
4
+ export declare let CHAR_WHITESPACE: number;
5
+ export declare let CHAR_NEWLINE: number;
6
+ export declare let char_types: Uint8Array<ArrayBuffer>;
1
7
  export declare function is_digit(ch: number): boolean;
2
8
  export declare function is_hex_digit(ch: number): boolean;
3
9
  export declare function is_alpha(ch: number): boolean;
@@ -52,4 +52,4 @@ function is_ident_char(ch) {
52
52
  return is_ident_start(ch) || is_digit(ch);
53
53
  }
54
54
 
55
- export { is_alpha, is_digit, is_hex_digit, is_ident_char, is_ident_start, is_newline, is_whitespace };
55
+ export { CHAR_ALPHA, CHAR_DIGIT, CHAR_HEX, CHAR_NEWLINE, CHAR_WHITESPACE, char_types, is_alpha, is_digit, is_hex_digit, is_ident_char, is_ident_start, is_newline, is_whitespace };
@@ -1,5 +1,5 @@
1
- import { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FEATURE_RANGE, FLAG_IMPORTANT, 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';
2
- export { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FEATURE_RANGE, FLAG_IMPORTANT, 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, };
1
+ import { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, VALUE, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FEATURE_RANGE, AT_RULE_PRELUDE, FLAG_IMPORTANT, 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';
2
+ export { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, VALUE, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FEATURE_RANGE, AT_RULE_PRELUDE, FLAG_IMPORTANT, 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, };
3
3
  export declare const NODE_TYPES: {
4
4
  readonly STYLESHEET: 1;
5
5
  readonly STYLE_RULE: 2;
@@ -17,6 +17,7 @@ export declare const NODE_TYPES: {
17
17
  readonly OPERATOR: 16;
18
18
  readonly PARENTHESIS: 17;
19
19
  readonly URL: 18;
20
+ readonly VALUE: 19;
20
21
  readonly SELECTOR_LIST: 20;
21
22
  readonly TYPE_SELECTOR: 21;
22
23
  readonly CLASS_SELECTOR: 22;
@@ -38,4 +39,5 @@ export declare const NODE_TYPES: {
38
39
  readonly LAYER_NAME: 37;
39
40
  readonly PRELUDE_OPERATOR: 38;
40
41
  readonly FEATURE_RANGE: 39;
42
+ readonly AT_RULE_PRELUDE: 40;
41
43
  };
package/dist/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- import { FEATURE_RANGE, PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_FEATURE, MEDIA_QUERY, LANG_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, COMBINATOR, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, ATTRIBUTE_SELECTOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, SELECTOR_LIST, URL, PARENTHESIS, OPERATOR, FUNCTION, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, BLOCK, COMMENT, SELECTOR, DECLARATION, AT_RULE, STYLE_RULE, STYLESHEET } from './arena.js';
1
+ import { AT_RULE_PRELUDE, FEATURE_RANGE, PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_FEATURE, MEDIA_QUERY, LANG_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, COMBINATOR, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, ATTRIBUTE_SELECTOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, SELECTOR_LIST, VALUE, URL, PARENTHESIS, OPERATOR, FUNCTION, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, BLOCK, COMMENT, SELECTOR, DECLARATION, AT_RULE, STYLE_RULE, STYLESHEET } from './arena.js';
2
2
  export { ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_NONE, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, FLAG_IMPORTANT } from './arena.js';
3
3
 
4
4
  const NODE_TYPES = {
@@ -20,6 +20,7 @@ const NODE_TYPES = {
20
20
  OPERATOR,
21
21
  PARENTHESIS,
22
22
  URL,
23
+ VALUE,
23
24
  // Selector nodes
24
25
  SELECTOR_LIST,
25
26
  TYPE_SELECTOR,
@@ -42,7 +43,8 @@ const NODE_TYPES = {
42
43
  SUPPORTS_QUERY,
43
44
  LAYER_NAME,
44
45
  PRELUDE_OPERATOR,
45
- FEATURE_RANGE
46
+ FEATURE_RANGE,
47
+ AT_RULE_PRELUDE
46
48
  };
47
49
 
48
- export { ATTRIBUTE_SELECTOR, AT_RULE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FEATURE_RANGE, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NODE_TYPES, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PARENTHESIS, PRELUDE_OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, STRING, STYLESHEET, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, UNIVERSAL_SELECTOR, URL };
50
+ export { ATTRIBUTE_SELECTOR, AT_RULE, AT_RULE_PRELUDE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FEATURE_RANGE, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NODE_TYPES, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PARENTHESIS, PRELUDE_OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, STRING, STYLESHEET, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, UNIVERSAL_SELECTOR, URL, VALUE };
@@ -1,5 +1,5 @@
1
1
  import type { CSSDataArena } from './arena';
2
- import { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FEATURE_RANGE } from './arena';
2
+ import { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, VALUE, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FEATURE_RANGE, AT_RULE_PRELUDE } from './arena';
3
3
  export declare const TYPE_NAMES: {
4
4
  readonly 1: "StyleSheet";
5
5
  readonly 2: "Rule";
@@ -17,6 +17,7 @@ export declare const TYPE_NAMES: {
17
17
  readonly 16: "Operator";
18
18
  readonly 17: "Parentheses";
19
19
  readonly 18: "Url";
20
+ readonly 19: "Value";
20
21
  readonly 20: "SelectorList";
21
22
  readonly 21: "TypeSelector";
22
23
  readonly 22: "ClassSelector";
@@ -38,9 +39,10 @@ export declare const TYPE_NAMES: {
38
39
  readonly 37: "Layer";
39
40
  readonly 38: "Operator";
40
41
  readonly 39: "MediaFeatureRange";
42
+ readonly 40: "AtrulePrelude";
41
43
  };
42
44
  export type TypeName = (typeof TYPE_NAMES)[keyof typeof TYPE_NAMES] | 'unknown';
43
- export type CSSNodeType = typeof STYLESHEET | typeof STYLE_RULE | typeof AT_RULE | typeof DECLARATION | typeof SELECTOR | typeof COMMENT | typeof BLOCK | typeof IDENTIFIER | typeof NUMBER | typeof DIMENSION | typeof STRING | typeof HASH | typeof FUNCTION | typeof OPERATOR | typeof PARENTHESIS | typeof URL | typeof SELECTOR_LIST | typeof TYPE_SELECTOR | typeof CLASS_SELECTOR | typeof ID_SELECTOR | typeof ATTRIBUTE_SELECTOR | typeof PSEUDO_CLASS_SELECTOR | typeof PSEUDO_ELEMENT_SELECTOR | typeof COMBINATOR | typeof UNIVERSAL_SELECTOR | typeof NESTING_SELECTOR | typeof NTH_SELECTOR | typeof NTH_OF_SELECTOR | typeof LANG_SELECTOR | typeof MEDIA_QUERY | typeof MEDIA_FEATURE | typeof MEDIA_TYPE | typeof CONTAINER_QUERY | typeof SUPPORTS_QUERY | typeof LAYER_NAME | typeof PRELUDE_OPERATOR | typeof FEATURE_RANGE;
45
+ export type CSSNodeType = typeof STYLESHEET | typeof STYLE_RULE | typeof AT_RULE | typeof DECLARATION | typeof SELECTOR | typeof COMMENT | typeof BLOCK | typeof IDENTIFIER | typeof NUMBER | typeof DIMENSION | typeof STRING | typeof HASH | typeof FUNCTION | typeof OPERATOR | typeof PARENTHESIS | typeof URL | typeof VALUE | typeof SELECTOR_LIST | typeof TYPE_SELECTOR | typeof CLASS_SELECTOR | typeof ID_SELECTOR | typeof ATTRIBUTE_SELECTOR | typeof PSEUDO_CLASS_SELECTOR | typeof PSEUDO_ELEMENT_SELECTOR | typeof COMBINATOR | typeof UNIVERSAL_SELECTOR | typeof NESTING_SELECTOR | typeof NTH_SELECTOR | typeof NTH_OF_SELECTOR | typeof LANG_SELECTOR | typeof MEDIA_QUERY | typeof MEDIA_FEATURE | typeof MEDIA_TYPE | typeof CONTAINER_QUERY | typeof SUPPORTS_QUERY | typeof LAYER_NAME | typeof PRELUDE_OPERATOR | typeof FEATURE_RANGE | typeof AT_RULE_PRELUDE;
44
46
  export interface CloneOptions {
45
47
  /**
46
48
  * Recursively clone all children
@@ -62,7 +64,7 @@ export type PlainCSSNode = {
62
64
  property?: string;
63
65
  value?: string | number | null;
64
66
  unit?: string;
65
- prelude?: string;
67
+ prelude?: PlainCSSNode | null;
66
68
  is_important?: boolean;
67
69
  is_vendor_prefixed?: boolean;
68
70
  is_browserhack?: boolean;
@@ -102,35 +104,37 @@ export declare class CSSNode {
102
104
  * For URL nodes with quoted string: returns the string with quotes (consistent with STRING node)
103
105
  * For URL nodes with unquoted URL: returns the URL content without quotes
104
106
  */
105
- get value(): string | number | null;
107
+ get value(): CSSNode | string | number | null;
106
108
  /** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
107
109
  get value_as_number(): number | null;
108
110
  /**
109
- * Get the prelude text (for at-rules: "(min-width: 768px)" in "@media (min-width: 768px)")
110
- * This is an alias for `value` to make at-rule usage more semantic
111
+ * Get the prelude node:
112
+ * - For at-rules: AT_RULE_PRELUDE wrapper containing structured prelude children (media queries, layer names, etc.)
113
+ * - For style rules: SELECTOR_LIST or SELECTOR node
114
+ * Returns null if no prelude exists
111
115
  */
112
- get prelude(): string | null;
116
+ get prelude(): CSSNode | null | undefined;
113
117
  /**
114
118
  * Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
115
119
  * Returns one of the ATTR_OPERATOR_* constants
116
120
  */
117
- get attr_operator(): number;
121
+ get attr_operator(): number | undefined;
118
122
  /**
119
123
  * Get the attribute flags (for attribute selectors: i, s)
120
124
  * Returns one of the ATTR_FLAG_* constants
121
125
  */
122
- get attr_flags(): number;
126
+ get attr_flags(): number | undefined;
123
127
  /** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
124
- get unit(): string | null;
128
+ get unit(): string | undefined;
125
129
  /** Check if this declaration has !important */
126
- get is_important(): boolean | null;
130
+ get is_important(): boolean | undefined;
127
131
  /** Check if this declaration has a browser hack prefix */
128
- get is_browserhack(): boolean | null;
132
+ get is_browserhack(): boolean | undefined;
129
133
  /** Check if this has a vendor prefix (computed on-demand) */
130
134
  get is_vendor_prefixed(): boolean;
131
135
  /** Check if this node has an error */
132
136
  get has_error(): boolean;
133
- /** Check if this at-rule has a prelude */
137
+ /** Check if this node has a prelude (at-rules and style rules) */
134
138
  get has_prelude(): boolean;
135
139
  /** Check if this rule has a block { } */
136
140
  get has_block(): boolean;
@@ -139,9 +143,7 @@ export declare class CSSNode {
139
143
  /** Get the block node (for style rules and at-rules with blocks) */
140
144
  get block(): CSSNode | null;
141
145
  /** Check if this block is empty (no declarations or rules, only comments allowed) */
142
- get is_empty(): boolean;
143
- /** Get array of parsed value nodes (for declarations only) */
144
- get values(): CSSNode[];
146
+ get is_empty(): boolean | undefined;
145
147
  /** Get start line number */
146
148
  get line(): number;
147
149
  /** Get start column number */
@@ -172,37 +174,18 @@ export declare class CSSNode {
172
174
  /** Make CSSNode iterable over its children */
173
175
  [Symbol.iterator](): Iterator<CSSNode>;
174
176
  /** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
175
- get nth_a(): string | null;
177
+ get nth_a(): string | undefined;
176
178
  /** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
177
- get nth_b(): string | null;
179
+ get nth_b(): string | undefined;
178
180
  /** Get the An+B formula node from :nth-child(2n+1 of .foo) */
179
- get nth(): CSSNode | null;
181
+ get nth(): CSSNode | undefined;
180
182
  /** Get the selector list from :nth-child(2n+1 of .foo) */
181
- get selector(): CSSNode | null;
183
+ get selector(): CSSNode | undefined;
182
184
  /**
183
185
  * Get selector list from pseudo-class functions
184
186
  * Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
185
187
  */
186
- get selector_list(): CSSNode | null;
187
- /**
188
- * Iterator over first compound selector parts (zero allocation)
189
- * Yields parts before the first combinator
190
- */
191
- compound_parts(): IterableIterator<CSSNode>;
192
- /**
193
- * Get first compound selector as array
194
- * Returns array of parts before first combinator
195
- */
196
- get first_compound(): CSSNode[];
197
- /**
198
- * Split selector into compound selectors
199
- * Returns array of compound arrays split by combinators
200
- */
201
- get all_compounds(): CSSNode[][];
202
- /** Check if selector is compound (no combinators) */
203
- get is_compound(): boolean;
204
- /** Get text of first compound selector (no node allocation) */
205
- get first_compound_text(): string;
188
+ get selector_list(): CSSNode | undefined;
206
189
  /**
207
190
  * Clone this node as a mutable plain JavaScript object with children as arrays.
208
191
  * See API.md for examples.
package/dist/css-node.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DIMENSION, NUMBER, URL, STRING, DECLARATION, FLAG_IMPORTANT, FLAG_BROWSERHACK, IDENTIFIER, FUNCTION, AT_RULE, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, FLAG_HAS_ERROR, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, STYLE_RULE, BLOCK, COMMENT, FLAG_HAS_PARENS, NTH_SELECTOR, NTH_OF_SELECTOR, SELECTOR_LIST, SELECTOR, COMBINATOR, ATTRIBUTE_SELECTOR, FEATURE_RANGE, PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_FEATURE, MEDIA_QUERY, LANG_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, PARENTHESIS, OPERATOR, HASH, STYLESHEET } from './arena.js';
1
+ import { DECLARATION, DIMENSION, NUMBER, URL, STRING, AT_RULE, AT_RULE_PRELUDE, STYLE_RULE, ATTRIBUTE_SELECTOR, FLAG_IMPORTANT, FLAG_BROWSERHACK, IDENTIFIER, FUNCTION, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, FLAG_HAS_ERROR, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, BLOCK, COMMENT, FLAG_HAS_PARENS, NTH_SELECTOR, NTH_OF_SELECTOR, SELECTOR_LIST, FEATURE_RANGE, PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_FEATURE, MEDIA_QUERY, LANG_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, COMBINATOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, VALUE, PARENTHESIS, OPERATOR, HASH, SELECTOR, STYLESHEET } from './arena.js';
2
2
  import { str_starts_with, is_vendor_prefixed, is_whitespace, CHAR_MINUS_HYPHEN, CHAR_PLUS } from './string-utils.js';
3
3
  import { parse_dimension } from './parse-utils.js';
4
4
 
@@ -19,6 +19,7 @@ const TYPE_NAMES = {
19
19
  [OPERATOR]: "Operator",
20
20
  [PARENTHESIS]: "Parentheses",
21
21
  [URL]: "Url",
22
+ [VALUE]: "Value",
22
23
  [SELECTOR_LIST]: "SelectorList",
23
24
  [TYPE_SELECTOR]: "TypeSelector",
24
25
  [CLASS_SELECTOR]: "ClassSelector",
@@ -39,7 +40,8 @@ const TYPE_NAMES = {
39
40
  [SUPPORTS_QUERY]: "SupportsQuery",
40
41
  [LAYER_NAME]: "Layer",
41
42
  [PRELUDE_OPERATOR]: "Operator",
42
- [FEATURE_RANGE]: "MediaFeatureRange"
43
+ [FEATURE_RANGE]: "MediaFeatureRange",
44
+ [AT_RULE_PRELUDE]: "AtrulePrelude"
43
45
  };
44
46
  class CSSNode {
45
47
  arena;
@@ -94,6 +96,9 @@ class CSSNode {
94
96
  */
95
97
  get value() {
96
98
  let { type, text } = this;
99
+ if (type === DECLARATION && this.first_child) {
100
+ return this.first_child;
101
+ }
97
102
  if (type === DIMENSION) {
98
103
  return parse_dimension(text).value;
99
104
  }
@@ -133,18 +138,30 @@ class CSSNode {
133
138
  return null;
134
139
  }
135
140
  /**
136
- * Get the prelude text (for at-rules: "(min-width: 768px)" in "@media (min-width: 768px)")
137
- * This is an alias for `value` to make at-rule usage more semantic
141
+ * Get the prelude node:
142
+ * - For at-rules: AT_RULE_PRELUDE wrapper containing structured prelude children (media queries, layer names, etc.)
143
+ * - For style rules: SELECTOR_LIST or SELECTOR node
144
+ * Returns null if no prelude exists
138
145
  */
139
146
  get prelude() {
140
- let val = this.value;
141
- return typeof val === "string" ? val : null;
147
+ if (this.type === AT_RULE) {
148
+ let first = this.first_child;
149
+ if (first && first.type === AT_RULE_PRELUDE) {
150
+ return first;
151
+ }
152
+ return null;
153
+ }
154
+ if (this.type === STYLE_RULE) {
155
+ return this.first_child;
156
+ }
157
+ return void 0;
142
158
  }
143
159
  /**
144
160
  * Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
145
161
  * Returns one of the ATTR_OPERATOR_* constants
146
162
  */
147
163
  get attr_operator() {
164
+ if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
148
165
  return this.arena.get_attr_operator(this.index);
149
166
  }
150
167
  /**
@@ -152,21 +169,22 @@ class CSSNode {
152
169
  * Returns one of the ATTR_FLAG_* constants
153
170
  */
154
171
  get attr_flags() {
172
+ if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
155
173
  return this.arena.get_attr_flags(this.index);
156
174
  }
157
175
  /** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
158
176
  get unit() {
159
- if (this.type !== DIMENSION) return null;
177
+ if (this.type !== DIMENSION) return void 0;
160
178
  return parse_dimension(this.text).unit;
161
179
  }
162
180
  /** Check if this declaration has !important */
163
181
  get is_important() {
164
- if (this.type !== DECLARATION) return null;
182
+ if (this.type !== DECLARATION) return void 0;
165
183
  return this.arena.has_flag(this.index, FLAG_IMPORTANT);
166
184
  }
167
185
  /** Check if this declaration has a browser hack prefix */
168
186
  get is_browserhack() {
169
- if (this.type !== DECLARATION) return null;
187
+ if (this.type !== DECLARATION) return void 0;
170
188
  return this.arena.has_flag(this.index, FLAG_BROWSERHACK);
171
189
  }
172
190
  /** Check if this has a vendor prefix (computed on-demand) */
@@ -191,9 +209,15 @@ class CSSNode {
191
209
  get has_error() {
192
210
  return this.arena.has_flag(this.index, FLAG_HAS_ERROR);
193
211
  }
194
- /** Check if this at-rule has a prelude */
212
+ /** Check if this node has a prelude (at-rules and style rules) */
195
213
  get has_prelude() {
196
- return this.arena.get_value_length(this.index) > 0;
214
+ if (this.type === AT_RULE) {
215
+ return this.arena.get_value_length(this.index) > 0;
216
+ }
217
+ if (this.type === STYLE_RULE) {
218
+ return this.first_child !== null;
219
+ }
220
+ return false;
197
221
  }
198
222
  /** Check if this rule has a block { } */
199
223
  get has_block() {
@@ -228,7 +252,7 @@ class CSSNode {
228
252
  }
229
253
  /** Check if this block is empty (no declarations or rules, only comments allowed) */
230
254
  get is_empty() {
231
- if (this.type !== BLOCK) return false;
255
+ if (this.type !== BLOCK) return void 0;
232
256
  let child = this.first_child;
233
257
  while (child) {
234
258
  if (child.type !== COMMENT) {
@@ -238,17 +262,6 @@ class CSSNode {
238
262
  }
239
263
  return true;
240
264
  }
241
- // --- Value Node Access (for declarations) ---
242
- /** Get array of parsed value nodes (for declarations only) */
243
- get values() {
244
- let result = [];
245
- let child = this.first_child;
246
- while (child) {
247
- result.push(child);
248
- child = child.next_sibling;
249
- }
250
- return result;
251
- }
252
265
  /** Get start line number */
253
266
  get line() {
254
267
  return this.arena.get_start_line(this.index);
@@ -324,17 +337,17 @@ class CSSNode {
324
337
  // --- An+B Expression Helpers (for NODE_SELECTOR_NTH) ---
325
338
  /** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
326
339
  get nth_a() {
327
- if (this.type !== NTH_SELECTOR) return null;
340
+ if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
328
341
  let len = this.arena.get_content_length(this.index);
329
- if (len === 0) return null;
342
+ if (len === 0) return void 0;
330
343
  let start = this.arena.get_content_start(this.index);
331
344
  return this.source.substring(start, start + len);
332
345
  }
333
346
  /** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
334
347
  get nth_b() {
335
- if (this.type !== NTH_SELECTOR) return null;
348
+ if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
336
349
  let len = this.arena.get_value_length(this.index);
337
- if (len === 0) return null;
350
+ if (len === 0) return void 0;
338
351
  let start = this.arena.get_value_start(this.index);
339
352
  let value = this.source.substring(start, start + len);
340
353
  let check_pos = start - 1;
@@ -356,14 +369,14 @@ class CSSNode {
356
369
  // --- Pseudo-Class Nth-Of Helpers (for NODE_SELECTOR_NTH_OF) ---
357
370
  /** Get the An+B formula node from :nth-child(2n+1 of .foo) */
358
371
  get nth() {
359
- if (this.type !== NTH_OF_SELECTOR) return null;
360
- return this.first_child;
372
+ if (this.type !== NTH_OF_SELECTOR) return void 0;
373
+ return this.first_child ?? void 0;
361
374
  }
362
375
  /** Get the selector list from :nth-child(2n+1 of .foo) */
363
376
  get selector() {
364
- if (this.type !== NTH_OF_SELECTOR) return null;
377
+ if (this.type !== NTH_OF_SELECTOR) return void 0;
365
378
  let first = this.first_child;
366
- return first ? first.next_sibling : null;
379
+ return first?.next_sibling ?? void 0;
367
380
  }
368
381
  // --- Pseudo-Class Selector List Helper ---
369
382
  /**
@@ -371,95 +384,16 @@ class CSSNode {
371
384
  * Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
372
385
  */
373
386
  get selector_list() {
374
- if (this.type !== PSEUDO_CLASS_SELECTOR) return null;
387
+ if (this.type !== PSEUDO_CLASS_SELECTOR) return void 0;
375
388
  let child = this.first_child;
376
- if (!child) return null;
389
+ if (!child) return void 0;
377
390
  if (child.type === SELECTOR_LIST) {
378
391
  return child;
379
392
  }
380
393
  if (child.type === NTH_OF_SELECTOR) {
381
394
  return child.selector;
382
395
  }
383
- return null;
384
- }
385
- // --- Compound Selector Helpers (for NODE_SELECTOR) ---
386
- /**
387
- * Iterator over first compound selector parts (zero allocation)
388
- * Yields parts before the first combinator
389
- */
390
- *compound_parts() {
391
- if (this.type !== SELECTOR) return;
392
- let child = this.first_child;
393
- while (child) {
394
- if (child.type === COMBINATOR) break;
395
- yield child;
396
- child = child.next_sibling;
397
- }
398
- }
399
- /**
400
- * Get first compound selector as array
401
- * Returns array of parts before first combinator
402
- */
403
- get first_compound() {
404
- if (this.type !== SELECTOR) return [];
405
- let result = [];
406
- let child = this.first_child;
407
- while (child) {
408
- if (child.type === COMBINATOR) break;
409
- result.push(child);
410
- child = child.next_sibling;
411
- }
412
- return result;
413
- }
414
- /**
415
- * Split selector into compound selectors
416
- * Returns array of compound arrays split by combinators
417
- */
418
- get all_compounds() {
419
- if (this.type !== SELECTOR) return [];
420
- let compounds = [];
421
- let current_compound = [];
422
- let child = this.first_child;
423
- while (child) {
424
- if (child.type === COMBINATOR) {
425
- if (current_compound.length > 0) {
426
- compounds.push(current_compound);
427
- current_compound = [];
428
- }
429
- } else {
430
- current_compound.push(child);
431
- }
432
- child = child.next_sibling;
433
- }
434
- if (current_compound.length > 0) {
435
- compounds.push(current_compound);
436
- }
437
- return compounds;
438
- }
439
- /** Check if selector is compound (no combinators) */
440
- get is_compound() {
441
- if (this.type !== SELECTOR) return false;
442
- let child = this.first_child;
443
- while (child) {
444
- if (child.type === COMBINATOR) return false;
445
- child = child.next_sibling;
446
- }
447
- return true;
448
- }
449
- /** Get text of first compound selector (no node allocation) */
450
- get first_compound_text() {
451
- if (this.type !== SELECTOR) return "";
452
- let start = -1;
453
- let end = -1;
454
- let child = this.first_child;
455
- while (child) {
456
- if (child.type === COMBINATOR) break;
457
- if (start === -1) start = child.start;
458
- end = child.start + child.length;
459
- child = child.next_sibling;
460
- }
461
- if (start === -1) return "";
462
- return this.source.substring(start, end);
396
+ return void 0;
463
397
  }
464
398
  // --- Node Cloning ---
465
399
  /**
@@ -484,9 +418,6 @@ class CSSNode {
484
418
  plain.value = this.value;
485
419
  if (this.unit) plain.unit = this.unit;
486
420
  }
487
- if (this.type === AT_RULE && this.prelude) {
488
- plain.prelude = this.prelude;
489
- }
490
421
  if (this.type === DECLARATION) {
491
422
  plain.is_important = this.is_important;
492
423
  plain.is_browserhack = this.is_browserhack;
package/dist/index.js CHANGED
@@ -7,6 +7,6 @@ export { tokenize } from './tokenize.js';
7
7
  export { BREAK, SKIP, traverse, walk } from './walk.js';
8
8
  export { is_custom, is_vendor_prefixed, str_equals, str_index_of, str_starts_with } from './string-utils.js';
9
9
  export { CSSNode, TYPE_NAMES } from './css-node.js';
10
- export { ATTRIBUTE_SELECTOR, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_NONE, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, AT_RULE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FEATURE_RANGE, FLAG_IMPORTANT, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PARENTHESIS, PRELUDE_OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, STRING, STYLESHEET, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, UNIVERSAL_SELECTOR, URL } from './arena.js';
10
+ export { ATTRIBUTE_SELECTOR, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_NONE, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, AT_RULE, AT_RULE_PRELUDE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FEATURE_RANGE, FLAG_IMPORTANT, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PARENTHESIS, PRELUDE_OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, STRING, STYLESHEET, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, UNIVERSAL_SELECTOR, URL, VALUE } from './arena.js';
11
11
  export { NODE_TYPES } from './constants.js';
12
12
  export { TOKEN_AT_KEYWORD, TOKEN_BAD_STRING, TOKEN_BAD_URL, TOKEN_CDC, TOKEN_CDO, TOKEN_COLON, TOKEN_COMMA, TOKEN_COMMENT, TOKEN_DELIM, TOKEN_DIMENSION, TOKEN_EOF, TOKEN_FUNCTION, TOKEN_HASH, TOKEN_IDENT, TOKEN_LEFT_BRACE, TOKEN_LEFT_BRACKET, TOKEN_LEFT_PAREN, TOKEN_NUMBER, TOKEN_PERCENTAGE, TOKEN_RIGHT_BRACE, TOKEN_RIGHT_BRACKET, TOKEN_RIGHT_PAREN, TOKEN_SEMICOLON, TOKEN_STRING, TOKEN_URL, TOKEN_WHITESPACE } from './token-types.js';
@@ -124,12 +124,16 @@ class DeclarationParser {
124
124
  this.arena.set_value_start_delta(declaration, trimmed[0] - prop_start);
125
125
  this.arena.set_value_length(declaration, trimmed[1] - trimmed[0]);
126
126
  if (this.value_parser) {
127
- let valueNodes = this.value_parser.parse_value(value_start, trimmed[1], value_start_line, value_start_column);
128
- this.arena.append_children(declaration, valueNodes);
127
+ let valueNode = this.value_parser.parse_value(value_start, trimmed[1], value_start_line, value_start_column);
128
+ this.arena.append_children(declaration, [valueNode]);
129
129
  }
130
130
  } else {
131
131
  this.arena.set_value_start_delta(declaration, value_start - prop_start);
132
132
  this.arena.set_value_length(declaration, 0);
133
+ if (this.value_parser) {
134
+ let valueNode = this.value_parser.parse_value(value_start, value_start, value_start_line, value_start_column);
135
+ this.arena.append_children(declaration, [valueNode]);
136
+ }
133
137
  }
134
138
  if (has_important) {
135
139
  this.arena.set_flag(declaration, FLAG_IMPORTANT);
@@ -34,9 +34,13 @@ class SelectorParser {
34
34
  let list_column = this.lexer.column;
35
35
  while (this.lexer.pos < this.selector_end) {
36
36
  let selector_start = this.lexer.pos;
37
+ let selector_line = this.lexer.line;
38
+ let selector_column = this.lexer.column;
37
39
  let complex_selector = this.parse_complex_selector(allow_relative);
38
40
  if (complex_selector !== null) {
39
- let selector_wrapper = this.create_node(SELECTOR, selector_start, this.lexer.pos);
41
+ let selector_wrapper = this.arena.create_node(SELECTOR, selector_start, this.lexer.pos - selector_start, selector_line, selector_column);
42
+ this.arena.set_content_start_delta(selector_wrapper, 0);
43
+ this.arena.set_content_length(selector_wrapper, this.lexer.pos - selector_start);
40
44
  let last_component = complex_selector;
41
45
  let next_sibling = this.arena.get_next_sibling(last_component);
42
46
  while (next_sibling !== 0) {
@@ -573,9 +577,11 @@ class SelectorParser {
573
577
  this.arena.set_content_length(node, end - start);
574
578
  return node;
575
579
  }
576
- // Helper to skip whitespace
580
+ // Helper to skip whitespace and update line/column
577
581
  skip_whitespace() {
578
- this.lexer.pos = skip_whitespace_forward(this.source, this.lexer.pos, this.selector_end);
582
+ while (this.lexer.pos < this.selector_end && is_whitespace(this.source.charCodeAt(this.lexer.pos))) {
583
+ this.lexer.advance();
584
+ }
579
585
  }
580
586
  }
581
587
  function parse_selector(source) {
@@ -1,7 +1,7 @@
1
1
  import { CSSNode } from './css-node';
2
2
  /**
3
- * Parse a CSS declaration value string and return an array of value AST nodes
3
+ * Parse a CSS declaration value string and return a VALUE node
4
4
  * @param value_string - The CSS value to parse (e.g., "1px solid red")
5
- * @returns An array of CSSNode objects representing the parsed value
5
+ * @returns A CSSNode VALUE wrapper containing the parsed value tokens as children
6
6
  */
7
- export declare function parse_value(value_string: string): CSSNode[];
7
+ export declare function parse_value(value_string: string): CSSNode;
@@ -1,5 +1,5 @@
1
1
  import { Lexer } from './tokenize.js';
2
- import { CSSDataArena, OPERATOR, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, URL, FUNCTION, PARENTHESIS } from './arena.js';
2
+ import { CSSDataArena, VALUE, OPERATOR, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, URL, FUNCTION, PARENTHESIS } from './arena.js';
3
3
  import { TOKEN_EOF, TOKEN_LEFT_PAREN, TOKEN_COMMA, TOKEN_DELIM, TOKEN_FUNCTION, TOKEN_HASH, TOKEN_STRING, TOKEN_DIMENSION, TOKEN_PERCENTAGE, TOKEN_NUMBER, TOKEN_IDENT, TOKEN_RIGHT_PAREN } from './token-types.js';
4
4
  import { is_whitespace, CHAR_PLUS, CHAR_MINUS_HYPHEN, CHAR_ASTERISK, CHAR_FORWARD_SLASH, str_equals } from './string-utils.js';
5
5
  import { CSSNode } from './css-node.js';
@@ -15,14 +15,24 @@ class ValueParser {
15
15
  this.lexer = new Lexer(source, false);
16
16
  this.value_end = 0;
17
17
  }
18
- // Parse a declaration value range into value nodes (standalone use)
19
- // Returns array of value node indices
18
+ // Parse a declaration value range into a VALUE wrapper node
19
+ // Returns single VALUE node index
20
20
  parse_value(start, end, start_line, start_column) {
21
21
  this.value_end = end;
22
22
  this.lexer.pos = start;
23
23
  this.lexer.line = start_line;
24
24
  this.lexer.column = start_column;
25
- return this.parse_value_tokens();
25
+ let value_nodes = this.parse_value_tokens();
26
+ if (value_nodes.length === 0) {
27
+ let value_node2 = this.arena.create_node(VALUE, start, 0, start_line, start_column);
28
+ return value_node2;
29
+ }
30
+ let first_node_start = this.arena.get_start_offset(value_nodes[0]);
31
+ let last_node_index = value_nodes[value_nodes.length - 1];
32
+ let last_node_end = this.arena.get_start_offset(last_node_index) + this.arena.get_length(last_node_index);
33
+ let value_node = this.arena.create_node(VALUE, first_node_start, last_node_end - first_node_start, start_line, start_column);
34
+ this.arena.append_children(value_node, value_nodes);
35
+ return value_node;
26
36
  }
27
37
  // Core token parsing logic
28
38
  parse_value_tokens() {
@@ -210,8 +220,8 @@ class ValueParser {
210
220
  function parse_value(value_string) {
211
221
  const arena = new CSSDataArena(CSSDataArena.capacity_for_source(value_string.length));
212
222
  const value_parser = new ValueParser(arena, value_string);
213
- const node_indices = value_parser.parse_value(0, value_string.length, 1, 1);
214
- return node_indices.map((index) => new CSSNode(arena, value_string, index));
223
+ const value_node_index = value_parser.parse_value(0, value_string.length, 1, 1);
224
+ return new CSSNode(arena, value_string, value_node_index);
215
225
  }
216
226
 
217
227
  export { ValueParser, parse_value };
package/dist/parse.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Lexer } from './tokenize.js';
2
- import { CSSDataArena, STYLESHEET, STYLE_RULE, FLAG_HAS_BLOCK, BLOCK, FLAG_HAS_DECLARATIONS, SELECTOR_LIST, AT_RULE } from './arena.js';
2
+ import { CSSDataArena, STYLESHEET, STYLE_RULE, FLAG_HAS_BLOCK, BLOCK, FLAG_HAS_DECLARATIONS, SELECTOR_LIST, AT_RULE, AT_RULE_PRELUDE } from './arena.js';
3
3
  import { CSSNode } from './css-node.js';
4
4
  import { SelectorParser } from './parse-selector.js';
5
5
  import { AtRulePreludeParser } from './parse-atrule-prelude.js';
@@ -167,13 +167,13 @@ class Parser {
167
167
  this.next_token();
168
168
  }
169
169
  if (this.parse_selectors_enabled && this.selector_parser) {
170
- let selectorNode = this.selector_parser.parse_selector(selector_start, last_end, selector_line, selector_column);
171
- if (selectorNode !== null) {
172
- return selectorNode;
170
+ let selector = this.selector_parser.parse_selector(selector_start, last_end, selector_line, selector_column);
171
+ if (selector !== null) {
172
+ return selector;
173
173
  }
174
174
  }
175
- let selector = this.arena.create_node(SELECTOR_LIST, selector_start, last_end - selector_start, selector_line, selector_column);
176
- return selector;
175
+ let selector_list = this.arena.create_node(SELECTOR_LIST, selector_start, last_end - selector_start, selector_line, selector_column);
176
+ return selector_list;
177
177
  }
178
178
  // Parse a declaration: property: value;
179
179
  parse_declaration() {
@@ -221,12 +221,16 @@ class Parser {
221
221
  this.next_token();
222
222
  }
223
223
  let trimmed = trim_boundaries(this.source, prelude_start, prelude_end);
224
- let prelude_nodes = [];
224
+ let prelude_wrapper = null;
225
225
  if (trimmed) {
226
226
  this.arena.set_value_start_delta(at_rule, trimmed[0] - at_rule_start);
227
227
  this.arena.set_value_length(at_rule, trimmed[1] - trimmed[0]);
228
228
  if (this.prelude_parser) {
229
- prelude_nodes = this.prelude_parser.parse_prelude(at_rule_name, trimmed[0], trimmed[1], at_rule_line, at_rule_column);
229
+ let prelude_nodes = this.prelude_parser.parse_prelude(at_rule_name, trimmed[0], trimmed[1], at_rule_line, at_rule_column);
230
+ if (prelude_nodes.length > 0) {
231
+ prelude_wrapper = this.arena.create_node(AT_RULE_PRELUDE, trimmed[0], trimmed[1] - trimmed[0], at_rule_line, at_rule_column);
232
+ this.arena.append_children(prelude_wrapper, prelude_nodes);
233
+ }
230
234
  }
231
235
  }
232
236
  let last_end = this.lexer.token_end;
@@ -304,13 +308,26 @@ class Parser {
304
308
  this.arena.set_length(block_node, last_end - block_start);
305
309
  }
306
310
  this.arena.append_children(block_node, block_children);
307
- prelude_nodes.push(block_node);
311
+ let at_rule_children = [];
312
+ if (prelude_wrapper !== null) {
313
+ at_rule_children.push(prelude_wrapper);
314
+ }
315
+ at_rule_children.push(block_node);
316
+ this.arena.set_length(at_rule, last_end - at_rule_start);
317
+ this.arena.append_children(at_rule, at_rule_children);
308
318
  } else if (this.peek_type() === TOKEN_SEMICOLON) {
309
319
  last_end = this.lexer.token_end;
310
320
  this.next_token();
321
+ this.arena.set_length(at_rule, last_end - at_rule_start);
322
+ if (prelude_wrapper !== null) {
323
+ this.arena.append_children(at_rule, [prelude_wrapper]);
324
+ }
325
+ } else {
326
+ this.arena.set_length(at_rule, last_end - at_rule_start);
327
+ if (prelude_wrapper !== null) {
328
+ this.arena.append_children(at_rule, [prelude_wrapper]);
329
+ }
311
330
  }
312
- this.arena.set_length(at_rule, last_end - at_rule_start);
313
- this.arena.append_children(at_rule, prelude_nodes);
314
331
  return at_rule;
315
332
  }
316
333
  // Determine if an at-rule contains declarations or nested rules
package/dist/tokenize.js CHANGED
@@ -1,5 +1,5 @@
1
- import { is_whitespace, is_newline, is_digit, is_ident_start, is_hex_digit, is_ident_char } from './char-types.js';
2
- import { TOKEN_EOF, TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE, TOKEN_COLON, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_CDO, TOKEN_CDC, TOKEN_DELIM, TOKEN_WHITESPACE, TOKEN_COMMENT, TOKEN_STRING, TOKEN_BAD_STRING, TOKEN_PERCENTAGE, TOKEN_DIMENSION, TOKEN_NUMBER, TOKEN_FUNCTION, TOKEN_IDENT, TOKEN_AT_KEYWORD, TOKEN_HASH } from './token-types.js';
1
+ import { char_types, CHAR_WHITESPACE, CHAR_NEWLINE, CHAR_DIGIT, is_ident_start, is_newline, is_hex_digit, is_whitespace, is_ident_char } from './char-types.js';
2
+ import { TOKEN_EOF, TOKEN_RIGHT_PAREN, TOKEN_LEFT_PAREN, TOKEN_RIGHT_BRACKET, TOKEN_LEFT_BRACKET, TOKEN_COMMA, TOKEN_SEMICOLON, TOKEN_COLON, TOKEN_RIGHT_BRACE, TOKEN_LEFT_BRACE, TOKEN_CDO, TOKEN_CDC, TOKEN_DELIM, TOKEN_WHITESPACE, TOKEN_COMMENT, TOKEN_STRING, TOKEN_BAD_STRING, TOKEN_PERCENTAGE, TOKEN_DIMENSION, TOKEN_NUMBER, TOKEN_FUNCTION, TOKEN_IDENT, TOKEN_AT_KEYWORD, TOKEN_HASH } from './token-types.js';
3
3
 
4
4
  const CHAR_LEFT_BRACE = 123;
5
5
  const CHAR_RIGHT_BRACE = 125;
@@ -57,7 +57,7 @@ class Lexer {
57
57
  if (skip_whitespace) {
58
58
  while (this.pos < this.source.length) {
59
59
  let ch2 = this.source.charCodeAt(this.pos);
60
- if (!is_whitespace(ch2) && !is_newline(ch2)) break;
60
+ if (ch2 >= 128 || (char_types[ch2] & (CHAR_WHITESPACE | CHAR_NEWLINE)) === 0) break;
61
61
  this.advance();
62
62
  }
63
63
  }
@@ -68,43 +68,36 @@ class Lexer {
68
68
  let start = this.pos;
69
69
  let start_line = this.line;
70
70
  let start_column = this.column;
71
- if (ch === CHAR_LEFT_BRACE) {
72
- this.advance();
73
- return this.make_token(TOKEN_LEFT_BRACE, start, this.pos, start_line, start_column);
74
- }
75
- if (ch === CHAR_RIGHT_BRACE) {
76
- this.advance();
77
- return this.make_token(TOKEN_RIGHT_BRACE, start, this.pos, start_line, start_column);
78
- }
79
- if (ch === CHAR_COLON) {
80
- this.advance();
81
- return this.make_token(TOKEN_COLON, start, this.pos, start_line, start_column);
82
- }
83
- if (ch === CHAR_SEMICOLON) {
84
- this.advance();
85
- return this.make_token(TOKEN_SEMICOLON, start, this.pos, start_line, start_column);
86
- }
87
- if (ch === CHAR_COMMA) {
88
- this.advance();
89
- return this.make_token(TOKEN_COMMA, start, this.pos, start_line, start_column);
90
- }
91
- if (ch === CHAR_LEFT_BRACKET) {
92
- this.advance();
93
- return this.make_token(TOKEN_LEFT_BRACKET, start, this.pos, start_line, start_column);
94
- }
95
- if (ch === CHAR_RIGHT_BRACKET) {
96
- this.advance();
97
- return this.make_token(TOKEN_RIGHT_BRACKET, start, this.pos, start_line, start_column);
98
- }
99
- if (ch === CHAR_LEFT_PAREN) {
100
- this.advance();
101
- return this.make_token(TOKEN_LEFT_PAREN, start, this.pos, start_line, start_column);
102
- }
103
- if (ch === CHAR_RIGHT_PAREN) {
104
- this.advance();
105
- return this.make_token(TOKEN_RIGHT_PAREN, start, this.pos, start_line, start_column);
71
+ switch (ch) {
72
+ case CHAR_LEFT_BRACE:
73
+ this.advance();
74
+ return this.make_token(TOKEN_LEFT_BRACE, start, this.pos, start_line, start_column);
75
+ case CHAR_RIGHT_BRACE:
76
+ this.advance();
77
+ return this.make_token(TOKEN_RIGHT_BRACE, start, this.pos, start_line, start_column);
78
+ case CHAR_COLON:
79
+ this.advance();
80
+ return this.make_token(TOKEN_COLON, start, this.pos, start_line, start_column);
81
+ case CHAR_SEMICOLON:
82
+ this.advance();
83
+ return this.make_token(TOKEN_SEMICOLON, start, this.pos, start_line, start_column);
84
+ case CHAR_COMMA:
85
+ this.advance();
86
+ return this.make_token(TOKEN_COMMA, start, this.pos, start_line, start_column);
87
+ case CHAR_LEFT_BRACKET:
88
+ this.advance();
89
+ return this.make_token(TOKEN_LEFT_BRACKET, start, this.pos, start_line, start_column);
90
+ case CHAR_RIGHT_BRACKET:
91
+ this.advance();
92
+ return this.make_token(TOKEN_RIGHT_BRACKET, start, this.pos, start_line, start_column);
93
+ case CHAR_LEFT_PAREN:
94
+ this.advance();
95
+ return this.make_token(TOKEN_LEFT_PAREN, start, this.pos, start_line, start_column);
96
+ case CHAR_RIGHT_PAREN:
97
+ this.advance();
98
+ return this.make_token(TOKEN_RIGHT_PAREN, start, this.pos, start_line, start_column);
106
99
  }
107
- if (is_whitespace(ch) || is_newline(ch)) {
100
+ if (ch < 128 && (char_types[ch] & (CHAR_WHITESPACE | CHAR_NEWLINE)) !== 0) {
108
101
  return this.consume_whitespace(start_line, start_column);
109
102
  }
110
103
  if (ch === CHAR_FORWARD_SLASH && this.peek() === CHAR_ASTERISK) {
@@ -112,7 +105,7 @@ class Lexer {
112
105
  this.advance(2);
113
106
  while (this.pos < this.source.length - 1) {
114
107
  let ch2 = this.source.charCodeAt(this.pos);
115
- if (ch2 === CHAR_ASTERISK && this.source.charCodeAt(this.pos + 1) === CHAR_FORWARD_SLASH) {
108
+ if (ch2 === CHAR_ASTERISK && this.peek() === CHAR_FORWARD_SLASH) {
116
109
  this.advance(2);
117
110
  break;
118
111
  }
@@ -125,20 +118,23 @@ class Lexer {
125
118
  if (ch === CHAR_DOUBLE_QUOTE || ch === CHAR_SINGLE_QUOTE) {
126
119
  return this.consume_string(ch, start_line, start_column);
127
120
  }
128
- if (is_digit(ch)) {
121
+ if (ch < 128 && (char_types[ch] & CHAR_DIGIT) !== 0) {
129
122
  return this.consume_number(start_line, start_column);
130
123
  }
131
- if (ch === CHAR_DOT && is_digit(this.peek())) {
132
- return this.consume_number(start_line, start_column);
124
+ if (ch === CHAR_DOT) {
125
+ let next = this.peek();
126
+ if (next < 128 && (char_types[next] & CHAR_DIGIT) !== 0) {
127
+ return this.consume_number(start_line, start_column);
128
+ }
133
129
  }
134
130
  if (ch === CHAR_LESS_THAN && this.pos + 3 < this.source.length) {
135
- if (this.source.charCodeAt(this.pos + 1) === CHAR_EXCLAMATION && this.source.charCodeAt(this.pos + 2) === CHAR_HYPHEN && this.source.charCodeAt(this.pos + 3) === CHAR_HYPHEN) {
131
+ if (this.peek() === CHAR_EXCLAMATION && this.peek(2) === CHAR_HYPHEN && this.peek(3) === CHAR_HYPHEN) {
136
132
  this.advance(4);
137
133
  return this.make_token(TOKEN_CDO, start, this.pos, start_line, start_column);
138
134
  }
139
135
  }
140
136
  if (ch === CHAR_HYPHEN && this.pos + 2 < this.source.length) {
141
- if (this.source.charCodeAt(this.pos + 1) === CHAR_HYPHEN && this.source.charCodeAt(this.pos + 2) === CHAR_GREATER_THAN) {
137
+ if (this.peek() === CHAR_HYPHEN && this.peek(2) === CHAR_GREATER_THAN) {
142
138
  this.advance(3);
143
139
  return this.make_token(TOKEN_CDC, start, this.pos, start_line, start_column);
144
140
  }
@@ -166,9 +162,16 @@ class Lexer {
166
162
  }
167
163
  if (ch === CHAR_HYPHEN || ch === CHAR_PLUS) {
168
164
  let next = this.peek();
169
- if (is_digit(next) || next === CHAR_DOT && is_digit(this.peek(2))) {
165
+ let is_next_digit = next < 128 && (char_types[next] & CHAR_DIGIT) !== 0;
166
+ if (is_next_digit) {
170
167
  return this.consume_number(start_line, start_column);
171
168
  }
169
+ if (next === CHAR_DOT) {
170
+ let next2 = this.peek(2);
171
+ if (next2 < 128 && (char_types[next2] & CHAR_DIGIT) !== 0) {
172
+ return this.consume_number(start_line, start_column);
173
+ }
174
+ }
172
175
  }
173
176
  this.advance();
174
177
  return this.make_token(TOKEN_DELIM, start, this.pos, start_line, start_column);
@@ -177,7 +180,7 @@ class Lexer {
177
180
  let start = this.pos;
178
181
  while (this.pos < this.source.length) {
179
182
  let ch = this.source.charCodeAt(this.pos);
180
- if (!is_whitespace(ch) && !is_newline(ch)) break;
183
+ if (ch >= 128 || (char_types[ch] & (CHAR_WHITESPACE | CHAR_NEWLINE)) === 0) break;
181
184
  this.advance();
182
185
  }
183
186
  return this.make_token(TOKEN_WHITESPACE, start, this.pos, start_line, start_column);
@@ -187,7 +190,7 @@ class Lexer {
187
190
  this.advance(2);
188
191
  while (this.pos < this.source.length - 1) {
189
192
  let ch = this.source.charCodeAt(this.pos);
190
- if (ch === CHAR_ASTERISK && this.source.charCodeAt(this.pos + 1) === CHAR_FORWARD_SLASH) {
193
+ if (ch === CHAR_ASTERISK && this.peek() === CHAR_FORWARD_SLASH) {
191
194
  this.advance(2);
192
195
  break;
193
196
  }
@@ -246,20 +249,30 @@ class Lexer {
246
249
  if (ch === CHAR_PLUS || ch === CHAR_HYPHEN) {
247
250
  this.advance();
248
251
  }
249
- while (this.pos < this.source.length && is_digit(this.source.charCodeAt(this.pos))) {
252
+ while (this.pos < this.source.length) {
253
+ let ch2 = this.source.charCodeAt(this.pos);
254
+ if (ch2 >= 128 || (char_types[ch2] & CHAR_DIGIT) === 0) break;
250
255
  this.advance();
251
256
  }
252
- if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_DOT && this.pos + 1 < this.source.length && is_digit(this.source.charCodeAt(this.pos + 1))) {
253
- this.advance();
254
- while (this.pos < this.source.length && is_digit(this.source.charCodeAt(this.pos))) {
257
+ if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_DOT && this.pos + 1 < this.source.length) {
258
+ let next = this.peek();
259
+ if (next < 128 && (char_types[next] & CHAR_DIGIT) !== 0) {
255
260
  this.advance();
261
+ while (this.pos < this.source.length) {
262
+ let ch2 = this.source.charCodeAt(this.pos);
263
+ if (ch2 >= 128 || (char_types[ch2] & CHAR_DIGIT) === 0) break;
264
+ this.advance();
265
+ }
256
266
  }
257
267
  }
258
268
  if (this.pos < this.source.length) {
259
269
  let ch2 = this.source.charCodeAt(this.pos);
260
270
  if (ch2 === CHAR_LOWERCASE_E || ch2 === CHAR_UPPERCASE_E) {
261
271
  let next = this.peek();
262
- if (is_digit(next) || (next === CHAR_PLUS || next === CHAR_HYPHEN) && is_digit(this.peek(2))) {
272
+ let is_next_digit = next < 128 && (char_types[next] & CHAR_DIGIT) !== 0;
273
+ let next2 = this.peek(2);
274
+ let is_next2_digit = next2 < 128 && (char_types[next2] & CHAR_DIGIT) !== 0;
275
+ if (is_next_digit || (next === CHAR_PLUS || next === CHAR_HYPHEN) && is_next2_digit) {
263
276
  this.advance();
264
277
  if (this.pos < this.source.length) {
265
278
  let sign = this.source.charCodeAt(this.pos);
@@ -267,7 +280,9 @@ class Lexer {
267
280
  this.advance();
268
281
  }
269
282
  }
270
- while (this.pos < this.source.length && is_digit(this.source.charCodeAt(this.pos))) {
283
+ while (this.pos < this.source.length) {
284
+ let ch3 = this.source.charCodeAt(this.pos);
285
+ if (ch3 >= 128 || (char_types[ch3] & CHAR_DIGIT) === 0) break;
271
286
  this.advance();
272
287
  }
273
288
  }
@@ -294,7 +309,7 @@ class Lexer {
294
309
  let ch = this.source.charCodeAt(this.pos);
295
310
  if (ch === CHAR_BACKSLASH) {
296
311
  if (this.pos + 1 >= this.source.length) break;
297
- let next = this.source.charCodeAt(this.pos + 1);
312
+ let next = this.peek();
298
313
  if (is_newline(next)) break;
299
314
  this.advance();
300
315
  if (is_hex_digit(next)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-parser",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
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",
@@ -60,7 +60,8 @@
60
60
  "benchmark:memory": "npm run build && node --expose-gc benchmark/memory.ts",
61
61
  "lint": "oxlint --config .oxlintrc.json",
62
62
  "lint-package": "publint",
63
- "check": "tsc --noEmit"
63
+ "check": "tsc --noEmit",
64
+ "precommit": "npm run test -- --run; npm run lint; npm run check"
64
65
  },
65
66
  "keywords": [
66
67
  "css",