Skip to content

Commit cf3bfe8

Browse files
committed
breaking: add structured prelude property to AT_RULE and STYLE_RULE
1 parent 38bf8e5 commit cf3bfe8

File tree

2 files changed

+38
-16
lines changed

2 files changed

+38
-16
lines changed

src/api.test.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,17 @@ describe('CSSNode', () => {
184184
expect(hasPrelude).toBe(true)
185185
})
186186

187+
test('should return true for style rules with selectors', () => {
188+
const source = 'body { color: red; }'
189+
const root = parse(source)
190+
const rule = root.first_child!
191+
192+
expect(rule.type).toBe(STYLE_RULE)
193+
expect(rule.has_prelude).toBe(true)
194+
expect(rule.prelude).not.toBeNull()
195+
expect(rule.prelude?.type_name).toBe('SelectorList')
196+
})
197+
187198
test('should work for other node types that use value field', () => {
188199
const source = 'body { color: red; }'
189200
const root = parse(source, { parse_values: false })
@@ -193,12 +204,9 @@ describe('CSSNode', () => {
193204
const declaration = block.first_child!
194205

195206
// Rules and selectors don't use value field
196-
expect(rule.has_prelude).toBe(false)
197-
expect(selector.has_prelude).toBe(false)
207+
expect(rule.has_prelude).toBe(true) // has selector
198208

199-
// Declarations use value field for their value (same arena fields as prelude)
200-
// So has_prelude returns true for declarations with values
201-
expect(declaration.has_prelude).toBe(true)
209+
expect(declaration.has_prelude).toBe(false)
202210
expect(declaration.value).toBe('red')
203211
})
204212
})

src/css-node.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export type PlainCSSNode = {
165165
property?: string
166166
value?: string | number | null
167167
unit?: string
168-
prelude?: string
168+
prelude?: PlainCSSNode | null
169169

170170
// Flags (only when true)
171171
is_important?: boolean
@@ -307,16 +307,24 @@ export class CSSNode {
307307
}
308308

309309
/**
310-
* Get the prelude node (for at-rules: structured prelude with media queries, layer names, etc.)
311-
* Returns the AT_RULE_PRELUDE wrapper node containing prelude children, or null if no prelude
310+
* Get the prelude node:
311+
* - For at-rules: AT_RULE_PRELUDE wrapper containing structured prelude children (media queries, layer names, etc.)
312+
* - For style rules: SELECTOR_LIST or SELECTOR node
313+
* Returns null if no prelude exists
312314
*/
313-
get prelude(): CSSNode | null {
314-
if (this.type !== AT_RULE) return null
315-
let first = this.first_child
316-
if (first && first.type === AT_RULE_PRELUDE) {
317-
return first
315+
get prelude(): CSSNode | null | undefined {
316+
if (this.type === AT_RULE) {
317+
let first = this.first_child
318+
if (first && first.type === AT_RULE_PRELUDE) {
319+
return first
320+
}
321+
return null
318322
}
319-
return null
323+
if (this.type === STYLE_RULE) {
324+
// For style rules, prelude is the selector (first child)
325+
return this.first_child
326+
}
327+
return undefined
320328
}
321329

322330
/**
@@ -382,9 +390,15 @@ export class CSSNode {
382390
return this.arena.has_flag(this.index, FLAG_HAS_ERROR)
383391
}
384392

385-
/** Check if this at-rule has a prelude */
393+
/** Check if this node has a prelude (at-rules and style rules) */
386394
get has_prelude(): boolean {
387-
return this.arena.get_value_length(this.index) > 0
395+
if (this.type === AT_RULE) {
396+
return this.arena.get_value_length(this.index) > 0
397+
}
398+
if (this.type === STYLE_RULE) {
399+
return this.first_child !== null
400+
}
401+
return false
388402
}
389403

390404
/** Check if this rule has a block { } */

0 commit comments

Comments
 (0)