@projectwallace/css-parser 0.10.0 → 0.11.1

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
@@ -36,6 +36,7 @@ export declare const SUPPORTS_QUERY = 36;
36
36
  export declare const LAYER_NAME = 37;
37
37
  export declare const PRELUDE_OPERATOR = 38;
38
38
  export declare const FEATURE_RANGE = 39;
39
+ export declare const AT_RULE_PRELUDE = 40;
39
40
  export declare const FLAG_IMPORTANT: number;
40
41
  export declare const FLAG_HAS_ERROR: number;
41
42
  export declare const FLAG_LENGTH_OVERFLOW: number;
package/dist/arena.js CHANGED
@@ -37,6 +37,7 @@ const SUPPORTS_QUERY = 36;
37
37
  const LAYER_NAME = 37;
38
38
  const PRELUDE_OPERATOR = 38;
39
39
  const FEATURE_RANGE = 39;
40
+ const AT_RULE_PRELUDE = 40;
40
41
  const FLAG_IMPORTANT = 1 << 0;
41
42
  const FLAG_HAS_ERROR = 1 << 1;
42
43
  const FLAG_LENGTH_OVERFLOW = 1 << 2;
@@ -284,4 +285,4 @@ class CSSDataArena {
284
285
  }
285
286
  }
286
287
 
287
- 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, VALUE };
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, 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, 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, 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;
@@ -39,4 +39,5 @@ export declare const NODE_TYPES: {
39
39
  readonly LAYER_NAME: 37;
40
40
  readonly PRELUDE_OPERATOR: 38;
41
41
  readonly FEATURE_RANGE: 39;
42
+ readonly AT_RULE_PRELUDE: 40;
42
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, VALUE, 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 = {
@@ -43,7 +43,8 @@ const NODE_TYPES = {
43
43
  SUPPORTS_QUERY,
44
44
  LAYER_NAME,
45
45
  PRELUDE_OPERATOR,
46
- FEATURE_RANGE
46
+ FEATURE_RANGE,
47
+ AT_RULE_PRELUDE
47
48
  };
48
49
 
49
- 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, VALUE };
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, 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 } 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";
@@ -39,9 +39,10 @@ export declare const TYPE_NAMES: {
39
39
  readonly 37: "Layer";
40
40
  readonly 38: "Operator";
41
41
  readonly 39: "MediaFeatureRange";
42
+ readonly 40: "AtrulePrelude";
42
43
  };
43
44
  export type TypeName = (typeof TYPE_NAMES)[keyof typeof TYPE_NAMES] | 'unknown';
44
- 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;
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;
45
46
  export interface CloneOptions {
46
47
  /**
47
48
  * Recursively clone all children
@@ -63,7 +64,7 @@ export type PlainCSSNode = {
63
64
  property?: string;
64
65
  value?: string | number | null;
65
66
  unit?: string;
66
- prelude?: string;
67
+ prelude?: PlainCSSNode | null;
67
68
  is_important?: boolean;
68
69
  is_vendor_prefixed?: boolean;
69
70
  is_browserhack?: boolean;
@@ -107,31 +108,33 @@ export declare class CSSNode {
107
108
  /** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
108
109
  get value_as_number(): number | null;
109
110
  /**
110
- * Get the prelude text (for at-rules: "(min-width: 768px)" in "@media (min-width: 768px)")
111
- * 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
112
115
  */
113
- get prelude(): string | null;
116
+ get prelude(): CSSNode | null | undefined;
114
117
  /**
115
118
  * Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
116
119
  * Returns one of the ATTR_OPERATOR_* constants
117
120
  */
118
- get attr_operator(): number;
121
+ get attr_operator(): number | undefined;
119
122
  /**
120
123
  * Get the attribute flags (for attribute selectors: i, s)
121
124
  * Returns one of the ATTR_FLAG_* constants
122
125
  */
123
- get attr_flags(): number;
126
+ get attr_flags(): number | undefined;
124
127
  /** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
125
- get unit(): string | null;
128
+ get unit(): string | undefined;
126
129
  /** Check if this declaration has !important */
127
- get is_important(): boolean | null;
130
+ get is_important(): boolean | undefined;
128
131
  /** Check if this declaration has a browser hack prefix */
129
- get is_browserhack(): boolean | null;
132
+ get is_browserhack(): boolean | undefined;
130
133
  /** Check if this has a vendor prefix (computed on-demand) */
131
134
  get is_vendor_prefixed(): boolean;
132
135
  /** Check if this node has an error */
133
136
  get has_error(): boolean;
134
- /** Check if this at-rule has a prelude */
137
+ /** Check if this node has a prelude (at-rules and style rules) */
135
138
  get has_prelude(): boolean;
136
139
  /** Check if this rule has a block { } */
137
140
  get has_block(): boolean;
@@ -140,7 +143,7 @@ export declare class CSSNode {
140
143
  /** Get the block node (for style rules and at-rules with blocks) */
141
144
  get block(): CSSNode | null;
142
145
  /** Check if this block is empty (no declarations or rules, only comments allowed) */
143
- get is_empty(): boolean;
146
+ get is_empty(): boolean | undefined;
144
147
  /** Get start line number */
145
148
  get line(): number;
146
149
  /** Get start column number */
@@ -171,37 +174,18 @@ export declare class CSSNode {
171
174
  /** Make CSSNode iterable over its children */
172
175
  [Symbol.iterator](): Iterator<CSSNode>;
173
176
  /** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
174
- get nth_a(): string | null;
177
+ get nth_a(): string | undefined;
175
178
  /** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
176
- get nth_b(): string | null;
179
+ get nth_b(): string | undefined;
177
180
  /** Get the An+B formula node from :nth-child(2n+1 of .foo) */
178
- get nth(): CSSNode | null;
181
+ get nth(): CSSNode | undefined;
179
182
  /** Get the selector list from :nth-child(2n+1 of .foo) */
180
- get selector(): CSSNode | null;
183
+ get selector(): CSSNode | undefined;
181
184
  /**
182
185
  * Get selector list from pseudo-class functions
183
186
  * Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
184
187
  */
185
- get selector_list(): CSSNode | null;
186
- /**
187
- * Iterator over first compound selector parts (zero allocation)
188
- * Yields parts before the first combinator
189
- */
190
- compound_parts(): IterableIterator<CSSNode>;
191
- /**
192
- * Get first compound selector as array
193
- * Returns array of parts before first combinator
194
- */
195
- get first_compound(): CSSNode[];
196
- /**
197
- * Split selector into compound selectors
198
- * Returns array of compound arrays split by combinators
199
- */
200
- get all_compounds(): CSSNode[][];
201
- /** Check if selector is compound (no combinators) */
202
- get is_compound(): boolean;
203
- /** Get text of first compound selector (no node allocation) */
204
- get first_compound_text(): string;
188
+ get selector_list(): CSSNode | undefined;
205
189
  /**
206
190
  * Clone this node as a mutable plain JavaScript object with children as arrays.
207
191
  * See API.md for examples.
package/dist/css-node.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DECLARATION, DIMENSION, NUMBER, URL, STRING, 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, VALUE, 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
 
@@ -40,7 +40,8 @@ const TYPE_NAMES = {
40
40
  [SUPPORTS_QUERY]: "SupportsQuery",
41
41
  [LAYER_NAME]: "Layer",
42
42
  [PRELUDE_OPERATOR]: "Operator",
43
- [FEATURE_RANGE]: "MediaFeatureRange"
43
+ [FEATURE_RANGE]: "MediaFeatureRange",
44
+ [AT_RULE_PRELUDE]: "AtrulePrelude"
44
45
  };
45
46
  class CSSNode {
46
47
  arena;
@@ -137,18 +138,30 @@ class CSSNode {
137
138
  return null;
138
139
  }
139
140
  /**
140
- * Get the prelude text (for at-rules: "(min-width: 768px)" in "@media (min-width: 768px)")
141
- * 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
142
145
  */
143
146
  get prelude() {
144
- let val = this.value;
145
- 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;
146
158
  }
147
159
  /**
148
160
  * Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
149
161
  * Returns one of the ATTR_OPERATOR_* constants
150
162
  */
151
163
  get attr_operator() {
164
+ if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
152
165
  return this.arena.get_attr_operator(this.index);
153
166
  }
154
167
  /**
@@ -156,21 +169,22 @@ class CSSNode {
156
169
  * Returns one of the ATTR_FLAG_* constants
157
170
  */
158
171
  get attr_flags() {
172
+ if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
159
173
  return this.arena.get_attr_flags(this.index);
160
174
  }
161
175
  /** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
162
176
  get unit() {
163
- if (this.type !== DIMENSION) return null;
177
+ if (this.type !== DIMENSION) return void 0;
164
178
  return parse_dimension(this.text).unit;
165
179
  }
166
180
  /** Check if this declaration has !important */
167
181
  get is_important() {
168
- if (this.type !== DECLARATION) return null;
182
+ if (this.type !== DECLARATION) return void 0;
169
183
  return this.arena.has_flag(this.index, FLAG_IMPORTANT);
170
184
  }
171
185
  /** Check if this declaration has a browser hack prefix */
172
186
  get is_browserhack() {
173
- if (this.type !== DECLARATION) return null;
187
+ if (this.type !== DECLARATION) return void 0;
174
188
  return this.arena.has_flag(this.index, FLAG_BROWSERHACK);
175
189
  }
176
190
  /** Check if this has a vendor prefix (computed on-demand) */
@@ -195,9 +209,15 @@ class CSSNode {
195
209
  get has_error() {
196
210
  return this.arena.has_flag(this.index, FLAG_HAS_ERROR);
197
211
  }
198
- /** Check if this at-rule has a prelude */
212
+ /** Check if this node has a prelude (at-rules and style rules) */
199
213
  get has_prelude() {
200
- 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;
201
221
  }
202
222
  /** Check if this rule has a block { } */
203
223
  get has_block() {
@@ -232,7 +252,7 @@ class CSSNode {
232
252
  }
233
253
  /** Check if this block is empty (no declarations or rules, only comments allowed) */
234
254
  get is_empty() {
235
- if (this.type !== BLOCK) return false;
255
+ if (this.type !== BLOCK) return void 0;
236
256
  let child = this.first_child;
237
257
  while (child) {
238
258
  if (child.type !== COMMENT) {
@@ -317,17 +337,17 @@ class CSSNode {
317
337
  // --- An+B Expression Helpers (for NODE_SELECTOR_NTH) ---
318
338
  /** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
319
339
  get nth_a() {
320
- if (this.type !== NTH_SELECTOR) return null;
340
+ if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
321
341
  let len = this.arena.get_content_length(this.index);
322
- if (len === 0) return null;
342
+ if (len === 0) return void 0;
323
343
  let start = this.arena.get_content_start(this.index);
324
344
  return this.source.substring(start, start + len);
325
345
  }
326
346
  /** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
327
347
  get nth_b() {
328
- if (this.type !== NTH_SELECTOR) return null;
348
+ if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
329
349
  let len = this.arena.get_value_length(this.index);
330
- if (len === 0) return null;
350
+ if (len === 0) return void 0;
331
351
  let start = this.arena.get_value_start(this.index);
332
352
  let value = this.source.substring(start, start + len);
333
353
  let check_pos = start - 1;
@@ -349,14 +369,14 @@ class CSSNode {
349
369
  // --- Pseudo-Class Nth-Of Helpers (for NODE_SELECTOR_NTH_OF) ---
350
370
  /** Get the An+B formula node from :nth-child(2n+1 of .foo) */
351
371
  get nth() {
352
- if (this.type !== NTH_OF_SELECTOR) return null;
353
- return this.first_child;
372
+ if (this.type !== NTH_OF_SELECTOR) return void 0;
373
+ return this.first_child ?? void 0;
354
374
  }
355
375
  /** Get the selector list from :nth-child(2n+1 of .foo) */
356
376
  get selector() {
357
- if (this.type !== NTH_OF_SELECTOR) return null;
377
+ if (this.type !== NTH_OF_SELECTOR) return void 0;
358
378
  let first = this.first_child;
359
- return first ? first.next_sibling : null;
379
+ return first?.next_sibling ?? void 0;
360
380
  }
361
381
  // --- Pseudo-Class Selector List Helper ---
362
382
  /**
@@ -364,95 +384,16 @@ class CSSNode {
364
384
  * Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
365
385
  */
366
386
  get selector_list() {
367
- if (this.type !== PSEUDO_CLASS_SELECTOR) return null;
387
+ if (this.type !== PSEUDO_CLASS_SELECTOR) return void 0;
368
388
  let child = this.first_child;
369
- if (!child) return null;
389
+ if (!child) return void 0;
370
390
  if (child.type === SELECTOR_LIST) {
371
391
  return child;
372
392
  }
373
393
  if (child.type === NTH_OF_SELECTOR) {
374
394
  return child.selector;
375
395
  }
376
- return null;
377
- }
378
- // --- Compound Selector Helpers (for NODE_SELECTOR) ---
379
- /**
380
- * Iterator over first compound selector parts (zero allocation)
381
- * Yields parts before the first combinator
382
- */
383
- *compound_parts() {
384
- if (this.type !== SELECTOR) return;
385
- let child = this.first_child;
386
- while (child) {
387
- if (child.type === COMBINATOR) break;
388
- yield child;
389
- child = child.next_sibling;
390
- }
391
- }
392
- /**
393
- * Get first compound selector as array
394
- * Returns array of parts before first combinator
395
- */
396
- get first_compound() {
397
- if (this.type !== SELECTOR) return [];
398
- let result = [];
399
- let child = this.first_child;
400
- while (child) {
401
- if (child.type === COMBINATOR) break;
402
- result.push(child);
403
- child = child.next_sibling;
404
- }
405
- return result;
406
- }
407
- /**
408
- * Split selector into compound selectors
409
- * Returns array of compound arrays split by combinators
410
- */
411
- get all_compounds() {
412
- if (this.type !== SELECTOR) return [];
413
- let compounds = [];
414
- let current_compound = [];
415
- let child = this.first_child;
416
- while (child) {
417
- if (child.type === COMBINATOR) {
418
- if (current_compound.length > 0) {
419
- compounds.push(current_compound);
420
- current_compound = [];
421
- }
422
- } else {
423
- current_compound.push(child);
424
- }
425
- child = child.next_sibling;
426
- }
427
- if (current_compound.length > 0) {
428
- compounds.push(current_compound);
429
- }
430
- return compounds;
431
- }
432
- /** Check if selector is compound (no combinators) */
433
- get is_compound() {
434
- if (this.type !== SELECTOR) return false;
435
- let child = this.first_child;
436
- while (child) {
437
- if (child.type === COMBINATOR) return false;
438
- child = child.next_sibling;
439
- }
440
- return true;
441
- }
442
- /** Get text of first compound selector (no node allocation) */
443
- get first_compound_text() {
444
- if (this.type !== SELECTOR) return "";
445
- let start = -1;
446
- let end = -1;
447
- let child = this.first_child;
448
- while (child) {
449
- if (child.type === COMBINATOR) break;
450
- if (start === -1) start = child.start;
451
- end = child.start + child.length;
452
- child = child.next_sibling;
453
- }
454
- if (start === -1) return "";
455
- return this.source.substring(start, end);
396
+ return void 0;
456
397
  }
457
398
  // --- Node Cloning ---
458
399
  /**
@@ -477,9 +418,6 @@ class CSSNode {
477
418
  plain.value = this.value;
478
419
  if (this.unit) plain.unit = this.unit;
479
420
  }
480
- if (this.type === AT_RULE && this.prelude) {
481
- plain.prelude = this.prelude;
482
- }
483
421
  if (this.type === DECLARATION) {
484
422
  plain.is_important = this.is_important;
485
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, VALUE } 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';
@@ -1,7 +1,7 @@
1
1
  import { Lexer } from './tokenize.js';
2
- import { CSSDataArena, PRELUDE_OPERATOR, MEDIA_TYPE, MEDIA_QUERY, MEDIA_FEATURE, FUNCTION, IDENTIFIER, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, URL, STRING, DIMENSION, NUMBER, FEATURE_RANGE } from './arena.js';
3
- import { TOKEN_COMMA, TOKEN_IDENT, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_FUNCTION, TOKEN_EOF, TOKEN_WHITESPACE, TOKEN_URL, TOKEN_STRING, TOKEN_DIMENSION, TOKEN_PERCENTAGE, TOKEN_NUMBER } from './token-types.js';
4
- import { str_equals, CHAR_LESS_THAN, CHAR_GREATER_THAN, CHAR_EQUALS, CHAR_COLON, is_whitespace } from './string-utils.js';
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
+ 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
+ import { strip_vendor_prefix, str_equals, CHAR_LESS_THAN, CHAR_GREATER_THAN, CHAR_EQUALS, CHAR_COLON, is_whitespace } from './string-utils.js';
5
5
  import { trim_boundaries, skip_whitespace_forward } from './parse-utils.js';
6
6
  import { CSSNode } from './css-node.js';
7
7
 
@@ -26,20 +26,23 @@ class AtRulePreludeParser {
26
26
  }
27
27
  // Dispatch to appropriate parser based on at-rule type
28
28
  parse_prelude_dispatch(at_rule_name) {
29
- if (str_equals("media", at_rule_name)) {
29
+ let normalized_name = strip_vendor_prefix(at_rule_name);
30
+ if (str_equals("media", normalized_name)) {
30
31
  return this.parse_media_query_list();
31
- } else if (str_equals("container", at_rule_name)) {
32
+ } else if (str_equals("container", normalized_name)) {
32
33
  return this.parse_container_query();
33
- } else if (str_equals("supports", at_rule_name)) {
34
+ } else if (str_equals("supports", normalized_name)) {
34
35
  return this.parse_supports_query();
35
- } else if (str_equals("layer", at_rule_name)) {
36
+ } else if (str_equals("layer", normalized_name)) {
36
37
  return this.parse_layer_names();
37
- } else if (str_equals("keyframes", at_rule_name)) {
38
+ } else if (str_equals("keyframes", normalized_name)) {
38
39
  return this.parse_identifier();
39
- } else if (str_equals("property", at_rule_name)) {
40
+ } else if (str_equals("property", normalized_name)) {
40
41
  return this.parse_identifier();
41
- } else if (str_equals("import", at_rule_name)) {
42
+ } else if (str_equals("import", normalized_name)) {
42
43
  return this.parse_import_prelude();
44
+ } else if (str_equals("charset", normalized_name)) {
45
+ return this.parse_charset_prelude();
43
46
  }
44
47
  return [];
45
48
  }
@@ -303,6 +306,15 @@ class AtRulePreludeParser {
303
306
  let ident = this.create_node(IDENTIFIER, this.lexer.token_start, this.lexer.token_end);
304
307
  return [ident];
305
308
  }
309
+ // Parse @charset prelude: "UTF-8"
310
+ parse_charset_prelude() {
311
+ this.skip_whitespace();
312
+ if (this.lexer.pos >= this.prelude_end) return [];
313
+ this.next_token();
314
+ if (this.lexer.token_type !== TOKEN_STRING) return [];
315
+ let str = this.create_node(STRING, this.lexer.token_start, this.lexer.token_end);
316
+ return [str];
317
+ }
306
318
  // Parse @import prelude: url() [layer] [supports()] [media-query-list]
307
319
  // @import url("styles.css") layer(base) supports(display: grid) screen and (min-width: 768px);
308
320
  parse_import_prelude() {
@@ -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) {
@@ -77,7 +81,7 @@ class SelectorParser {
77
81
  if (token_type === TOKEN_DELIM) {
78
82
  let ch = this.source.charCodeAt(this.lexer.token_start);
79
83
  if (ch === CHAR_GREATER_THAN || ch === CHAR_PLUS || ch === CHAR_TILDE) {
80
- let combinator = this.create_node(COMBINATOR, this.lexer.token_start, this.lexer.token_end);
84
+ let combinator = this.create_node_at(COMBINATOR, this.lexer.token_start, this.lexer.token_end, this.lexer.token_line, this.lexer.token_column);
81
85
  components.push(combinator);
82
86
  this.skip_whitespace();
83
87
  } else {
@@ -233,40 +237,48 @@ class SelectorParser {
233
237
  // Parse combinator (>, +, ~, or descendant space)
234
238
  try_parse_combinator() {
235
239
  let whitespace_start = this.lexer.pos;
240
+ let whitespace_start_line = this.lexer.line;
241
+ let whitespace_start_column = this.lexer.column;
236
242
  let has_whitespace = false;
237
243
  while (this.lexer.pos < this.selector_end) {
238
244
  let ch = this.source.charCodeAt(this.lexer.pos);
239
245
  if (ch === CHAR_SPACE || ch === CHAR_TAB || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
240
246
  has_whitespace = true;
241
- this.lexer.pos++;
247
+ this.lexer.advance();
242
248
  } else {
243
249
  break;
244
250
  }
245
251
  }
246
252
  if (this.lexer.pos >= this.selector_end) {
247
253
  this.lexer.pos = whitespace_start;
254
+ this.lexer.line = whitespace_start_line;
255
+ this.lexer.column = whitespace_start_column;
248
256
  return null;
249
257
  }
250
258
  this.lexer.next_token_fast(false);
251
259
  if (this.lexer.token_type === TOKEN_DELIM) {
252
260
  let ch = this.source.charCodeAt(this.lexer.token_start);
253
261
  if (is_combinator(ch)) {
254
- return this.create_node(COMBINATOR, this.lexer.token_start, this.lexer.token_end);
262
+ return this.create_node_at(COMBINATOR, this.lexer.token_start, this.lexer.token_end, this.lexer.token_line, this.lexer.token_column);
255
263
  }
256
264
  }
257
265
  if (has_whitespace) {
258
266
  this.lexer.pos = whitespace_start;
267
+ this.lexer.line = whitespace_start_line;
268
+ this.lexer.column = whitespace_start_column;
259
269
  while (this.lexer.pos < this.selector_end) {
260
270
  let ch = this.source.charCodeAt(this.lexer.pos);
261
271
  if (ch === CHAR_SPACE || ch === CHAR_TAB || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
262
- this.lexer.pos++;
272
+ this.lexer.advance();
263
273
  } else {
264
274
  break;
265
275
  }
266
276
  }
267
- return this.create_node(COMBINATOR, whitespace_start, this.lexer.pos);
277
+ return this.create_node_at(COMBINATOR, whitespace_start, this.lexer.pos, whitespace_start_line, whitespace_start_column);
268
278
  }
269
279
  this.lexer.pos = whitespace_start;
280
+ this.lexer.line = whitespace_start_line;
281
+ this.lexer.column = whitespace_start_column;
270
282
  return null;
271
283
  }
272
284
  // Parse class selector (.classname)
@@ -568,14 +580,22 @@ class SelectorParser {
568
580
  return -1;
569
581
  }
570
582
  create_node(type, start, end) {
571
- let node = this.arena.create_node(type, start, end - start, this.lexer.line, this.lexer.column);
583
+ let node = this.arena.create_node(type, start, end - start, this.lexer.token_line, this.lexer.token_column);
572
584
  this.arena.set_content_start_delta(node, 0);
573
585
  this.arena.set_content_length(node, end - start);
574
586
  return node;
575
587
  }
576
- // Helper to skip whitespace
588
+ create_node_at(type, start, end, line, column) {
589
+ let node = this.arena.create_node(type, start, end - start, line, column);
590
+ this.arena.set_content_start_delta(node, 0);
591
+ this.arena.set_content_length(node, end - start);
592
+ return node;
593
+ }
594
+ // Helper to skip whitespace and update line/column
577
595
  skip_whitespace() {
578
- this.lexer.pos = skip_whitespace_forward(this.source, this.lexer.pos, this.selector_end);
596
+ while (this.lexer.pos < this.selector_end && is_whitespace(this.source.charCodeAt(this.lexer.pos))) {
597
+ this.lexer.advance();
598
+ }
579
599
  }
580
600
  }
581
601
  function parse_selector(source) {
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
+ prelude_wrapper = this.arena.create_node(AT_RULE_PRELUDE, trimmed[0], trimmed[1] - trimmed[0], at_rule_line, at_rule_column);
230
+ let prelude_nodes = this.prelude_parser.parse_prelude(at_rule_name, trimmed[0], trimmed[1], at_rule_line, at_rule_column);
231
+ if (prelude_nodes.length > 0) {
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
@@ -82,3 +82,18 @@ export declare function is_vendor_prefixed(source: string, start: number, end: n
82
82
  * - `color` → false
83
83
  */
84
84
  export declare function is_custom(str: string): boolean;
85
+ /**
86
+ * Strip vendor prefix from a string
87
+ *
88
+ * @param str - The string to strip vendor prefix from
89
+ * @returns The string without vendor prefix, or original string if no prefix found
90
+ *
91
+ * Examples:
92
+ * - `-webkit-keyframes` → `keyframes`
93
+ * - `-moz-appearance` → `appearance`
94
+ * - `-ms-filter` → `filter`
95
+ * - `-o-border-image` → `border-image`
96
+ * - `keyframes` → `keyframes` (no change)
97
+ * - `--custom-property` → `--custom-property` (custom property, not vendor prefix)
98
+ */
99
+ export declare function strip_vendor_prefix(str: string): string;
@@ -114,5 +114,16 @@ function is_custom(str) {
114
114
  if (str.length < 3) return false;
115
115
  return str.charCodeAt(0) === CHAR_MINUS_HYPHEN && str.charCodeAt(1) === CHAR_MINUS_HYPHEN;
116
116
  }
117
+ function strip_vendor_prefix(str) {
118
+ if (!is_vendor_prefixed(str)) {
119
+ return str;
120
+ }
121
+ for (let i = 2; i < str.length; i++) {
122
+ if (str.charCodeAt(i) === CHAR_MINUS_HYPHEN) {
123
+ return str.substring(i + 1);
124
+ }
125
+ }
126
+ return str;
127
+ }
117
128
 
118
- export { CHAR_AMPERSAND, CHAR_ASTERISK, CHAR_CARET, CHAR_CARRIAGE_RETURN, CHAR_COLON, CHAR_DOLLAR, CHAR_DOUBLE_QUOTE, CHAR_EQUALS, CHAR_FORM_FEED, CHAR_FORWARD_SLASH, CHAR_GREATER_THAN, CHAR_LESS_THAN, CHAR_MINUS_HYPHEN, CHAR_NEWLINE, CHAR_PERIOD, CHAR_PIPE, CHAR_PLUS, CHAR_SINGLE_QUOTE, CHAR_SPACE, CHAR_TAB, CHAR_TILDE, is_combinator, is_custom, is_digit, is_vendor_prefixed, is_whitespace, str_equals, str_index_of, str_starts_with };
129
+ export { CHAR_AMPERSAND, CHAR_ASTERISK, CHAR_CARET, CHAR_CARRIAGE_RETURN, CHAR_COLON, CHAR_DOLLAR, CHAR_DOUBLE_QUOTE, CHAR_EQUALS, CHAR_FORM_FEED, CHAR_FORWARD_SLASH, CHAR_GREATER_THAN, CHAR_LESS_THAN, CHAR_MINUS_HYPHEN, CHAR_NEWLINE, CHAR_PERIOD, CHAR_PIPE, CHAR_PLUS, CHAR_SINGLE_QUOTE, CHAR_SPACE, CHAR_TAB, CHAR_TILDE, is_combinator, is_custom, is_digit, is_vendor_prefixed, is_whitespace, str_equals, str_index_of, str_starts_with, strip_vendor_prefix };
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.10.0",
3
+ "version": "0.11.1",
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",