Skip to content

Commit 725cbb7

Browse files
committed
feat: add has_declarations convenience property
1 parent 02f940a commit 725cbb7

File tree

4 files changed

+61
-0
lines changed

4 files changed

+61
-0
lines changed

src/arena.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export const FLAG_HAS_ERROR = 1 << 1 // Syntax error
7070
export const FLAG_LENGTH_OVERFLOW = 1 << 2 // Node > 65k chars
7171
export const FLAG_HAS_BLOCK = 1 << 3 // Has { } block (for style rules and at-rules)
7272
export const FLAG_VENDOR_PREFIXED = 1 << 4 // Has vendor prefix (-webkit-, -moz-, -ms-, -o-)
73+
export const FLAG_HAS_DECLARATIONS = 1 << 5 // Has declarations (for style rules)
7374

7475
export class CSSDataArena {
7576
private buffer: ArrayBuffer

src/css-node.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,4 +336,56 @@ describe('CSSNode', () => {
336336
expect(keyframes.has_block).toBe(true)
337337
})
338338
})
339+
340+
describe('has_declarations', () => {
341+
test('should return true for style rules with declarations', () => {
342+
const source = 'body { color: red; margin: 0; }'
343+
const parser = new Parser(source)
344+
const root = parser.parse()
345+
const rule = root.first_child!
346+
347+
expect(rule.type).toBe(NODE_STYLE_RULE)
348+
expect(rule.has_declarations).toBe(true)
349+
})
350+
351+
test('should return false for empty style rules', () => {
352+
const source = 'body { }'
353+
const parser = new Parser(source)
354+
const root = parser.parse()
355+
const rule = root.first_child!
356+
357+
expect(rule.type).toBe(NODE_STYLE_RULE)
358+
expect(rule.has_declarations).toBe(false)
359+
})
360+
361+
test('should return false for style rules with only nested rules', () => {
362+
const source = 'body { .nested { color: red; } }'
363+
const parser = new Parser(source)
364+
const root = parser.parse()
365+
const rule = root.first_child!
366+
367+
expect(rule.type).toBe(NODE_STYLE_RULE)
368+
expect(rule.has_declarations).toBe(false)
369+
})
370+
371+
test('should return true for style rules with both declarations and nested rules', () => {
372+
const source = 'body { color: blue; .nested { margin: 0; } }'
373+
const parser = new Parser(source)
374+
const root = parser.parse()
375+
const rule = root.first_child!
376+
377+
expect(rule.type).toBe(NODE_STYLE_RULE)
378+
expect(rule.has_declarations).toBe(true)
379+
})
380+
381+
test('should return false for at-rules', () => {
382+
const source = '@media screen { body { color: red; } }'
383+
const parser = new Parser(source)
384+
const root = parser.parse()
385+
const media = root.first_child!
386+
387+
expect(media.type).toBe(NODE_AT_RULE)
388+
expect(media.has_declarations).toBe(false)
389+
})
390+
})
339391
})

src/css-node.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
FLAG_HAS_ERROR,
4040
FLAG_HAS_BLOCK,
4141
FLAG_VENDOR_PREFIXED,
42+
FLAG_HAS_DECLARATIONS,
4243
} from './arena'
4344

4445
// Node type constants (numeric for performance)
@@ -159,6 +160,11 @@ export class CSSNode {
159160
return this.arena.has_flag(this.index, FLAG_HAS_BLOCK)
160161
}
161162

163+
// Check if this style rule has declarations
164+
get has_declarations(): boolean {
165+
return this.arena.has_flag(this.index, FLAG_HAS_DECLARATIONS)
166+
}
167+
162168
// --- Value Node Access (for declarations) ---
163169

164170
// Get array of parsed value nodes (for declarations only)

src/parser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
FLAG_IMPORTANT,
1111
FLAG_HAS_BLOCK,
1212
FLAG_VENDOR_PREFIXED,
13+
FLAG_HAS_DECLARATIONS,
1314
} from './arena'
1415
import { CSSNode } from './css-node'
1516
import { ValueParser } from './value-parser'
@@ -186,6 +187,7 @@ export class Parser {
186187
// Try to parse as declaration first
187188
let declaration = this.parse_declaration()
188189
if (declaration !== null) {
190+
this.arena.set_flag(style_rule, FLAG_HAS_DECLARATIONS)
189191
this.arena.append_child(style_rule, declaration)
190192
continue
191193
}

0 commit comments

Comments
 (0)