@@ -46,6 +46,8 @@ function parse(source: string, options?: ParserOptions): CSSNode
4646- `has_prelude` - Whether at-rule has a prelude
4747- `has_block` - Whether rule has a `{ }` block
4848- ` has_children ` - Whether node has child nodes
49+ - ` block ` - Block node containing declarations/nested rules (for style rules and at-rules with blocks)
50+ - ` is_empty ` - Whether block has no declarations or rules (only comments allowed)
4951- ` first_child ` - First child node or ` null `
5052- ` next_sibling ` - Next sibling node or ` null `
5153- ` children ` - Array of all child nodes
@@ -69,7 +71,12 @@ console.log(rule.has_block) // true
6971const selector = rule .first_child
7072console .log (selector .text ) // "body"
7173
72- const declaration = selector .next_sibling
74+ // Access block, then declaration inside it
75+ const block = rule .block
76+ console .log (block .type ) // 7 (NODE_BLOCK)
77+ console .log (block .is_empty ) // false
78+
79+ const declaration = block .first_child
7380console .log (declaration .property ) // "color"
7481console .log (declaration .value ) // "red"
7582```
@@ -81,8 +88,9 @@ Stylesheet (NODE_STYLESHEET)
8188 └─ StyleRule (NODE_STYLE_RULE)
8289 ├─ SelectorList (NODE_SELECTOR_LIST) "body"
8390 │ └─ Type (NODE_SELECTOR_TYPE) "body"
84- └─ Declaration (NODE_DECLARATION) "color: red"
85- └─ Keyword (NODE_VALUE_KEYWORD) "red"
91+ └─ Block (NODE_BLOCK)
92+ └─ Declaration (NODE_DECLARATION) "color: red"
93+ └─ Keyword (NODE_VALUE_KEYWORD) "red"
8694```
8795
8896### Example 2: Parsing with Options
@@ -97,8 +105,8 @@ const ast = parse('div { margin: 10px 20px; }', {
97105})
98106
99107const rule = ast .first_child
100- const selector = rule .first_child
101- const declaration = selector . next_sibling
108+ const block = rule .block
109+ const declaration = block . first_child
102110
103111console .log (declaration .property ) // "margin"
104112console .log (declaration .value ) // "10px 20px"
@@ -120,13 +128,15 @@ console.log(mediaRule.has_block) // true
120128console .log (mediaRule .has_children ) // true
121129
122130// Access prelude nodes when parse_atrule_preludes is true
131+ // (Prelude nodes are first children, before the block)
123132const mediaQuery = mediaRule .first_child
124133console .log (mediaQuery .type ) // NODE_PRELUDE_MEDIA_QUERY
125134console .log (mediaQuery .text ) // "(min-width: 768px)"
126135console .log (mediaQuery .value ) // "min-width: 768px" (without parentheses)
127136
128- // Access block content
129- for (const child of mediaRule ) {
137+ // Access block content (nested rules/declarations)
138+ const block = mediaRule .block
139+ for (const child of block ) {
130140 if (child .type === 2 ) {
131141 // NODE_STYLE_RULE
132142 console .log (' Found style rule in media query' )
@@ -218,6 +228,37 @@ const firstNode = ast2.first_child
218228console .log (firstNode .type ) // NODE_STYLE_RULE (comment skipped)
219229```
220230
231+ ### Example 7: Block Nodes and Empty Rules
232+
233+ ``` typescript
234+ import { parse } from ' @projectwallace/css-parser'
235+
236+ // Empty rule
237+ const ast1 = parse (' .empty { }' )
238+ const rule1 = ast1 .first_child
239+ console .log (rule1 .has_block ) // true
240+ console .log (rule1 .block .is_empty ) // true
241+
242+ // Rule with only comments
243+ const ast2 = parse (' .comments { /* todo */ }' , { skip_comments: false })
244+ const rule2 = ast2 .first_child
245+ console .log (rule2 .block .is_empty ) // true (only comments)
246+
247+ // Rule with declarations
248+ const ast3 = parse (' .filled { color: red; }' )
249+ const rule3 = ast3 .first_child
250+ console .log (rule3 .block .is_empty ) // false
251+
252+ // Nested rules inside blocks
253+ const ast4 = parse (' .parent { .child { color: blue; } }' )
254+ const parent = ast4 .first_child
255+ const parentBlock = parent .block
256+ const nestedRule = parentBlock .first_child
257+
258+ console .log (nestedRule .type ) // NODE_STYLE_RULE
259+ console .log (nestedRule .block .is_empty ) // false
260+ ```
261+
221262---
222263
223264## ` parse_selector(source) `
@@ -294,8 +335,9 @@ walk(ast, (node, depth) => {
294335// NODE_STYLE_RULE
295336// NODE_SELECTOR_LIST
296337// NODE_SELECTOR_TYPE
297- // NODE_DECLARATION
298- // NODE_VALUE_KEYWORD
338+ // NODE_BLOCK
339+ // NODE_DECLARATION
340+ // NODE_VALUE_KEYWORD
299341` ` `
300342
301343-- -
@@ -328,3 +370,69 @@ for (const token of tokenize('body { color: red; }')) {
328370// TOKEN_WHITESPACE " "
329371// TOKEN_RIGHT_BRACE "}"
330372` ` `
373+
374+ -- -
375+
376+ ## Node Type Constants
377+
378+ The parser uses numeric constants for node types . Import them from the parser :
379+
380+ ` ` ` typescript
381+ import {
382+ NODE_STYLESHEET,
383+ NODE_STYLE_RULE,
384+ NODE_AT_RULE,
385+ NODE_DECLARATION,
386+ NODE_SELECTOR,
387+ NODE_COMMENT,
388+ NODE_BLOCK,
389+ // ... and more
390+ } from '@projectwallace/css-parser'
391+ ` ` `
392+
393+ ### Core Node Types
394+
395+ - ` NODE_STYLESHEET ` (1 ) - Root stylesheet node
396+ - ` NODE_STYLE_RULE ` (2 ) - Style rule (e .g ., ` body { } ` )
397+ - ` NODE_AT_RULE ` (3 ) - At - rule (e .g ., ` @media ` , ` @keyframes ` )
398+ - ` NODE_DECLARATION ` (4 ) - Property declaration (e .g ., ` color: red ` )
399+ - ` NODE_SELECTOR ` (5 ) - Selector wrapper (deprecated , use NODE_SELECTOR_LIST )
400+ - ` NODE_COMMENT ` (6 ) - CSS comment
401+ - ` NODE_BLOCK ` (7 ) - Block container for declarations and nested rules
402+
403+ ### Value Node Types (10 - 16 )
404+
405+ - ` NODE_VALUE_KEYWORD ` (10 ) - Keyword value (e .g ., ` red ` , ` auto ` )
406+ - ` NODE_VALUE_NUMBER ` (11 ) - Number value (e .g ., ` 42 ` , ` 3.14 ` )
407+ - ` NODE_VALUE_DIMENSION ` (12 ) - Dimension value (e .g ., ` 10px ` , ` 2em ` , ` 50% ` )
408+ - ` NODE_VALUE_STRING ` (13 ) - String value (e .g ., ` "hello" ` )
409+ - ` NODE_VALUE_COLOR ` (14 ) - Hex color (e .g ., ` #fff ` , ` #ff0000 ` )
410+ - ` NODE_VALUE_FUNCTION ` (15 ) - Function (e .g ., ` calc() ` , ` var() ` )
411+ - ` NODE_VALUE_OPERATOR ` (16 ) - Operator (e .g ., ` + ` , ` , ` )
412+
413+ ### Selector Node Types (20 - 29 )
414+
415+ - ` NODE_SELECTOR_LIST ` (20 ) - Selector list container
416+ - ` NODE_SELECTOR_TYPE ` (21 ) - Type selector (e .g ., ` div ` , ` span ` )
417+ - ` NODE_SELECTOR_CLASS ` (22 ) - Class selector (e .g ., ` .classname ` )
418+ - ` NODE_SELECTOR_ID ` (23 ) - ID selector (e .g ., ` #identifier ` )
419+ - ` NODE_SELECTOR_ATTRIBUTE ` (24 ) - Attribute selector (e .g ., ` [attr=value] ` )
420+ - ` NODE_SELECTOR_PSEUDO_CLASS ` (25 ) - Pseudo - class (e .g ., `:hover `)
421+ - `NODE_SELECTOR_PSEUDO_ELEMENT ` (26) - Pseudo -element (e .g ., `::before `)
422+ - `NODE_SELECTOR_COMBINATOR ` (27) - Combinator (e .g ., `>`, `+`, `~`, or ` `)
423+ - `NODE_SELECTOR_UNIVERSAL ` (28) - Universal selector (`*`)
424+ - `NODE_SELECTOR_NESTING ` (29) - Nesting selector (`&`)
425+
426+ ### At -Rule Prelude Node Types (30-40)
427+
428+ - `NODE_PRELUDE_MEDIA_QUERY ` (30) - Media query
429+ - `NODE_PRELUDE_MEDIA_FEATURE ` (31) - Media feature
430+ - `NODE_PRELUDE_MEDIA_TYPE ` (32) - Media type (e .g ., `screen `, `print `)
431+ - `NODE_PRELUDE_CONTAINER_QUERY ` (33) - Container query
432+ - `NODE_PRELUDE_SUPPORTS_QUERY ` (34) - Supports query
433+ - `NODE_PRELUDE_LAYER_NAME ` (35) - Layer name
434+ - `NODE_PRELUDE_IDENTIFIER ` (36) - Generic identifier
435+ - `NODE_PRELUDE_OPERATOR ` (37) - Logical operator (e .g ., `and `, `or `)
436+ - `NODE_PRELUDE_IMPORT_URL ` (38) - Import URL
437+ - `NODE_PRELUDE_IMPORT_LAYER ` (39) - Import layer
438+ - `NODE_PRELUDE_IMPORT_SUPPORTS ` (40) - Import supports condition
0 commit comments