@projectwallace/css-parser 0.19.0 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ Heavily influenced by [CSSTree](https://github.com/csstree/csstree). Some parsin
14
14
  - **Full modern CSS** — CSS Nesting, `:is()`, `:where()`, `:has()`, `@layer`, `@container`
15
15
  - **Error-tolerant** — keeps parsing through malformed input; one bad rule won't kill the file
16
16
  - **Precise source locations** — line, column, offset, and length on every node
17
- - **Tiny footprint** — tree-shakeable subparsers; as small as 278 bytes gzipped, < 16 kB kB worst case
17
+ - **Tiny footprint** — tree-shakeable subparsers; as small as 278 bytes gzipped, < 16 kB worst case
18
18
 
19
19
  ## Installation
20
20
 
@@ -65,34 +65,30 @@ for (const rule of ast) {
65
65
 
66
66
  ## API
67
67
 
68
- Everything is exported from the package root. Each part is also available from its own entry point, so you can import only what you need and skip evaluating the rest of the package. Node properties and return types ship as TypeScript types, so this section sticks to short examples.
68
+ Everything is exported from the package root. Each part is also available from its own entry point, so you can import only what you need. Node properties and return types ship as TypeScript types.
69
69
 
70
70
  ### Entry points
71
71
 
72
- | Import | Exports |
73
- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
74
- | `@projectwallace/css-parser` | Everything below, except the `Lexer` and `Parser` classes |
75
- | `@projectwallace/css-parser/parse` | `parse`, `Parser`, `ParserOptions` |
76
- | `@projectwallace/css-parser/parse-selector` | `parse_selector`, `parse_selector_list` |
77
- | `@projectwallace/css-parser/parse-atrule-prelude` | `parse_atrule_prelude` |
78
- | `@projectwallace/css-parser/parse-declaration` | `parse_declaration` |
79
- | `@projectwallace/css-parser/parse-value` | `parse_value` |
80
- | `@projectwallace/css-parser/parse-anplusb` | `parse_anplusb` |
81
- | `@projectwallace/css-parser/parse-dimension` | `parse_dimension` |
82
- | `@projectwallace/css-parser/tokenizer` | `tokenize`, `Lexer` |
83
- | `@projectwallace/css-parser/walker` | `walk`, `traverse`, `SKIP`, `BREAK` |
84
- | `@projectwallace/css-parser/nodes` | Node type constants (`STYLE_RULE`, `DECLARATION`, …), `NODE_TYPES` |
85
- | `@projectwallace/css-parser/node-types` | Type guards (`is_declaration`, `is_rule`, …) and node types (`CSSNode`, `Rule`, `Declaration`, …) |
86
- | `@projectwallace/css-parser/string-utils` | `is_custom`, `is_vendor_prefixed`, `strip_vendor_prefix`, `str_equals`, `str_starts_with`, `str_index_of` |
87
- | `@projectwallace/css-parser/token-types` | Token type constants (`TOKEN_*`) |
72
+ - `@projectwallace/css-parser` — everything below, except the `Lexer` and `Parser` classes
73
+ - `/parse` — `parse`, `Parser`, `ParserOptions`
74
+ - `/parse-selector` — `parse_selector`, `parse_selector_list`
75
+ - `/parse-atrule-prelude` — `parse_atrule_prelude`
76
+ - `/parse-declaration` — `parse_declaration`
77
+ - `/parse-value` — `parse_value`
78
+ - `/parse-anplusb` — `parse_anplusb`
79
+ - `/parse-dimension` — `parse_dimension`
80
+ - `/tokenizer` — `tokenize`, `Lexer`
81
+ - `/walker` — `walk`, `traverse`, `SKIP`, `BREAK`
82
+ - `/nodes` — node type constants (`STYLE_RULE`, `DECLARATION`, …), `NODE_TYPES`
83
+ - `/node-types` — type guards (`is_declaration`, `is_rule`, …) and node types (`CSSNode`, `Rule`, …)
84
+ - `/string-utils` — `is_custom`, `is_vendor_prefixed`, `strip_vendor_prefix`, `str_equals`, `str_starts_with`, `str_index_of`
85
+ - `/token-types` — token type constants (`TOKEN_*`)
88
86
 
89
87
  ### `parse(source, options?)`
90
88
 
91
- Parses a full stylesheet and returns the root `StyleSheet` node. Nodes are iterable and expose `first_child`, `next_sibling`, `children`, `text`, `line`, `column`, `start`, `end` and `length`.
89
+ Returns the root `StyleSheet` node. Nodes are iterable and expose `first_child`, `next_sibling`, `children`, `text`, `line`, `column`, `start`, `end` and `length`.
92
90
 
93
91
  ```typescript
94
- import { parse } from '@projectwallace/css-parser'
95
-
96
92
  const ast = parse('body { color: red; }')
97
93
  const rule = ast.first_child
98
94
  rule.block.first_child.property // "color"
@@ -104,72 +100,26 @@ Options:
104
100
  - `parse_values` (default `true`) — parse declaration values into nodes
105
101
  - `parse_selectors` (default `true`) — parse selectors into nodes
106
102
  - `parse_atrule_preludes` (default `true`) — parse at-rule preludes into nodes
107
- - `on_comment` — called for every comment; comments are never part of the tree
108
-
109
- ```typescript
110
- const comments = []
111
- parse('/* hi */ a { color: red }', {
112
- parse_values: false,
113
- on_comment: (info) => comments.push(info), // { start, end, length, line, column }
114
- })
115
- ```
116
-
117
- ### `parse_selector(source)` and `parse_selector_list(source)`
103
+ - `on_comment` — called for every comment (`{ start, end, length, line, column }`); comments are never part of the tree
118
104
 
119
- `parse_selector` returns the first selector, `parse_selector_list` the whole `SelectorList`.
105
+ ### Sub-parsers
120
106
 
121
107
  ```typescript
122
- import { parse_selector, parse_selector_list } from '@projectwallace/css-parser'
123
-
124
108
  parse_selector('a.b > c').children.map((part) => part.text) // ["a", ".b", ">", "c"]
125
109
  parse_selector_list('h1, h2').children.map((s) => s.text) // ["h1", "h2"]
126
- ```
127
-
128
- ### `parse_declaration(source)`
129
-
130
- ```typescript
131
- import { parse_declaration } from '@projectwallace/css-parser'
132
110
 
133
111
  const decl = parse_declaration('color: red !important')
134
112
  decl.property // "color"
135
113
  decl.is_important // true
136
- decl.value.text // "red"
137
- ```
138
-
139
- ### `parse_value(source)`
140
-
141
- Parses a declaration value into a `Value` node with one child per component.
142
-
143
- ```typescript
144
- import { parse_value } from '@projectwallace/css-parser'
145
114
 
146
115
  for (const node of parse_value('10px 20px')) {
147
- console.log(node.type_name, node.value, node.unit) // "Dimension" 10 "px", then "Dimension" 20 "px"
116
+ node.type_name // "Dimension"
148
117
  }
149
- ```
150
-
151
- ### `parse_atrule_prelude(name, prelude)`
152
-
153
- Returns an array of nodes for the prelude of the at-rule called `name` (without the `@`).
154
-
155
- ```typescript
156
- import { parse_atrule_prelude } from '@projectwallace/css-parser'
157
118
 
158
- const [query] = parse_atrule_prelude('media', '(min-width: 768px)')
119
+ const [query] = parse_atrule_prelude('media', '(min-width: 768px)') // name without the `@`
159
120
  query.type_name // "MediaQuery"
160
- ```
161
-
162
- ### `parse_dimension(text)`
163
-
164
- Splits a number from its unit. It works for anything shaped like a number followed by a unit: lengths (`100px`), percentages (`2%`), durations (`200ms`) and so on. It does not validate; text without a leading number gives `value: 0`.
165
121
 
166
- ```typescript
167
- import { parse_dimension } from '@projectwallace/css-parser'
168
-
169
- parse_dimension('100px') // { value: 100, unit: "px" }
170
- parse_dimension('2%') // { value: 2, unit: "%" }
171
- parse_dimension('1.5e3s') // { value: 1500, unit: "s" }
172
- parse_dimension('5') // { value: 5, unit: "" }
122
+ parse_dimension('1.5e3s') // { value: 1500, unit: "s" } (no validation; no leading number gives `value: 0`)
173
123
  ```
174
124
 
175
125
  ### `walk(node, callback)` and `traverse(node, { enter, leave })`
@@ -177,28 +127,13 @@ parse_dimension('5') // { value: 5, unit: "" }
177
127
  Both walk the tree depth-first. Return `SKIP` to skip the children of a node and `BREAK` to stop entirely. `walk` also passes a `depth` that counts rule and at-rule nesting. `traverse` calls `enter` before and `leave` after the children (`leave` still runs after `SKIP`, but not after `BREAK`).
178
128
 
179
129
  ```typescript
180
- import {
181
- parse,
182
- walk,
183
- traverse,
184
- SKIP,
185
- BREAK,
186
- DECLARATION,
187
- AT_RULE,
188
- } from '@projectwallace/css-parser'
189
-
190
- const ast = parse('@media screen { a { color: red; margin: 0 } }')
191
-
192
130
  walk(ast, (node) => {
193
- if (node.type === DECLARATION) {
194
- console.log(node.property) // "color"
195
- return BREAK
196
- }
131
+ if (node.type === DECLARATION) return BREAK
197
132
  })
198
133
 
199
134
  traverse(ast, {
200
135
  enter(node) {
201
- if (node.type === AT_RULE) return SKIP // leave() still runs for this node
136
+ if (node.type === AT_RULE) return SKIP
202
137
  },
203
138
  leave(node) {},
204
139
  })
@@ -209,8 +144,6 @@ traverse(ast, {
209
144
  A generator of tokens; comments are skipped and reported through `on_comment`.
210
145
 
211
146
  ```typescript
212
- import { tokenize } from '@projectwallace/css-parser'
213
-
214
147
  for (const token of tokenize('a { color: red }')) {
215
148
  console.log(token.type, token.start, token.end)
216
149
  }
@@ -218,31 +151,19 @@ for (const token of tokenize('a { color: red }')) {
218
151
 
219
152
  ### Node types, type guards and `clone()`
220
153
 
221
- Node type constants (`STYLE_RULE`, `DECLARATION`, `DIMENSION`, …) and `NODE_TYPES` are exported from the root and from `@projectwallace/css-parser/nodes`. Type guards such as `is_declaration()` and `is_dimension()` that narrow a node to its specific type are exported from the root and from `@projectwallace/css-parser/node-types`. `node.clone({ deep?, locations? })` returns a mutable plain-object copy of a node.
154
+ Type guards such as `is_declaration()` narrow a node to its specific type. `node.clone({ deep?, locations? })` returns a mutable plain-object copy of a node.
222
155
 
223
156
  ```typescript
224
- import { parse, is_declaration } from '@projectwallace/css-parser'
225
-
226
157
  const decl = parse('a { color: red }').first_child.block.first_child
227
158
  is_declaration(decl) // true
228
159
  decl.type_name // "Declaration"
229
- decl.clone({ deep: false }).property // "color"
230
160
  ```
231
161
 
232
162
  ### String helpers
233
163
 
234
- Allocation-free helpers, handy when working with node names. Also available from `@projectwallace/css-parser/string-utils`; token type constants (`TOKEN_*`) are available from `@projectwallace/css-parser/token-types`. The lowercase argument is always the first one.
164
+ Allocation-free helpers, handy when working with node names. The lowercase argument is always the first one.
235
165
 
236
166
  ```typescript
237
- import {
238
- is_custom,
239
- is_vendor_prefixed,
240
- strip_vendor_prefix,
241
- str_equals,
242
- str_starts_with,
243
- str_index_of,
244
- } from '@projectwallace/css-parser'
245
-
246
167
  is_custom('--primary') // true
247
168
  is_vendor_prefixed('-webkit-transform') // true
248
169
  strip_vendor_prefix('-webkit-transform') // "transform"
@@ -89,7 +89,6 @@ declare class CSSDataArena {
89
89
  private grow;
90
90
  create_node(type: number, start_offset: number, length: number, start_line: number, start_column: number): number;
91
91
  append_children(parent_index: number, children: number[]): void;
92
- /** Shrink the buffer to the live node count, releasing wasted capacity. Call once after parsing; no-op if already tight. */
93
92
  trim(): void;
94
93
  get_last_sibling(node_index: number): number;
95
94
  has_children(node_index: number): boolean;
@@ -189,7 +189,6 @@ var CSSDataArena = class CSSDataArena {
189
189
  this.view.setUint32(offset + 4, children[0], true);
190
190
  for (let i = 0; i < children.length - 1; i++) this.set_next_sibling(children[i], children[i + 1]);
191
191
  }
192
- /** Shrink the buffer to the live node count, releasing wasted capacity. Call once after parsing; no-op if already tight. */
193
192
  trim() {
194
193
  if (this.count === this.capacity) return;
195
194
  const byte_count = this.count * BYTES_PER_NODE;
@@ -1,4 +1,4 @@
1
- import { A as PARENTHESIS, B as STYLESHEET, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RATIO, G as UNICODE_RANGE, H as SUPPORTS_DECLARATION, I as RAW, J as VALUE, K as UNIVERSAL_SELECTOR, L as SELECTOR, N as PSEUDO_CLASS_SELECTOR, O as NUMBER, P as PSEUDO_ELEMENT_SELECTOR, R as SELECTOR_LIST, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as SUPPORTS_QUERY, V as STYLE_RULE, W as TYPE_SELECTOR, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as URL, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STRING } from "./arena-DvT75bvK.js";
1
+ import { A as PARENTHESIS, B as STYLESHEET, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RATIO, G as UNICODE_RANGE, H as SUPPORTS_DECLARATION, I as RAW, J as VALUE, K as UNIVERSAL_SELECTOR, L as SELECTOR, N as PSEUDO_CLASS_SELECTOR, O as NUMBER, P as PSEUDO_ELEMENT_SELECTOR, R as SELECTOR_LIST, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as SUPPORTS_QUERY, V as STYLE_RULE, W as TYPE_SELECTOR, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as URL, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STRING } from "./arena-7RLvyGPB.js";
2
2
  //#region src/constants.d.ts
3
3
  export declare const NODE_TYPES: {
4
4
  readonly STYLESHEET: 1;
package/dist/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as PARENTHESIS, B as STYLE_RULE, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RAW, G as UNIVERSAL_SELECTOR, H as SUPPORTS_QUERY, I as SELECTOR, K as URL, L as SELECTOR_LIST, M as PSEUDO_CLASS_SELECTOR, N as PSEUDO_ELEMENT_SELECTOR, O as NUMBER, P as RATIO, R as STRING, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as TYPE_SELECTOR, V as SUPPORTS_DECLARATION, W as UNICODE_RANGE, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as VALUE, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STYLESHEET } from "./arena-Bsf96CQK.js";
1
+ import { A as PARENTHESIS, B as STYLE_RULE, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RAW, G as UNIVERSAL_SELECTOR, H as SUPPORTS_QUERY, I as SELECTOR, K as URL, L as SELECTOR_LIST, M as PSEUDO_CLASS_SELECTOR, N as PSEUDO_ELEMENT_SELECTOR, O as NUMBER, P as RATIO, R as STRING, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as TYPE_SELECTOR, V as SUPPORTS_DECLARATION, W as UNICODE_RANGE, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as VALUE, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STYLESHEET } from "./arena-CsJhQRUX.js";
2
2
  //#region src/constants.ts
3
3
  const NODE_TYPES = {
4
4
  STYLESHEET: 1,
@@ -1,4 +1,4 @@
1
- import "./arena-Bsf96CQK.js";
1
+ import "./arena-CsJhQRUX.js";
2
2
  import { a as is_vendor_prefixed, o as is_whitespace, s as str_equals, u as str_starts_with } from "./string-utils-aEEsA2CS.js";
3
3
  import { dimension_number_end } from "./parse-dimension.js";
4
4
  //#region src/css-node.ts
@@ -36,7 +36,8 @@ const nodes_with_children = /* @__PURE__ */ new Set([
36
36
  36,
37
37
  57,
38
38
  59,
39
- 60
39
+ 60,
40
+ 41
40
41
  ]);
41
42
  const enumerable_properties = [
42
43
  "name",
@@ -82,11 +83,9 @@ var CSSNode = class CSSNode {
82
83
  if (length === 0) return "";
83
84
  return this.source.substring(start, start + length);
84
85
  }
85
- /** Get node type as number (for performance) */
86
86
  get type() {
87
87
  return this.arena.get_type(this.index);
88
88
  }
89
- /** Get node type as human-readable string */
90
89
  get type_name() {
91
90
  switch (this.type) {
92
91
  case 1: return "StyleSheet";
@@ -138,13 +137,11 @@ var CSSNode = class CSSNode {
138
137
  default: return "unknown";
139
138
  }
140
139
  }
141
- /** Get the full text of this node from source */
142
140
  get text() {
143
141
  const start = this.arena.get_start_offset(this.index);
144
142
  const length = this.arena.get_length(this.index);
145
143
  return this.source.substring(start, start + length);
146
144
  }
147
- /** Get the "content" text (at-rule name for at-rules, layer name for import layers) */
148
145
  get name() {
149
146
  if (!nodes_with_name.has(this.type)) return;
150
147
  const { type } = this;
@@ -153,7 +150,6 @@ var CSSNode = class CSSNode {
153
150
  if (type === 56 && content === "") return null;
154
151
  return content;
155
152
  }
156
- /** Namespace prefix for type/universal selectors: null (none), '' (`|div`), 'ns' (`ns|div`), or '*' (`*|div`). */
157
153
  get namespace() {
158
154
  const { type } = this;
159
155
  if (type !== 21 && type !== 28) return void 0;
@@ -162,16 +158,11 @@ var CSSNode = class CSSNode {
162
158
  const length = this.arena.get_value_length(this.index);
163
159
  return this.source.substring(start, start + length);
164
160
  }
165
- /** Alias for `name` on declarations ("color" in "color: blue"), more semantic than `name` there. */
166
161
  get property() {
167
162
  const { type } = this;
168
163
  if (type !== 4 && type !== 33 && type !== 57) return;
169
164
  return this.get_content();
170
165
  }
171
- /**
172
- * Declarations: the value text ("1px solid blue"). Dimension/number: the numeric value.
173
- * String: content without quotes. URL: quoted string keeps quotes, unquoted URL doesn't.
174
- */
175
166
  get value() {
176
167
  const { type, text, first_child } = this;
177
168
  if (type === 3) return void 0;
@@ -194,13 +185,13 @@ var CSSNode = class CSSNode {
194
185
  } else if (text.startsWith("\"") || text.startsWith("'")) return text;
195
186
  }
196
187
  if (type === 16 || type === 37) return this.get_content();
188
+ if (type === 38) return text;
197
189
  if (type !== 4 && type !== 15 && type !== 24 && type !== 36 && type !== 41) return;
198
190
  const start = this.arena.get_value_start(this.index);
199
191
  const length = this.arena.get_value_length(this.index);
200
192
  if (length === 0) return null;
201
193
  return this.source.substring(start, start + length);
202
194
  }
203
- /** At-rules: AT_RULE_PRELUDE wrapper (media queries, layer names, …). Style rules: SELECTOR_LIST/SELECTOR. Null if none. */
204
195
  get prelude() {
205
196
  if (this.type === 3) {
206
197
  const first = this.first_child;
@@ -209,7 +200,6 @@ var CSSNode = class CSSNode {
209
200
  }
210
201
  if (this.type === 2) return this.first_child;
211
202
  }
212
- /** Attribute selector operator (=, ~=, |=, ^=, $=, *=), or null for the bare `[attr]` form. */
213
203
  get attr_operator() {
214
204
  if (this.type !== 24) return void 0;
215
205
  const content_end = this.arena.get_content_start(this.index) + this.arena.get_content_length(this.index);
@@ -227,7 +217,6 @@ var CSSNode = class CSSNode {
227
217
  if (ch1 === 42 && ch2 === 61) return this.source.substring(pos, pos + 2);
228
218
  return null;
229
219
  }
230
- /** Attribute selector flag: "i" (case-insensitive), "s" (case-sensitive), or null if absent. */
231
220
  get attr_flags() {
232
221
  if (this.type !== 24) return void 0;
233
222
  const value_len = this.arena.get_value_length(this.index);
@@ -241,43 +230,35 @@ var CSSNode = class CSSNode {
241
230
  if (flag_ch === 105 || flag_ch === 73 || flag_ch === 115 || flag_ch === 83) return this.source.at(pos);
242
231
  return null;
243
232
  }
244
- /** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
245
233
  get unit() {
246
234
  if (this.type !== 12) return void 0;
247
235
  const text = this.text;
248
236
  return text.substring(dimension_number_end(text));
249
237
  }
250
- /** Numerator for ratio values, e.g. the Number "16" in `aspect-ratio: 16/9` */
251
238
  get left() {
252
239
  if (this.type !== 58) return void 0;
253
240
  return this.first_child ?? void 0;
254
241
  }
255
- /** Denominator for ratio values, e.g. the Number "9" in `aspect-ratio: 16/9` */
256
242
  get right() {
257
243
  if (this.type !== 58) return void 0;
258
244
  return this.first_child?.next_sibling ?? void 0;
259
245
  }
260
- /** Get the parsed condition node of an if() branch, e.g. the Function "style(--active: 1)" or the Identifier "else" */
261
246
  get condition() {
262
247
  if (this.type !== 59) return;
263
248
  return this.first_child ?? void 0;
264
249
  }
265
- /** True when this is the else branch of an if() function */
266
250
  get is_else() {
267
251
  if (this.type !== 59) return void 0;
268
252
  return str_equals("else", this.get_content());
269
253
  }
270
- /** Check if this declaration has !important */
271
254
  get is_important() {
272
255
  if (this.type !== 4) return void 0;
273
256
  return this.arena.has_flag(this.index, 1);
274
257
  }
275
- /** Check if this declaration has a browser hack prefix */
276
258
  get is_browserhack() {
277
259
  if (this.type !== 4) return void 0;
278
260
  return this.arena.has_flag(this.index, 128);
279
261
  }
280
- /** Check if this has a vendor prefix (computed on-demand) */
281
262
  get is_vendor_prefixed() {
282
263
  switch (this.type) {
283
264
  case 4: return is_vendor_prefixed(this.get_content());
@@ -290,28 +271,23 @@ var CSSNode = class CSSNode {
290
271
  default: return false;
291
272
  }
292
273
  }
293
- /** Check if this node has an error */
294
274
  get has_error() {
295
275
  return this.arena.has_flag(this.index, 2);
296
276
  }
297
- /** Check if this node has a prelude (at-rules and style rules) */
298
277
  get has_prelude() {
299
278
  const { type } = this;
300
279
  if (type === 3) return this.first_child !== null && this.first_child.type !== 7;
301
280
  if (type === 2) return this.first_child !== null;
302
281
  return false;
303
282
  }
304
- /** Check if this rule has a block { } */
305
283
  get has_block() {
306
284
  return this.arena.has_flag(this.index, 8);
307
285
  }
308
- /** Check if this style rule has declarations */
309
286
  get has_declarations() {
310
287
  const { type } = this;
311
288
  if (type !== 3 && type !== 2) return void 0;
312
289
  return this.arena.has_flag(this.index, 32);
313
290
  }
314
- /** Get the block node (for style rules and at-rules with blocks) */
315
291
  get block() {
316
292
  const { type } = this;
317
293
  if (type === 2) {
@@ -331,7 +307,6 @@ var CSSNode = class CSSNode {
331
307
  }
332
308
  return null;
333
309
  }
334
- /** Check if this block is empty (no declarations or rules, only comments allowed) */
335
310
  get is_empty() {
336
311
  if (this.type !== 7) return void 0;
337
312
  let child = this.first_child;
@@ -341,46 +316,34 @@ var CSSNode = class CSSNode {
341
316
  }
342
317
  return true;
343
318
  }
344
- /** Get start line number */
345
319
  get line() {
346
320
  return this.arena.get_start_line(this.index);
347
321
  }
348
- /** Get start column number */
349
322
  get column() {
350
323
  return this.arena.get_start_column(this.index);
351
324
  }
352
- /** Get start offset in source */
353
325
  get start() {
354
326
  return this.arena.get_start_offset(this.index);
355
327
  }
356
- /** Get length in source */
357
328
  get length() {
358
329
  return this.arena.get_length(this.index);
359
330
  }
360
- /**
361
- * Get end offset in source
362
- * End is not stored, must be calculated
363
- */
364
331
  get end() {
365
332
  return this.start + this.length;
366
333
  }
367
- /** Get first child node */
368
334
  get first_child() {
369
335
  const child_index = this.arena.get_first_child(this.index);
370
336
  if (child_index === 0) return null;
371
337
  return new CSSNode(this.arena, this.source, child_index);
372
338
  }
373
- /** Get next sibling node */
374
339
  get next_sibling() {
375
340
  const sibling_index = this.arena.get_next_sibling(this.index);
376
341
  if (sibling_index === 0) return null;
377
342
  return new CSSNode(this.arena, this.source, sibling_index);
378
343
  }
379
- /** Check if this node has a next sibling */
380
344
  get has_next() {
381
345
  return this.arena.get_next_sibling(this.index) !== 0;
382
346
  }
383
- /** Whether this node has children. For pseudo-class/element functions, tracks FLAG_HAS_PARENS so formatters can tell `:lang()` from `:hover`. */
384
347
  get has_children() {
385
348
  const { type } = this;
386
349
  if (type === 25 || type === 26) {
@@ -388,7 +351,6 @@ var CSSNode = class CSSNode {
388
351
  }
389
352
  return this.arena.has_children(this.index);
390
353
  }
391
- /** Count children without allocating an intermediate array */
392
354
  get child_count() {
393
355
  let count = 0;
394
356
  let child_index = this.arena.get_first_child(this.index);
@@ -398,7 +360,6 @@ var CSSNode = class CSSNode {
398
360
  }
399
361
  return count;
400
362
  }
401
- /** Get all children as an array */
402
363
  get children() {
403
364
  const result = [];
404
365
  let child = this.first_child;
@@ -408,7 +369,6 @@ var CSSNode = class CSSNode {
408
369
  }
409
370
  return result;
410
371
  }
411
- /** Make CSSNode iterable over its children */
412
372
  *[Symbol.iterator]() {
413
373
  let child = this.first_child;
414
374
  while (child) {
@@ -416,7 +376,6 @@ var CSSNode = class CSSNode {
416
376
  child = child.next_sibling;
417
377
  }
418
378
  }
419
- /** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
420
379
  get nth_a() {
421
380
  const { type, arena, index } = this;
422
381
  if (type !== 30 && type !== 31) return void 0;
@@ -425,7 +384,6 @@ var CSSNode = class CSSNode {
425
384
  const start = arena.get_content_start(index);
426
385
  return this.source.substring(start, start + len);
427
386
  }
428
- /** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
429
387
  get nth_b() {
430
388
  const { type, arena, index, source } = this;
431
389
  if (type !== 30 && type !== 31) return void 0;
@@ -446,20 +404,14 @@ var CSSNode = class CSSNode {
446
404
  }
447
405
  return value;
448
406
  }
449
- /** Get the An+B formula node from :nth-child(2n+1 of .foo) */
450
407
  get nth() {
451
408
  if (this.type !== 31) return void 0;
452
409
  return this.first_child ?? void 0;
453
410
  }
454
- /** Get the selector list from :nth-child(2n+1 of .foo) */
455
411
  get selector() {
456
412
  if (this.type !== 31) return void 0;
457
413
  return this.first_child?.next_sibling ?? void 0;
458
414
  }
459
- /**
460
- * Clone this node as a mutable plain object with children as arrays.
461
- * Use sparingly — can consume a lot of memory.
462
- */
463
415
  clone(options = {}) {
464
416
  const { deep = true, locations = false } = options;
465
417
  const { type } = this;
@@ -483,7 +435,7 @@ var CSSNode = class CSSNode {
483
435
  plain.length = this.length;
484
436
  plain.end = this.end;
485
437
  }
486
- if (deep && nodes_with_children.has(type)) {
438
+ if (deep && (nodes_with_children.has(type) || this.has_children)) {
487
439
  plain.children = [];
488
440
  plain.child_count = this.child_count;
489
441
  plain.has_children = this.has_children;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { A as PARENTHESIS, B as STYLESHEET, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RATIO, G as UNICODE_RANGE, H as SUPPORTS_DECLARATION, I as RAW, J as VALUE, K as UNIVERSAL_SELECTOR, L as SELECTOR, N as PSEUDO_CLASS_SELECTOR, O as NUMBER, P as PSEUDO_ELEMENT_SELECTOR, R as SELECTOR_LIST, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as SUPPORTS_QUERY, V as STYLE_RULE, W as TYPE_SELECTOR, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as URL, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STRING } from "./arena-DvT75bvK.js";
1
+ import { A as PARENTHESIS, B as STYLESHEET, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RATIO, G as UNICODE_RANGE, H as SUPPORTS_DECLARATION, I as RAW, J as VALUE, K as UNIVERSAL_SELECTOR, L as SELECTOR, N as PSEUDO_CLASS_SELECTOR, O as NUMBER, P as PSEUDO_ELEMENT_SELECTOR, R as SELECTOR_LIST, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as SUPPORTS_QUERY, V as STYLE_RULE, W as TYPE_SELECTOR, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as URL, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STRING } from "./arena-7RLvyGPB.js";
2
2
  import { NODE_TYPES } from "./constants.js";
3
3
  import { C as TOKEN_SEMICOLON, D as TOKEN_WHITESPACE, E as TOKEN_URL, O as Token, S as TOKEN_RIGHT_PAREN, T as TOKEN_UNICODE_RANGE, _ as TOKEN_LEFT_PAREN, a as TOKEN_CDO, b as TOKEN_RIGHT_BRACE, c as TOKEN_COMMENT, d as TOKEN_EOF, f as TOKEN_FUNCTION, g as TOKEN_LEFT_BRACKET, h as TOKEN_LEFT_BRACE, i as TOKEN_CDC, k as TokenType, l as TOKEN_DELIM, m as TOKEN_IDENT, n as TOKEN_BAD_STRING, o as TOKEN_COLON, p as TOKEN_HASH, r as TOKEN_BAD_URL, s as TOKEN_COMMA, t as TOKEN_AT_KEYWORD, u as TOKEN_DIMENSION, v as TOKEN_NUMBER, w as TOKEN_STRING, x as TOKEN_RIGHT_BRACKET, y as TOKEN_PERCENTAGE } from "./token-types-Bqsf5FX-.js";
4
4
  import { CommentInfo, LexerPosition, tokenize } from "./tokenize.js";
5
- import { $ as is_atrule, A as Operator, At as is_pseudo_element_selector, B as SelectorList, Bt as is_type_selector, C as MediaFeature, Ct as is_nth_selector, D as NthOfSelector, Dt as is_prelude_operator, E as NestingSelector, Et as is_parenthesis, F as PseudoElementSelector, Ft as is_selector_list, G as SupportsQuery, Gt as CloneOptions, H as String, Ht as is_universal_selector, I as Ratio, It as is_string, J as UnicodeRange, K as ToPlain, Kt as PlainCSSNode, L as Raw, Lt as is_stylesheet, M as PreludeOperator, Mt as is_raw, N as PreludeSelectorList, Nt as is_rule, O as NthSelector, Ot as is_prelude_selectorlist, P as PseudoClassSelector, Pt as is_selector, Q as WithChildren, R as Rule, Rt as is_supports_declaration, S as LayerName, St as is_nth_of_selector, T as MediaType, Tt as is_operator, U as StyleSheet, Ut as is_url, V as SelectorNode, Vt as is_unicode_range, W as SupportsDeclaration, Wt as is_value, X as Url, Y as UniversalSelector, Z as Value, _ as IdSelector, _t as is_layer_name, a as Block, at as is_comment, b as IfCondition, bt as is_media_type, c as ClassSelector, ct as is_dimension, d as ContainerQuery, dt as is_hash, et as is_atrule_prelude, f as Declaration, ft as is_id_selector, g as Hash, gt as is_lang_selector, h as Function, ht as is_if_condition, i as AttributeSelector, it as is_combinator, j as Parenthesis, jt as is_ratio, k as Number, kt as is_pseudo_class_selector, l as Combinator, lt as is_feature_range, m as FeatureRange, mt as is_if_branch, n as Atrule, nt as is_block, ot as is_container_query, p as Dimension, pt as is_identifier, q as TypeSelector, r as AtrulePrelude, rt as is_class_selector, s as CSSNode, st as is_declaration, t as AnyNode, tt as is_attribute_selector, u as Comment, ut as is_function, v as Identifier, vt as is_media_feature, w as MediaQuery, wt as is_number, x as LangSelector, xt as is_nesting_selector, y as IfBranch, yt as is_media_query, z as Selector, zt as is_supports_query } from "./node-types-ZcWS9nNe.js";
5
+ import { $ as is_atrule, A as Operator, At as is_pseudo_element_selector, B as SelectorList, Bt as is_type_selector, C as MediaFeature, Ct as is_nth_selector, D as NthOfSelector, Dt as is_prelude_operator, E as NestingSelector, Et as is_parenthesis, F as PseudoElementSelector, Ft as is_selector_list, G as SupportsQuery, Gt as CloneOptions, H as String, Ht as is_universal_selector, I as Ratio, It as is_string, J as UnicodeRange, K as ToPlain, Kt as PlainCSSNode, L as Raw, Lt as is_stylesheet, M as PreludeOperator, Mt as is_raw, N as PreludeSelectorList, Nt as is_rule, O as NthSelector, Ot as is_prelude_selectorlist, P as PseudoClassSelector, Pt as is_selector, Q as WithChildren, R as Rule, Rt as is_supports_declaration, S as LayerName, St as is_nth_of_selector, T as MediaType, Tt as is_operator, U as StyleSheet, Ut as is_url, V as SelectorNode, Vt as is_unicode_range, W as SupportsDeclaration, Wt as is_value, X as Url, Y as UniversalSelector, Z as Value, _ as IdSelector, _t as is_layer_name, a as Block, at as is_comment, b as IfCondition, bt as is_media_type, c as ClassSelector, ct as is_dimension, d as ContainerQuery, dt as is_hash, et as is_atrule_prelude, f as Declaration, ft as is_id_selector, g as Hash, gt as is_lang_selector, h as Function, ht as is_if_condition, i as AttributeSelector, it as is_combinator, j as Parenthesis, jt as is_ratio, k as Number, kt as is_pseudo_class_selector, l as Combinator, lt as is_feature_range, m as FeatureRange, mt as is_if_branch, n as Atrule, nt as is_block, ot as is_container_query, p as Dimension, pt as is_identifier, q as TypeSelector, r as AtrulePrelude, rt as is_class_selector, s as CSSNode, st as is_declaration, t as AnyNode, tt as is_attribute_selector, u as Comment, ut as is_function, v as Identifier, vt as is_media_feature, w as MediaQuery, wt as is_number, x as LangSelector, xt as is_nesting_selector, y as IfBranch, yt as is_media_query, z as Selector, zt as is_supports_query } from "./node-types-CD7A-d_c.js";
6
6
  import { ParserOptions, parse } from "./parse.js";
7
7
  import { parse_selector, parse_selector_list } from "./parse-selector.js";
8
8
  import { parse_atrule_prelude } from "./parse-atrule-prelude.js";
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { n as tokenize } from "./tokenize-CduZS6FG.js";
1
+ import { n as tokenize } from "./tokenize-B2QSzdrM.js";
2
2
  import { 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_UNICODE_RANGE, TOKEN_URL, TOKEN_WHITESPACE } from "./token-types.js";
3
- import { A as PARENTHESIS, B as STYLE_RULE, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RAW, G as UNIVERSAL_SELECTOR, H as SUPPORTS_QUERY, I as SELECTOR, K as URL, L as SELECTOR_LIST, M as PSEUDO_CLASS_SELECTOR, N as PSEUDO_ELEMENT_SELECTOR, O as NUMBER, P as RATIO, R as STRING, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as TYPE_SELECTOR, V as SUPPORTS_DECLARATION, W as UNICODE_RANGE, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as VALUE, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STYLESHEET } from "./arena-Bsf96CQK.js";
3
+ import { A as PARENTHESIS, B as STYLE_RULE, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RAW, G as UNIVERSAL_SELECTOR, H as SUPPORTS_QUERY, I as SELECTOR, K as URL, L as SELECTOR_LIST, M as PSEUDO_CLASS_SELECTOR, N as PSEUDO_ELEMENT_SELECTOR, O as NUMBER, P as RATIO, R as STRING, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as TYPE_SELECTOR, V as SUPPORTS_DECLARATION, W as UNICODE_RANGE, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, p as FLAG_IMPORTANT, q as VALUE, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STYLESHEET } from "./arena-CsJhQRUX.js";
4
4
  import { a as is_vendor_prefixed, d as strip_vendor_prefix, l as str_index_of, r as is_custom, s as str_equals, u as str_starts_with } from "./string-utils-aEEsA2CS.js";
5
5
  import { parse_dimension } from "./parse-dimension.js";
6
- import { n as parse_selector, r as parse_selector_list } from "./parse-selector-BJJ4gjdB.js";
6
+ import { n as parse_selector, r as parse_selector_list } from "./parse-selector-DORsuJvq.js";
7
7
  import { parse_atrule_prelude } from "./parse-atrule-prelude.js";
8
8
  import { parse_value } from "./parse-value.js";
9
9
  import { parse_declaration } from "./parse-declaration.js";
@@ -1,4 +1,4 @@
1
- import { A as PARENTHESIS, B as STYLESHEET, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RATIO, G as UNICODE_RANGE, H as SUPPORTS_DECLARATION, I as RAW, J as VALUE, K as UNIVERSAL_SELECTOR, L as SELECTOR, M as PRELUDE_SELECTORLIST, N as PSEUDO_CLASS_SELECTOR, O as NUMBER, P as PSEUDO_ELEMENT_SELECTOR, R as SELECTOR_LIST, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as SUPPORTS_QUERY, V as STYLE_RULE, W as TYPE_SELECTOR, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, q as URL, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STRING } from "./arena-DvT75bvK.js";
1
+ import { A as PARENTHESIS, B as STYLESHEET, C as MEDIA_QUERY, D as NTH_SELECTOR, E as NTH_OF_SELECTOR, F as RATIO, G as UNICODE_RANGE, H as SUPPORTS_DECLARATION, I as RAW, J as VALUE, K as UNIVERSAL_SELECTOR, L as SELECTOR, M as PRELUDE_SELECTORLIST, N as PSEUDO_CLASS_SELECTOR, O as NUMBER, P as PSEUDO_ELEMENT_SELECTOR, R as SELECTOR_LIST, S as MEDIA_FEATURE, T as NESTING_SELECTOR, U as SUPPORTS_QUERY, V as STYLE_RULE, W as TYPE_SELECTOR, _ as ID_SELECTOR, a as CLASS_SELECTOR, b as LANG_SELECTOR, c as CONTAINER_QUERY, d as DIMENSION, f as FEATURE_RANGE, g as IDENTIFIER, h as HASH, i as BLOCK, j as PRELUDE_OPERATOR, k as OPERATOR, m as FUNCTION, n as AT_RULE, o as COMBINATOR, q as URL, r as AT_RULE_PRELUDE, s as COMMENT, t as ATTRIBUTE_SELECTOR, u as DECLARATION, v as IF_BRANCH, w as MEDIA_TYPE, x as LAYER_NAME, y as IF_CONDITION, z as STRING } from "./arena-7RLvyGPB.js";
2
2
  //#region src/css-node.d.ts
3
3
  type TypeName = 'StyleSheet' | 'Rule' | 'Atrule' | 'Declaration' | 'Selector' | 'Comment' | 'Block' | 'Raw' | 'Identifier' | 'Number' | 'Dimension' | 'String' | 'Hash' | 'Function' | 'Operator' | 'Parentheses' | 'Url' | 'UnicodeRange' | 'Value' | 'SelectorList' | 'TypeSelector' | 'ClassSelector' | 'IdSelector' | 'AttributeSelector' | 'PseudoClassSelector' | 'PseudoElementSelector' | 'Combinator' | 'UniversalSelector' | 'NestingSelector' | 'Nth' | 'NthOf' | 'Lang' | 'MediaQuery' | 'Feature' | 'MediaType' | 'ContainerQuery' | 'SupportsQuery' | 'SupportsDeclaration' | 'Layer' | 'MediaFeatureRange' | 'AtrulePrelude' | 'PreludeSelectorList' | 'Ratio' | 'IfBranch' | 'IfCondition' | 'unknown';
4
4
  type CSSNodeType = typeof STYLESHEET | typeof STYLE_RULE | typeof AT_RULE | typeof DECLARATION | typeof SELECTOR | typeof COMMENT | typeof BLOCK | typeof RAW | typeof IDENTIFIER | typeof NUMBER | typeof DIMENSION | typeof STRING | typeof HASH | typeof FUNCTION | typeof OPERATOR | typeof PARENTHESIS | typeof URL | typeof UNICODE_RANGE | 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 | typeof PRELUDE_SELECTORLIST | typeof SUPPORTS_DECLARATION | typeof RATIO | typeof IF_BRANCH | typeof IF_CONDITION;
@@ -195,40 +195,19 @@ type Value = WithClone<CSSNode & WithChildren<ValueLike> & {
195
195
  readonly type: typeof VALUE;
196
196
  readonly type_name: 'Value';
197
197
  }>;
198
- /**
199
- * A compound `if()`-branch condition combining multiple tests with `not`/`and`/`or`,
200
- * e.g. `not style(--scheme: light)` or `style(--a: 1) and media(width > 600px)`.
201
- *
202
- * Children are the flat sequence of PreludeOperator ("not"/"and"/"or") and
203
- * Function (`style()`/`media()`/`supports()`) nodes, in source order — the same
204
- * shape `@supports`'s own compound condition uses.
205
- */
198
+ /** Compound `if()` condition: a flat sequence of `not`/`and`/`or` operators and `style()`/`media()`/`supports()` functions, e.g. `not style(--a: 1)`. */
206
199
  type IfCondition = WithClone<CSSNode & WithChildren<Function | PreludeOperator> & {
207
200
  readonly type: typeof IF_CONDITION;
208
201
  readonly type_name: 'IfCondition';
209
202
  }>;
210
- /**
211
- * One branch inside a CSS `if()` inline conditional function.
212
- *
213
- * Each branch corresponds to a `<condition>: <value>` pair in:
214
- * `if( <condition>: <value>; … else: <fallback> )`
215
- *
216
- * - `condition` — the parsed condition node: a Function (e.g. `style(--x: 1)`), an
217
- * Identifier (`else`), or an IfCondition for compound `not`/`and`/`or` conditions
218
- * (e.g. `not style(--x: 1)`)
219
- * - `value` — the value text, e.g. `"green"`; `null` when omitted
220
- * - `is_else` — `true` for the `else` branch
221
- * - `first_child` — same node as `condition`
222
- * - `children` — condition node followed by parsed value nodes
223
- */
203
+ /** A `<condition>: <value>` branch of an `if()` function. `children` is the condition followed by the parsed value nodes. */
224
204
  type IfBranch = CSSNode & WithChildren<Function | Identifier | IfCondition | Value> & {
225
205
  readonly type: typeof IF_BRANCH;
226
206
  readonly type_name: 'IfBranch';
227
- /** The parsed condition node, e.g. the Function "style(--active: 1)", the Identifier "else", or an IfCondition for compound conditions */
207
+ /** Function (`style(--x: 1)`), Identifier (`else`) or IfCondition */
228
208
  readonly condition: Function | Identifier | IfCondition;
229
- /** The parsed value as a VALUE node, or null when the branch value is empty */
209
+ /** Branch value, or null when empty */
230
210
  readonly value: Value | null;
231
- /** True when this is the else branch */
232
211
  readonly is_else: boolean;
233
212
  clone(options?: CloneOptions): ToPlain<IfBranch>;
234
213
  };
@@ -342,12 +321,19 @@ type LayerName = Leaf<typeof LAYER_NAME, 'Layer', {
342
321
  * A parenthesised selector argument in an at-rule prelude, e.g. `(.parent)`
343
322
  * in `@scope (.parent) to (.child)`. Distinct from SELECTOR_LIST, which
344
323
  * already means "matches elements", so tooling can tell scoping arguments
345
- * apart from real selectors. `value` is the trimmed text inside the parens.
324
+ * apart from real selectors. `value` is the trimmed text inside the parens;
325
+ * the parsed selectors are available as a single `SelectorList` child
326
+ * (absent when the parens are empty).
346
327
  */
347
- type PreludeSelectorList = Leaf<typeof PRELUDE_SELECTORLIST, 'PreludeSelectorList', {
328
+ type PreludeSelectorList = WithClone<CSSNode & WithChildren<SelectorList> & {
329
+ readonly type: typeof PRELUDE_SELECTORLIST;
330
+ readonly type_name: 'PreludeSelectorList';
331
+ readonly value: string;
332
+ }>;
333
+ type PreludeOperator = Leaf<typeof PRELUDE_OPERATOR, 'Operator', {
334
+ /** The operator keyword or range comparator, e.g. "and", "or", "not", "only", "to", "<", "<=", ">", ">=", "=" */
348
335
  readonly value: string;
349
336
  }>;
350
- type PreludeOperator = Leaf<typeof PRELUDE_OPERATOR, 'Operator'>;
351
337
  type FeatureRange = WithClone<CSSNode & WithChildren<Dimension | Function | PreludeOperator> & {
352
338
  readonly type: typeof FEATURE_RANGE;
353
339
  readonly type_name: 'MediaFeatureRange';
@@ -1,2 +1,2 @@
1
- import { $ as is_atrule, A as Operator, At as is_pseudo_element_selector, B as SelectorList, Bt as is_type_selector, C as MediaFeature, Ct as is_nth_selector, D as NthOfSelector, Dt as is_prelude_operator, E as NestingSelector, Et as is_parenthesis, F as PseudoElementSelector, Ft as is_selector_list, G as SupportsQuery, Gt as CloneOptions, H as String, Ht as is_universal_selector, I as Ratio, It as is_string, J as UnicodeRange, K as ToPlain, Kt as PlainCSSNode, L as Raw, Lt as is_stylesheet, M as PreludeOperator, Mt as is_raw, N as PreludeSelectorList, Nt as is_rule, O as NthSelector, Ot as is_prelude_selectorlist, P as PseudoClassSelector, Pt as is_selector, Q as WithChildren, R as Rule, Rt as is_supports_declaration, S as LayerName, St as is_nth_of_selector, T as MediaType, Tt as is_operator, U as StyleSheet, Ut as is_url, V as SelectorNode, Vt as is_unicode_range, W as SupportsDeclaration, Wt as is_value, X as Url, Y as UniversalSelector, Z as Value, _ as IdSelector, _t as is_layer_name, a as Block, at as is_comment, b as IfCondition, bt as is_media_type, c as ClassSelector, ct as is_dimension, d as ContainerQuery, dt as is_hash, et as is_atrule_prelude, f as Declaration, ft as is_id_selector, g as Hash, gt as is_lang_selector, h as Function, ht as is_if_condition, i as AttributeSelector, it as is_combinator, j as Parenthesis, jt as is_ratio, k as Number, kt as is_pseudo_class_selector, l as Combinator, lt as is_feature_range, m as FeatureRange, mt as is_if_branch, n as Atrule, nt as is_block, o as BlockChild, ot as is_container_query, p as Dimension, pt as is_identifier, q as TypeSelector, r as AtrulePrelude, rt as is_class_selector, s as CSSNode, st as is_declaration, t as AnyNode, tt as is_attribute_selector, u as Comment, ut as is_function, v as Identifier, vt as is_media_feature, w as MediaQuery, wt as is_number, x as LangSelector, xt as is_nesting_selector, y as IfBranch, yt as is_media_query, z as Selector, zt as is_supports_query } from "./node-types-ZcWS9nNe.js";
1
+ import { $ as is_atrule, A as Operator, At as is_pseudo_element_selector, B as SelectorList, Bt as is_type_selector, C as MediaFeature, Ct as is_nth_selector, D as NthOfSelector, Dt as is_prelude_operator, E as NestingSelector, Et as is_parenthesis, F as PseudoElementSelector, Ft as is_selector_list, G as SupportsQuery, Gt as CloneOptions, H as String, Ht as is_universal_selector, I as Ratio, It as is_string, J as UnicodeRange, K as ToPlain, Kt as PlainCSSNode, L as Raw, Lt as is_stylesheet, M as PreludeOperator, Mt as is_raw, N as PreludeSelectorList, Nt as is_rule, O as NthSelector, Ot as is_prelude_selectorlist, P as PseudoClassSelector, Pt as is_selector, Q as WithChildren, R as Rule, Rt as is_supports_declaration, S as LayerName, St as is_nth_of_selector, T as MediaType, Tt as is_operator, U as StyleSheet, Ut as is_url, V as SelectorNode, Vt as is_unicode_range, W as SupportsDeclaration, Wt as is_value, X as Url, Y as UniversalSelector, Z as Value, _ as IdSelector, _t as is_layer_name, a as Block, at as is_comment, b as IfCondition, bt as is_media_type, c as ClassSelector, ct as is_dimension, d as ContainerQuery, dt as is_hash, et as is_atrule_prelude, f as Declaration, ft as is_id_selector, g as Hash, gt as is_lang_selector, h as Function, ht as is_if_condition, i as AttributeSelector, it as is_combinator, j as Parenthesis, jt as is_ratio, k as Number, kt as is_pseudo_class_selector, l as Combinator, lt as is_feature_range, m as FeatureRange, mt as is_if_branch, n as Atrule, nt as is_block, o as BlockChild, ot as is_container_query, p as Dimension, pt as is_identifier, q as TypeSelector, r as AtrulePrelude, rt as is_class_selector, s as CSSNode, st as is_declaration, t as AnyNode, tt as is_attribute_selector, u as Comment, ut as is_function, v as Identifier, vt as is_media_feature, w as MediaQuery, wt as is_number, x as LangSelector, xt as is_nesting_selector, y as IfBranch, yt as is_media_query, z as Selector, zt as is_supports_query } from "./node-types-CD7A-d_c.js";
2
2
  export { AnyNode, Atrule, AtrulePrelude, AttributeSelector, Block, BlockChild, CSSNode, ClassSelector, type CloneOptions, Combinator, Comment, ContainerQuery, Declaration, Dimension, FeatureRange, Function, Hash, IdSelector, Identifier, IfBranch, IfCondition, LangSelector, LayerName, MediaFeature, MediaQuery, MediaType, NestingSelector, NthOfSelector, NthSelector, Number, Operator, Parenthesis, type PlainCSSNode, PreludeOperator, PreludeSelectorList, PseudoClassSelector, PseudoElementSelector, Ratio, Raw, Rule, Selector, SelectorList, SelectorNode, String, StyleSheet, SupportsDeclaration, SupportsQuery, ToPlain, TypeSelector, UnicodeRange, UniversalSelector, Url, Value, WithChildren, is_atrule, is_atrule_prelude, is_attribute_selector, is_block, is_class_selector, is_combinator, is_comment, is_container_query, is_declaration, is_dimension, is_feature_range, is_function, is_hash, is_id_selector, is_identifier, is_if_branch, is_if_condition, is_lang_selector, is_layer_name, is_media_feature, is_media_query, is_media_type, is_nesting_selector, is_nth_of_selector, is_nth_selector, is_number, is_operator, is_parenthesis, is_prelude_operator, is_prelude_selectorlist, is_pseudo_class_selector, is_pseudo_element_selector, is_ratio, is_raw, is_rule, is_selector, is_selector_list, is_string, is_stylesheet, is_supports_declaration, is_supports_query, is_type_selector, is_unicode_range, is_universal_selector, is_url, is_value };
@@ -1,4 +1,4 @@
1
- import "./arena-Bsf96CQK.js";
1
+ import "./arena-CsJhQRUX.js";
2
2
  //#region src/node-types.ts
3
3
  function is_stylesheet(node) {
4
4
  return node.type === 1;
@@ -1,5 +1,5 @@
1
- import { l as CSSDataArena } from "./arena-DvT75bvK.js";
2
- import { O as NthSelector } from "./node-types-ZcWS9nNe.js";
1
+ import { l as CSSDataArena } from "./arena-7RLvyGPB.js";
2
+ import { O as NthSelector } from "./node-types-CD7A-d_c.js";
3
3
  //#region src/parse-anplusb.d.ts
4
4
  /** @internal */
5
5
  export declare class ANplusBParser {
@@ -8,15 +8,7 @@ export declare class ANplusBParser {
8
8
  private source;
9
9
  private expr_end;
10
10
  constructor(arena: CSSDataArena, source: string);
11
- /**
12
- * Parse An+B expression
13
- * Examples: odd, even, 3, n, -n, 2n, 2n+1, -3n-5
14
- */
15
11
  parse_anplusb(start: number, end: number, line?: number): number | null;
16
- /**
17
- * Parse the b part after 'n'
18
- * Handles: +5, -3, whitespace variations
19
- */
20
12
  private parse_b_part;
21
13
  private skip_whitespace;
22
14
  private create_anplusb_node;
@@ -1,8 +1,8 @@
1
- import { t as Lexer } from "./tokenize-CduZS6FG.js";
1
+ import { t as Lexer } from "./tokenize-B2QSzdrM.js";
2
2
  import "./token-types.js";
3
- import { l as CSSDataArena } from "./arena-Bsf96CQK.js";
3
+ import { l as CSSDataArena } from "./arena-CsJhQRUX.js";
4
4
  import { l as str_index_of, s as str_equals } from "./string-utils-aEEsA2CS.js";
5
- import { t as CSSNode } from "./css-node-XDNQLChU.js";
5
+ import { t as CSSNode } from "./css-node-BKcf0ZeS.js";
6
6
  //#region src/parse-anplusb.ts
7
7
  /** @internal */
8
8
  var ANplusBParser = class {
@@ -16,10 +16,6 @@ var ANplusBParser = class {
16
16
  this.lexer = new Lexer(source);
17
17
  this.expr_end = 0;
18
18
  }
19
- /**
20
- * Parse An+B expression
21
- * Examples: odd, even, 3, n, -n, 2n, 2n+1, -3n-5
22
- */
23
19
  parse_anplusb(start, end, line = 1) {
24
20
  this.expr_end = end;
25
21
  this.lexer.seek(start, line);
@@ -139,10 +135,6 @@ var ANplusBParser = class {
139
135
  }
140
136
  return null;
141
137
  }
142
- /**
143
- * Parse the b part after 'n'
144
- * Handles: +5, -3, whitespace variations
145
- */
146
138
  parse_b_part() {
147
139
  this.skip_whitespace();
148
140
  if (this.lexer.pos >= this.expr_end) return null;
@@ -1,5 +1,5 @@
1
- import { l as CSSDataArena } from "./arena-DvT75bvK.js";
2
- import { t as AnyNode } from "./node-types-ZcWS9nNe.js";
1
+ import { l as CSSDataArena } from "./arena-7RLvyGPB.js";
2
+ import { t as AnyNode } from "./node-types-CD7A-d_c.js";
3
3
  //#region src/parse-atrule-prelude.d.ts
4
4
  /** @internal */
5
5
  export declare class AtRulePreludeParser {
@@ -9,6 +9,7 @@ export declare class AtRulePreludeParser {
9
9
  private prelude_end;
10
10
  private value_node_parser;
11
11
  private condition_parser;
12
+ private selector_parser;
12
13
  constructor(arena: CSSDataArena, source: string);
13
14
  parse_prelude(at_rule_name: string, start: number, end: number, line?: number, column?: number): number[];
14
15
  private parse_prelude_dispatch;
@@ -1,10 +1,10 @@
1
- import { t as Lexer } from "./tokenize-CduZS6FG.js";
1
+ import { t as Lexer } from "./tokenize-B2QSzdrM.js";
2
2
  import "./token-types.js";
3
- import { l as CSSDataArena } from "./arena-Bsf96CQK.js";
3
+ import { l as CSSDataArena } from "./arena-CsJhQRUX.js";
4
4
  import { c as str_equals_range, d as strip_vendor_prefix, t as is_and_or_not_range } from "./string-utils-aEEsA2CS.js";
5
- import { t as CSSNode } from "./css-node-XDNQLChU.js";
6
- import { s as trim_boundaries } from "./parse-selector-BJJ4gjdB.js";
7
- import { n as ConditionParser, t as ValueNodeParser } from "./value-node-parser-Duyvn_ok.js";
5
+ import { t as CSSNode } from "./css-node-BKcf0ZeS.js";
6
+ import { s as trim_boundaries, t as SelectorParser } from "./parse-selector-DORsuJvq.js";
7
+ import { n as ConditionParser, t as ValueNodeParser } from "./value-node-parser-B568SwUx.js";
8
8
  //#region src/parse-atrule-prelude.ts
9
9
  /** @internal */
10
10
  var AtRulePreludeParser = class {
@@ -14,6 +14,7 @@ var AtRulePreludeParser = class {
14
14
  prelude_end;
15
15
  value_node_parser;
16
16
  condition_parser;
17
+ selector_parser;
17
18
  constructor(arena, source) {
18
19
  this.arena = arena;
19
20
  this.source = source;
@@ -21,6 +22,7 @@ var AtRulePreludeParser = class {
21
22
  this.prelude_end = 0;
22
23
  this.value_node_parser = new ValueNodeParser(arena, source);
23
24
  this.condition_parser = new ConditionParser(arena, source, this.value_node_parser);
25
+ this.selector_parser = new SelectorParser(arena, source);
24
26
  }
25
27
  parse_prelude(at_rule_name, start, end, line = 1, column = 1) {
26
28
  this.prelude_end = end;
@@ -270,6 +272,7 @@ var AtRulePreludeParser = class {
270
272
  return layer_node;
271
273
  }
272
274
  const content_start = this.lexer.token_end;
275
+ const after_open = this.lexer.save_position();
273
276
  const matched = this.scan_matching_paren();
274
277
  const content_end = this.paren_content_end;
275
278
  const layer_end = matched ? this.paren_close_end : this.lexer.pos;
@@ -278,14 +281,16 @@ var AtRulePreludeParser = class {
278
281
  this.arena.set_content_length(func_node, name_end - layer_start);
279
282
  const trimmed = matched ? trim_boundaries(this.source, content_start, content_end) : null;
280
283
  if (trimmed) {
281
- const name_start = trimmed[0];
282
- const name_length = trimmed[1] - name_start;
283
- this.arena.set_value_start_delta(func_node, name_start - layer_start);
284
- this.arena.set_value_length(func_node, name_length);
285
- const name_node = this.create_node(37, name_start, trimmed[1]);
286
- this.arena.set_content_start_delta(name_node, 0);
287
- this.arena.set_content_length(name_node, name_length);
288
- this.arena.set_first_child(func_node, name_node);
284
+ this.arena.set_value_start_delta(func_node, trimmed[0] - layer_start);
285
+ this.arena.set_value_length(func_node, trimmed[1] - trimmed[0]);
286
+ const after_close = this.lexer.save_position();
287
+ const saved_prelude_end = this.prelude_end;
288
+ this.lexer.restore_position(after_open);
289
+ this.prelude_end = content_end;
290
+ const names = this.parse_layer_names();
291
+ this.prelude_end = saved_prelude_end;
292
+ this.lexer.restore_position(after_close);
293
+ this.arena.append_children(func_node, names);
289
294
  }
290
295
  return func_node;
291
296
  }
@@ -375,6 +380,8 @@ var AtRulePreludeParser = class {
375
380
  this.next_token();
376
381
  const paren_start = this.lexer.token_start;
377
382
  const content_start = this.lexer.pos;
383
+ const content_line = this.lexer.token_line;
384
+ const content_column = this.lexer.token_column;
378
385
  this.scan_matching_paren();
379
386
  const content_end = this.paren_content_end;
380
387
  const paren_end = this.paren_close_end;
@@ -383,6 +390,14 @@ var AtRulePreludeParser = class {
383
390
  if (trimmed) {
384
391
  this.arena.set_value_start_delta(scope_node, trimmed[0] - paren_start);
385
392
  this.arena.set_value_length(scope_node, trimmed[1] - trimmed[0]);
393
+ let line = content_line;
394
+ let column = content_column;
395
+ for (let i = content_start; i < trimmed[0]; i++) if (this.source.charCodeAt(i) === 10) {
396
+ line++;
397
+ column = 1;
398
+ } else column++;
399
+ const selector_list = this.selector_parser.parse_selector(trimmed[0], trimmed[1], line, column, false);
400
+ if (selector_list !== null) this.arena.set_first_child(scope_node, selector_list);
386
401
  }
387
402
  nodes.push(scope_node);
388
403
  } else if (token_type === 1) {
@@ -1,6 +1,6 @@
1
- import { l as CSSDataArena } from "./arena-DvT75bvK.js";
1
+ import { l as CSSDataArena } from "./arena-7RLvyGPB.js";
2
2
  import { Lexer } from "./tokenize.js";
3
- import { f as Declaration } from "./node-types-ZcWS9nNe.js";
3
+ import { f as Declaration } from "./node-types-CD7A-d_c.js";
4
4
  //#region src/parse-declaration.d.ts
5
5
  /** @internal */
6
6
  export declare class DeclarationParser {
@@ -1,9 +1,9 @@
1
- import { t as Lexer } from "./tokenize-CduZS6FG.js";
1
+ import { t as Lexer } from "./tokenize-B2QSzdrM.js";
2
2
  import "./token-types.js";
3
- import { l as CSSDataArena } from "./arena-Bsf96CQK.js";
3
+ import { l as CSSDataArena } from "./arena-CsJhQRUX.js";
4
4
  import { a as is_vendor_prefixed } from "./string-utils-aEEsA2CS.js";
5
- import { t as CSSNode } from "./css-node-XDNQLChU.js";
6
- import { a as skip_whitespace_and_comments_backward, o as skip_whitespace_and_comments_forward } from "./parse-selector-BJJ4gjdB.js";
5
+ import { t as CSSNode } from "./css-node-BKcf0ZeS.js";
6
+ import { a as skip_whitespace_and_comments_backward, o as skip_whitespace_and_comments_forward } from "./parse-selector-DORsuJvq.js";
7
7
  import { ValueParser } from "./parse-value.js";
8
8
  //#region src/parse-declaration.ts
9
9
  /** @internal */
@@ -1,8 +1,8 @@
1
- import { t as Lexer } from "./tokenize-CduZS6FG.js";
1
+ import { t as Lexer } from "./tokenize-B2QSzdrM.js";
2
2
  import "./token-types.js";
3
- import { l as CSSDataArena } from "./arena-Bsf96CQK.js";
3
+ import { l as CSSDataArena } from "./arena-CsJhQRUX.js";
4
4
  import { c as str_equals_range, n as is_combinator, o as is_whitespace } from "./string-utils-aEEsA2CS.js";
5
- import { t as CSSNode } from "./css-node-XDNQLChU.js";
5
+ import { t as CSSNode } from "./css-node-BKcf0ZeS.js";
6
6
  import { ANplusBParser } from "./parse-anplusb.js";
7
7
  //#region src/parse-utils.ts
8
8
  function skip_whitespace_forward(source, pos, end) {
@@ -1,5 +1,5 @@
1
- import { l as CSSDataArena } from "./arena-DvT75bvK.js";
2
- import { B as SelectorList, z as Selector } from "./node-types-ZcWS9nNe.js";
1
+ import { l as CSSDataArena } from "./arena-7RLvyGPB.js";
2
+ import { B as SelectorList, z as Selector } from "./node-types-CD7A-d_c.js";
3
3
  //#region src/parse-selector.d.ts
4
4
  /** @internal */
5
5
  export declare class SelectorParser {
@@ -1 +1 @@
1
- export { t as SelectorParser, n as parse_selector, r as parse_selector_list } from "./parse-selector-BJJ4gjdB.js";
1
+ export { t as SelectorParser, n as parse_selector, r as parse_selector_list } from "./parse-selector-DORsuJvq.js";
@@ -1,5 +1,5 @@
1
- import { l as CSSDataArena } from "./arena-DvT75bvK.js";
2
- import { Z as Value } from "./node-types-ZcWS9nNe.js";
1
+ import { l as CSSDataArena } from "./arena-7RLvyGPB.js";
2
+ import { Z as Value } from "./node-types-CD7A-d_c.js";
3
3
  //#region src/parse-value.d.ts
4
4
  /** @internal */
5
5
  export declare class ValueParser {
@@ -1,6 +1,6 @@
1
- import { l as CSSDataArena } from "./arena-Bsf96CQK.js";
2
- import { t as CSSNode } from "./css-node-XDNQLChU.js";
3
- import { t as ValueNodeParser } from "./value-node-parser-Duyvn_ok.js";
1
+ import { l as CSSDataArena } from "./arena-CsJhQRUX.js";
2
+ import { t as CSSNode } from "./css-node-BKcf0ZeS.js";
3
+ import { t as ValueNodeParser } from "./value-node-parser-B568SwUx.js";
4
4
  //#region src/parse-value.ts
5
5
  /** @internal */
6
6
  var ValueParser = class {
package/dist/parse.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { l as CSSDataArena } from "./arena-DvT75bvK.js";
1
+ import { l as CSSDataArena } from "./arena-7RLvyGPB.js";
2
2
  import { CommentInfo } from "./tokenize.js";
3
- import { U as StyleSheet } from "./node-types-ZcWS9nNe.js";
3
+ import { U as StyleSheet } from "./node-types-CD7A-d_c.js";
4
4
  //#region src/parse.d.ts
5
5
  export interface ParserOptions {
6
6
  parse_values?: boolean;
package/dist/parse.js CHANGED
@@ -1,8 +1,8 @@
1
- import { t as Lexer } from "./tokenize-CduZS6FG.js";
1
+ import { t as Lexer } from "./tokenize-B2QSzdrM.js";
2
2
  import "./token-types.js";
3
- import { l as CSSDataArena } from "./arena-Bsf96CQK.js";
4
- import { t as CSSNode } from "./css-node-XDNQLChU.js";
5
- import { a as skip_whitespace_and_comments_backward, o as skip_whitespace_and_comments_forward, t as SelectorParser } from "./parse-selector-BJJ4gjdB.js";
3
+ import { l as CSSDataArena } from "./arena-CsJhQRUX.js";
4
+ import { t as CSSNode } from "./css-node-BKcf0ZeS.js";
5
+ import { a as skip_whitespace_and_comments_backward, o as skip_whitespace_and_comments_forward, t as SelectorParser } from "./parse-selector-DORsuJvq.js";
6
6
  import { AtRulePreludeParser } from "./parse-atrule-prelude.js";
7
7
  import { DeclarationParser } from "./parse-declaration.js";
8
8
  //#region src/parse.ts
@@ -257,18 +257,6 @@ var Lexer = class {
257
257
  column: comment_column
258
258
  });
259
259
  }
260
- /**
261
- * Fast-forward to the next `target` char that isn't inside a string or comment, without
262
- * fully tokenizing what's in between — used by callers that only need to know *where a
263
- * construct ends* (e.g. a selector's `{`), since the sub-parser for that construct
264
- * re-tokenizes the span properly afterward.
265
- *
266
- * Strings go through consume_string and comments through _skip_comment, so escapes and
267
- * on_comment callbacks behave identically to real tokenization. Ordinary characters cost
268
- * one SKIP_SCAN_INTERESTING lookup each, same as consume_ident_or_function's inner loop.
269
- *
270
- * Returns true with this.pos at target, or false with this.pos at source_length.
271
- */
272
260
  skip_to_unquoted(target) {
273
261
  const source = this.source;
274
262
  const source_length = source.length;
@@ -305,13 +293,6 @@ var Lexer = class {
305
293
  this.pos = pos;
306
294
  return false;
307
295
  }
308
- /**
309
- * Same approach as skip_to_unquoted, but stops at any of `; { } ( ) !` — the stop
310
- * characters parse-declaration.ts's value scan needs (paren depth, statement end,
311
- * !important). The caller decides what each one means; this only locates the next one.
312
- *
313
- * Returns the stop character's code (this.pos left at it), or 0 at `end` if none found.
314
- */
315
296
  skip_to_declaration_stop(end) {
316
297
  const source = this.source;
317
298
  let pos = this.pos;
@@ -619,10 +600,6 @@ var Lexer = class {
619
600
  this.token_column = column;
620
601
  return type;
621
602
  }
622
- /**
623
- * Save complete lexer state for backtracking
624
- * @returns Object containing all lexer state
625
- */
626
603
  save_position() {
627
604
  return {
628
605
  pos: this.pos,
@@ -648,10 +625,6 @@ var Lexer = class {
648
625
  target.token_column = this.token_column;
649
626
  return target;
650
627
  }
651
- /**
652
- * Restore lexer state from saved position
653
- * @param saved The saved position to restore
654
- */
655
628
  restore_position(saved) {
656
629
  this.pos = saved.pos;
657
630
  this._line = saved.line;
@@ -662,10 +635,6 @@ var Lexer = class {
662
635
  this.token_line = saved.token_line;
663
636
  this.token_column = saved.token_column;
664
637
  }
665
- /**
666
- * Skip whitespace and comments within a range, maintaining line/column tracking
667
- * @param end The end boundary (exclusive)
668
- */
669
638
  skip_whitespace_in_range(end) {
670
639
  while (this.pos < end) {
671
640
  const ch = this.source.charCodeAt(this.pos);
@@ -37,26 +37,7 @@ export declare class Lexer {
37
37
  next_token(skip_whitespace?: boolean): TokenType;
38
38
  private _scan_newlines;
39
39
  private _skip_comment;
40
- /**
41
- * Fast-forward to the next `target` char that isn't inside a string or comment, without
42
- * fully tokenizing what's in between — used by callers that only need to know *where a
43
- * construct ends* (e.g. a selector's `{`), since the sub-parser for that construct
44
- * re-tokenizes the span properly afterward.
45
- *
46
- * Strings go through consume_string and comments through _skip_comment, so escapes and
47
- * on_comment callbacks behave identically to real tokenization. Ordinary characters cost
48
- * one SKIP_SCAN_INTERESTING lookup each, same as consume_ident_or_function's inner loop.
49
- *
50
- * Returns true with this.pos at target, or false with this.pos at source_length.
51
- */
52
40
  skip_to_unquoted(target: number): boolean;
53
- /**
54
- * Same approach as skip_to_unquoted, but stops at any of `; { } ( ) !` — the stop
55
- * characters parse-declaration.ts's value scan needs (paren depth, statement end,
56
- * !important). The caller decides what each one means; this only locates the next one.
57
- *
58
- * Returns the stop character's code (this.pos left at it), or 0 at `end` if none found.
59
- */
60
41
  skip_to_declaration_stop(end: number): number;
61
42
  consume_whitespace(start_line: number, start_column: number): TokenType;
62
43
  consume_string(quote: number, start_line: number, start_column: number): TokenType;
@@ -69,21 +50,9 @@ export declare class Lexer {
69
50
  advance(count?: number): void;
70
51
  peek(offset?: number): number;
71
52
  make_token(type: TokenType, start: number, end: number, line?: number, column?: number): TokenType;
72
- /**
73
- * Save complete lexer state for backtracking
74
- * @returns Object containing all lexer state
75
- */
76
53
  save_position(): LexerPosition;
77
54
  save_position_into(target: LexerPosition): LexerPosition;
78
- /**
79
- * Restore lexer state from saved position
80
- * @param saved The saved position to restore
81
- */
82
55
  restore_position(saved: LexerPosition): void;
83
- /**
84
- * Skip whitespace and comments within a range, maintaining line/column tracking
85
- * @param end The end boundary (exclusive)
86
- */
87
56
  skip_whitespace_in_range(end: number): void;
88
57
  }
89
58
  /**
package/dist/tokenize.js CHANGED
@@ -1 +1 @@
1
- export { t as Lexer, n as tokenize } from "./tokenize-CduZS6FG.js";
1
+ export { t as Lexer, n as tokenize } from "./tokenize-B2QSzdrM.js";
@@ -1,8 +1,8 @@
1
- import { t as Lexer } from "./tokenize-CduZS6FG.js";
1
+ import { t as Lexer } from "./tokenize-B2QSzdrM.js";
2
2
  import "./token-types.js";
3
- import "./arena-Bsf96CQK.js";
3
+ import "./arena-CsJhQRUX.js";
4
4
  import { c as str_equals_range, t as is_and_or_not_range } from "./string-utils-aEEsA2CS.js";
5
- import { i as find_colon_at_depth_zero, o as skip_whitespace_and_comments_forward, s as trim_boundaries, t as SelectorParser } from "./parse-selector-BJJ4gjdB.js";
5
+ import { i as find_colon_at_depth_zero, o as skip_whitespace_and_comments_forward, s as trim_boundaries, t as SelectorParser } from "./parse-selector-DORsuJvq.js";
6
6
  //#region src/parse-condition.ts
7
7
  var ConditionParser = class {
8
8
  lexer;
@@ -33,15 +33,28 @@ var ConditionParser = class {
33
33
  }
34
34
  paren_content_end = 0;
35
35
  paren_close_end = 0;
36
+ unmatched_opens = null;
37
+ unmatched_opens_end = 0;
36
38
  scan_matching_paren() {
37
39
  let depth = 1;
38
40
  let content_end = this.lexer.pos;
39
41
  let close_end = this.lexer.token_end;
42
+ if (this.unmatched_opens !== null && this.unmatched_opens_end === this.end) {
43
+ if (this.unmatched_opens.has(this.lexer.token_start)) {
44
+ this.paren_content_end = content_end;
45
+ this.paren_close_end = close_end;
46
+ return false;
47
+ }
48
+ }
49
+ const open_stack = [this.lexer.token_start];
40
50
  while (this.lexer.pos < this.end && depth > 0) {
41
51
  const token_type = this.next_token();
42
- if (token_type === 21 || token_type === 2) depth++;
43
- else if (token_type === 22) {
52
+ if (token_type === 21 || token_type === 2) {
53
+ depth++;
54
+ open_stack.push(this.lexer.token_start);
55
+ } else if (token_type === 22) {
44
56
  depth--;
57
+ open_stack.pop();
45
58
  if (depth === 0) {
46
59
  content_end = this.lexer.token_start;
47
60
  close_end = this.lexer.token_end;
@@ -50,7 +63,15 @@ var ConditionParser = class {
50
63
  }
51
64
  this.paren_content_end = content_end;
52
65
  this.paren_close_end = close_end;
53
- return depth === 0;
66
+ if (depth !== 0) {
67
+ if (this.unmatched_opens === null || this.unmatched_opens_end !== this.end) {
68
+ this.unmatched_opens = /* @__PURE__ */ new Set();
69
+ this.unmatched_opens_end = this.end;
70
+ }
71
+ for (const pos of open_stack) this.unmatched_opens.add(pos);
72
+ return false;
73
+ }
74
+ return true;
54
75
  }
55
76
  parse_feature_value(start, end) {
56
77
  return this.value_node_parser.parse_chain(start, end, this.lexer.line, this.lexer.column);
package/dist/walk.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { s as CSSNode, t as AnyNode } from "./node-types-ZcWS9nNe.js";
1
+ import { s as CSSNode, t as AnyNode } from "./node-types-CD7A-d_c.js";
2
2
  //#region src/walk.d.ts
3
3
  export declare const SKIP: unique symbol;
4
4
  export declare const BREAK: unique symbol;
package/dist/walk.js CHANGED
@@ -1,5 +1,5 @@
1
- import "./arena-Bsf96CQK.js";
2
- import { t as CSSNode } from "./css-node-XDNQLChU.js";
1
+ import "./arena-CsJhQRUX.js";
2
+ import { t as CSSNode } from "./css-node-BKcf0ZeS.js";
3
3
  import "./constants.js";
4
4
  //#region src/walk.ts
5
5
  const SKIP = Symbol("SKIP");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-parser",
3
- "version": "0.19.0",
3
+ "version": "0.19.2",
4
4
  "description": "High-performance CSS lexer and parser, optimized for CSS inspection and analysis",
5
5
  "keywords": [
6
6
  "ast",