diff --git a/API.md b/API.md index ea5f5d0..32380c6 100644 --- a/API.md +++ b/API.md @@ -31,8 +31,8 @@ function parse(source: string, options?: ParserOptions): CSSNode `CSSNode` - Root stylesheet node with the following properties: -- `type` - Node type constant (e.g., `NODE_STYLESHEET`, `NODE_STYLE_RULE`) -- `type_name` - Human-readable type name (e.g., `'stylesheet'`, `'style_rule'`) +- `type` - Node type constant (e.g., `STYLESHEET`, `STYLE_RULE`) +- `type_name` - CSSTree-compatible type name (e.g., `'StyleSheet'`, `'Rule'`) - `text` - Full text of the node from source - `name` - Property name, at-rule name, or layer name - `property` - Alias for `name` (for declarations) @@ -54,15 +54,15 @@ function parse(source: string, options?: ParserOptions): CSSNode - `children` - Array of all child nodes - `values` - Array of value nodes (for declarations) - `selector_list` - Selector list from pseudo-classes like `:is()`, `:not()`, `:has()`, `:where()`, or `:nth-child(of)` -- `nth` - An+B formula node from `:nth-child(of)` wrapper (for NODE_SELECTOR_NTH_OF nodes) -- `selector` - Selector list from `:nth-child(of)` wrapper (for NODE_SELECTOR_NTH_OF nodes) +- `nth` - An+B formula node from `:nth-child(of)` wrapper (for NTH_OF_SELECTOR nodes) +- `selector` - Selector list from `:nth-child(of)` wrapper (for NTH_OF_SELECTOR nodes) - `nth_a` - The 'a' coefficient from An+B expressions like `2n` from `:nth-child(2n+1)` - `nth_b` - The 'b' coefficient from An+B expressions like `+1` from `:nth-child(2n+1)` -- `compound_parts()` - Iterator over first compound selector parts (zero allocation, for NODE_SELECTOR) -- `first_compound` - Array of parts before first combinator (for NODE_SELECTOR) -- `all_compounds` - Array of compound arrays split by combinators (for NODE_SELECTOR) -- `is_compound` - Whether selector has no combinators (for NODE_SELECTOR) -- `first_compound_text` - Text of first compound selector (for NODE_SELECTOR) +- `compound_parts()` - Iterator over first compound selector parts (zero allocation, for SELECTOR) +- `first_compound` - Array of parts before first combinator (for SELECTOR) +- `all_compounds` - Array of compound arrays split by combinators (for SELECTOR) +- `is_compound` - Whether selector has no combinators (for SELECTOR) +- `first_compound_text` - Text of first compound selector (for SELECTOR) - `clone(options?)` - Clone node as a mutable plain object with children as arrays ### Example 1: Basic Parsing @@ -72,12 +72,12 @@ import { parse } from '@projectwallace/css-parser' const ast = parse('body { color: red; }') -console.log(ast.type) // 1 (NODE_STYLESHEET) +console.log(ast.type) // 1 (STYLESHEET) console.log(ast.has_children) // true console.log(ast.children.length) // 1 const rule = ast.first_child -console.log(rule.type) // 2 (NODE_STYLE_RULE) +console.log(rule.type) // 2 (STYLE_RULE) console.log(rule.has_block) // true const selector = rule.first_child @@ -85,7 +85,7 @@ console.log(selector.text) // "body" // Access block, then declaration inside it const block = rule.block -console.log(block.type) // 7 (NODE_BLOCK) +console.log(block.type) // 7 (BLOCK) console.log(block.is_empty) // false const declaration = block.first_child @@ -96,13 +96,13 @@ console.log(declaration.value) // "red" **Response structure:** ``` -Stylesheet (NODE_STYLESHEET) - └─ StyleRule (NODE_STYLE_RULE) - ├─ SelectorList (NODE_SELECTOR_LIST) "body" - │ └─ Type (NODE_SELECTOR_TYPE) "body" - └─ Block (NODE_BLOCK) - └─ Declaration (NODE_DECLARATION) "color: red" - └─ Keyword (NODE_VALUE_KEYWORD) "red" +Stylesheet (STYLESHEET) + └─ StyleRule (STYLE_RULE) + ├─ SelectorList (SELECTOR_LIST) "body" + │ └─ Type (TYPE_SELECTOR) "body" + └─ Block (BLOCK) + └─ Declaration (DECLARATION) "color: red" + └─ Keyword (IDENTIFIER) "red" ``` ### Example 2: Parsing with Options @@ -142,7 +142,7 @@ console.log(mediaRule.has_children) // true // Access prelude nodes when parse_atrule_preludes is true // (Prelude nodes are first children, before the block) const mediaQuery = mediaRule.first_child -console.log(mediaQuery.type) // NODE_PRELUDE_MEDIA_QUERY +console.log(mediaQuery.type) // MEDIA_QUERY console.log(mediaQuery.text) // "(min-width: 768px)" console.log(mediaQuery.value) // "min-width: 768px" (without parentheses) @@ -150,7 +150,7 @@ console.log(mediaQuery.value) // "min-width: 768px" (without parentheses) const block = mediaRule.block for (const child of block) { if (child.type === 2) { - // NODE_STYLE_RULE + // STYLE_RULE console.log('Found style rule in media query') } } @@ -172,18 +172,18 @@ console.log(importRule.has_children) // true (has prelude nodes) // Access parsed import components const [url, layer, supports, media] = importRule.children -console.log(url.type) // NODE_PRELUDE_IMPORT_URL +console.log(url.type) // URL console.log(url.text) // 'url("styles.css")' -console.log(layer.type) // NODE_PRELUDE_IMPORT_LAYER +console.log(layer.type) // LAYER_NAME console.log(layer.name) // "base" console.log(layer.text) // "layer(base)" -console.log(supports.type) // NODE_PRELUDE_IMPORT_SUPPORTS +console.log(supports.type) // SUPPORTS_QUERY console.log(supports.text) // "supports(display: flex)" console.log(supports.value) // "display: flex" (without supports() wrapper) -console.log(media.type) // NODE_PRELUDE_MEDIA_TYPE +console.log(media.type) // MEDIA_TYPE console.log(media.text) // "screen" ``` @@ -209,11 +209,9 @@ for (const rule of ast) { const rules = ast.children console.log(rules.length) // 3 -// Iterate using next_sibling -let node = ast.first_child -while (node) { - console.log(node.type) // 2, 2, 2 (all NODE_STYLE_RULE) - node = node.next_sibling +// Iterate using for...of (recommended) +for (const node of ast) { + console.log(node.type) // 2, 2, 2 (all STYLE_RULE) } ``` @@ -228,16 +226,16 @@ const ast = parse('/* header */ body { color: red; }', { }) const comment = ast.first_child -console.log(comment.type) // NODE_COMMENT +console.log(comment.type) // COMMENT console.log(comment.text) // "/* header */" const rule = comment.next_sibling -console.log(rule.type) // NODE_STYLE_RULE +console.log(rule.type) // STYLE_RULE // Skip comments (default) const ast2 = parse('/* header */ body { color: red; }') const firstNode = ast2.first_child -console.log(firstNode.type) // NODE_STYLE_RULE (comment skipped) +console.log(firstNode.type) // STYLE_RULE (comment skipped) ``` ### Example 7: Block Nodes and Empty Rules @@ -267,7 +265,7 @@ const parent = ast4.first_child const parentBlock = parent.block const nestedRule = parentBlock.first_child -console.log(nestedRule.type) // NODE_STYLE_RULE +console.log(nestedRule.type) // STYLE_RULE console.log(nestedRule.block.is_empty) // false ``` @@ -285,23 +283,23 @@ for (let node of ast) { console.log(`${node.type_name}: ${node.text}`) } // Output: -// style_rule: .foo { color: red; } -// selector_list: .foo -// selector_class: .foo -// block: color: red -// declaration: color: red -// value_keyword: red +// Rule: .foo { color: red; } +// SelectorList: .foo +// ClassSelector: .foo +// Block: color: red +// Declaration: color: red +// Identifier: red // Useful for logging and error messages const rule = ast.first_child -console.log(`Processing ${rule.type_name}`) // "Processing style_rule" +console.log(`Processing ${rule.type_name}`) // "Processing Rule" // TYPE_NAMES export for custom type checking -import { NODE_DECLARATION } from '@projectwallace/css-parser' -console.log(TYPE_NAMES[NODE_DECLARATION]) // 'declaration' +import { DECLARATION } from '@projectwallace/css-parser' +console.log(TYPE_NAMES[DECLARATION]) // 'Declaration' // Compare strings instead of numeric constants -if (node.type_name === 'declaration') { +if (node.type_name === 'Declaration') { console.log(`Property: ${node.property}, Value: ${node.value}`) } ``` @@ -311,7 +309,7 @@ if (node.type_name === 'declaration') { Convenience properties simplify access to nested selector data: ```typescript -import { parse_selector, NODE_SELECTOR_LIST, NODE_SELECTOR_NTH } from '@projectwallace/css-parser' +import { parse_selector, SELECTOR_LIST, NTH_SELECTOR } from '@projectwallace/css-parser' // Simple pseudo-classes with selectors const isSelector = parse_selector(':is(.foo, #bar)') @@ -319,15 +317,15 @@ const pseudo = isSelector.first_child?.first_child // Direct access to selector list console.log(pseudo.selector_list.text) // ".foo, #bar" -console.log(pseudo.selector_list.type === NODE_SELECTOR_LIST) // true +console.log(pseudo.selector_list.type === SELECTOR_LIST) // true // Complex pseudo-classes with An+B notation const nthSelector = parse_selector(':nth-child(2n+1 of .foo)') const nthPseudo = nthSelector.first_child?.first_child -const nthOf = nthPseudo.first_child // NODE_SELECTOR_NTH_OF +const nthOf = nthPseudo.first_child // NTH_OF_SELECTOR // Direct access to formula -console.log(nthOf.nth.type === NODE_SELECTOR_NTH) // true +console.log(nthOf.nth.type === NTH_SELECTOR) // true console.log(nthOf.nth.nth_a) // "2n" console.log(nthOf.nth.nth_b) // "+1" @@ -344,10 +342,10 @@ console.log(nthPseudo.selector_list.text) // ".foo" // Had to manually traverse to find selector list let child = pseudo.first_child while (child) { - if (child.type === NODE_SELECTOR_NTH_OF) { + if (child.type === NTH_OF_SELECTOR) { let inner = child.first_child while (inner) { - if (inner.type === NODE_SELECTOR_LIST) { + if (inner.type === SELECTOR_LIST) { processSelectors(inner) break } @@ -373,7 +371,7 @@ if (pseudo.selector_list) { Compound selectors (parts between combinators) can be extracted without reparsing: ```typescript -import { parse_selector, NODE_SELECTOR_ID, NODE_SELECTOR_CLASS, NODE_SELECTOR_TYPE } from '@projectwallace/css-parser' +import { parse_selector, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR } from '@projectwallace/css-parser' const root = parse_selector('div.container#app > p.text + span') const selector = root.first_child @@ -381,9 +379,9 @@ const selector = root.first_child // Hot path: Calculate specificity (zero allocations) let [id, cls, type] = [0, 0, 0] for (let part of selector.compound_parts()) { - if (part.type === NODE_SELECTOR_ID) id++ - else if (part.type === NODE_SELECTOR_CLASS) cls++ - else if (part.type === NODE_SELECTOR_TYPE) type++ + if (part.type === ID_SELECTOR) id++ + else if (part.type === CLASS_SELECTOR) cls++ + else if (part.type === TYPE_SELECTOR) type++ } console.log('Specificity:', [id, cls, type]) // [1, 1, 1] @@ -417,7 +415,7 @@ console.log('First text:', selector.first_compound_text) // "div.container#app" const compoundParts = [] let selectorPart = selector.first_child while (selectorPart) { - if (selectorPart.type === NODE_SELECTOR_COMBINATOR) break + if (selectorPart.type === COMBINATOR) break compoundParts.push(selectorPart) selectorPart = selectorPart.next_sibling } @@ -455,8 +453,8 @@ const marginDecl = block.first_child // Shallow clone (no children) const shallow = marginDecl.clone({ deep: false }) -console.log(shallow.type) // NODE_DECLARATION -console.log(shallow.type_name) // "declaration" +console.log(shallow.type) // DECLARATION +console.log(shallow.type_name) // "Declaration" console.log(shallow.property) // "margin" console.log(shallow.children) // [] (empty array) @@ -515,17 +513,17 @@ import { parse_selector } from '@projectwallace/css-parser' const selector = parse_selector('div.class > p#id::before') -console.log(selector.type) // NODE_SELECTOR_LIST +console.log(selector.type) // SELECTOR_LIST // Iterate over selector components for (const part of selector.first_child) { console.log(part.type, part.text) } -// NODE_SELECTOR_TYPE "div" -// NODE_SELECTOR_CLASS ".class" -// NODE_SELECTOR_COMBINATOR ">" -// NODE_SELECTOR_TYPE "p" -// NODE_SELECTOR_ID "#id" -// NODE_SELECTOR_PSEUDO_ELEMENT "::before" +// TYPE_SELECTOR "div" +// CLASS_SELECTOR ".class" +// COMBINATOR ">" +// TYPE_SELECTOR "p" +// ID_SELECTOR "#id" +// PSEUDO_ELEMENT_SELECTOR "::before" ``` --- @@ -546,7 +544,7 @@ import { parse_atrule_prelude } from '@projectwallace/css-parser' const nodes = parse_atrule_prelude('media', '(min-width: 768px)') console.log(nodes.length) // 1 -console.log(nodes[0].type) // NODE_PRELUDE_MEDIA_QUERY +console.log(nodes[0].type) // MEDIA_QUERY console.log(nodes[0].text) // "(min-width: 768px)" ``` @@ -570,13 +568,13 @@ const ast = parse('body { color: red; }') walk(ast, (node, depth) => { console.log(' '.repeat(depth) + node.type) }) -// NODE_STYLESHEET -// NODE_STYLE_RULE -// NODE_SELECTOR_LIST -// NODE_SELECTOR_TYPE -// NODE_BLOCK -// NODE_DECLARATION -// NODE_VALUE_KEYWORD +// STYLESHEET +// STYLE_RULE +// SELECTOR_LIST +// TYPE_SELECTOR +// BLOCK +// DECLARATION +// IDENTIFIER ``` --- @@ -618,63 +616,61 @@ The parser uses numeric constants for node types. Import them from the parser: ```typescript import { - NODE_STYLESHEET, - NODE_STYLE_RULE, - NODE_AT_RULE, - NODE_DECLARATION, - NODE_SELECTOR, - NODE_COMMENT, - NODE_BLOCK, + STYLESHEET, + STYLE_RULE, + AT_RULE, + DECLARATION, + SELECTOR, + COMMENT, + BLOCK, // ... and more } from '@projectwallace/css-parser' ``` ### Core Node Types -- `NODE_STYLESHEET` (1) - Root stylesheet node -- `NODE_STYLE_RULE` (2) - Style rule (e.g., `body { }`) -- `NODE_AT_RULE` (3) - At-rule (e.g., `@media`, `@keyframes`) -- `NODE_DECLARATION` (4) - Property declaration (e.g., `color: red`) -- `NODE_SELECTOR` (5) - Selector wrapper (deprecated, use NODE_SELECTOR_LIST) -- `NODE_COMMENT` (6) - CSS comment -- `NODE_BLOCK` (7) - Block container for declarations and nested rules - -### Value Node Types (10-16) - -- `NODE_VALUE_KEYWORD` (10) - Keyword value (e.g., `red`, `auto`) -- `NODE_VALUE_NUMBER` (11) - Number value (e.g., `42`, `3.14`) -- `NODE_VALUE_DIMENSION` (12) - Dimension value (e.g., `10px`, `2em`, `50%`) -- `NODE_VALUE_STRING` (13) - String value (e.g., `"hello"`) -- `NODE_VALUE_COLOR` (14) - Hex color (e.g., `#fff`, `#ff0000`) -- `NODE_VALUE_FUNCTION` (15) - Function (e.g., `calc()`, `var()`) -- `NODE_VALUE_OPERATOR` (16) - Operator (e.g., `+`, `,`) +- `STYLESHEET` (1) - Root stylesheet node +- `STYLE_RULE` (2) - Style rule (e.g., `body { }`) +- `AT_RULE` (3) - At-rule (e.g., `@media`, `@keyframes`) +- `DECLARATION` (4) - Property declaration (e.g., `color: red`) +- `SELECTOR` (5) - Selector wrapper (deprecated, use SELECTOR_LIST) +- `COMMENT` (6) - CSS comment +- `BLOCK` (7) - Block container for declarations and nested rules + +### Value Node Types (10-18) + +- `IDENTIFIER` (10) - Identifier/keyword value (e.g., `red`, `auto`, `inherit`), also used in at-rule preludes (keyframe names, @property names) +- `NUMBER` (11) - Number value (e.g., `42`, `3.14`) +- `DIMENSION` (12) - Dimension value (e.g., `10px`, `2em`, `50%`) +- `STRING` (13) - String value (e.g., `"hello"`) +- `HASH` (14) - Hex color (e.g., `#fff`, `#ff0000`) +- `FUNCTION` (15) - Function (e.g., `calc()`, `var()`) +- `OPERATOR` (16) - Operator (e.g., `+`, `,`) +- `PARENTHESIS` (17) - Parenthesized expression (e.g., `(100% - 50px)`) +- `URL` (18) - URL (e.g., `url("file.css")`, `url(image.png)`), used in values and @import preludes ### Selector Node Types (20-29) -- `NODE_SELECTOR_LIST` (20) - Selector list container -- `NODE_SELECTOR_TYPE` (21) - Type selector (e.g., `div`, `span`) -- `NODE_SELECTOR_CLASS` (22) - Class selector (e.g., `.classname`) -- `NODE_SELECTOR_ID` (23) - ID selector (e.g., `#identifier`) -- `NODE_SELECTOR_ATTRIBUTE` (24) - Attribute selector (e.g., `[attr=value]`) -- `NODE_SELECTOR_PSEUDO_CLASS` (25) - Pseudo-class (e.g., `:hover`) -- `NODE_SELECTOR_PSEUDO_ELEMENT` (26) - Pseudo-element (e.g., `::before`) -- `NODE_SELECTOR_COMBINATOR` (27) - Combinator (e.g., `>`, `+`, `~`, or ` `) -- `NODE_SELECTOR_UNIVERSAL` (28) - Universal selector (`*`) -- `NODE_SELECTOR_NESTING` (29) - Nesting selector (`&`) - -### At-Rule Prelude Node Types (30-40) - -- `NODE_PRELUDE_MEDIA_QUERY` (30) - Media query -- `NODE_PRELUDE_MEDIA_FEATURE` (31) - Media feature -- `NODE_PRELUDE_MEDIA_TYPE` (32) - Media type (e.g., `screen`, `print`) -- `NODE_PRELUDE_CONTAINER_QUERY` (33) - Container query -- `NODE_PRELUDE_SUPPORTS_QUERY` (34) - Supports query -- `NODE_PRELUDE_LAYER_NAME` (35) - Layer name -- `NODE_PRELUDE_IDENTIFIER` (36) - Generic identifier -- `NODE_PRELUDE_OPERATOR` (37) - Logical operator (e.g., `and`, `or`) -- `NODE_PRELUDE_IMPORT_URL` (38) - Import URL -- `NODE_PRELUDE_IMPORT_LAYER` (39) - Import layer -- `NODE_PRELUDE_IMPORT_SUPPORTS` (40) - Import supports condition +- `SELECTOR_LIST` (20) - Selector list container +- `TYPE_SELECTOR` (21) - Type selector (e.g., `div`, `span`) +- `CLASS_SELECTOR` (22) - Class selector (e.g., `.classname`) +- `ID_SELECTOR` (23) - ID selector (e.g., `#identifier`) +- `ATTRIBUTE_SELECTOR` (24) - Attribute selector (e.g., `[attr=value]`) +- `PSEUDO_CLASS_SELECTOR` (25) - Pseudo-class (e.g., `:hover`) +- `PSEUDO_ELEMENT_SELECTOR` (26) - Pseudo-element (e.g., `::before`) +- `COMBINATOR` (27) - Combinator (e.g., `>`, `+`, `~`, or ` `) +- `UNIVERSAL_SELECTOR` (28) - Universal selector (`*`) +- `NESTING_SELECTOR` (29) - Nesting selector (`&`) + +### At-Rule Prelude Node Types (32-38) + +- `MEDIA_QUERY` (32) - Media query +- `MEDIA_FEATURE` (33) - Media feature +- `MEDIA_TYPE` (34) - Media type (e.g., `screen`, `print`) +- `CONTAINER_QUERY` (35) - Container query +- `SUPPORTS_QUERY` (36) - Supports query (used in @supports and @import) +- `LAYER_NAME` (37) - Layer name (used in @layer and @import) +- `PRELUDE_OPERATOR` (38) - Logical operator (e.g., `and`, `or`) ## Pseudo-Class Function Syntax Detection @@ -735,12 +731,12 @@ Use these constants with the `node.attr_flags` property to identify case sensiti #### Example ```javascript -import { parse_selector, NODE_SELECTOR_ATTRIBUTE, ATTR_OPERATOR_EQUAL, ATTR_FLAG_CASE_INSENSITIVE } from '@projectwallace/css-parser' +import { parse_selector, ATTRIBUTE_SELECTOR, ATTR_OPERATOR_EQUAL, ATTR_FLAG_CASE_INSENSITIVE } from '@projectwallace/css-parser' const ast = parse_selector('[type="text" i]') for (let node of ast) { - if (node.type === NODE_SELECTOR_ATTRIBUTE) { + if (node.type === ATTRIBUTE_SELECTOR) { console.log(node.attr_operator === ATTR_OPERATOR_EQUAL) // true console.log(node.attr_flags === ATTR_FLAG_CASE_INSENSITIVE) // true } diff --git a/src/arena.test.ts b/src/arena.test.ts index bc8ec6e..23ef8f7 100644 --- a/src/arena.test.ts +++ b/src/arena.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'vitest' -import { CSSDataArena, NODE_STYLESHEET, NODE_DECLARATION, FLAG_IMPORTANT, FLAG_HAS_ERROR } from './arena' +import { CSSDataArena, STYLESHEET, DECLARATION, FLAG_IMPORTANT, FLAG_HAS_ERROR } from './arena' describe('CSSDataArena', () => { describe('initialization', () => { @@ -51,23 +51,23 @@ describe('CSSDataArena', () => { const node2 = arena.create_node() // Set data on existing nodes - arena.set_type(node1, NODE_STYLESHEET) + arena.set_type(node1, STYLESHEET) arena.set_start_offset(node1, 100) - arena.set_type(node2, NODE_DECLARATION) + arena.set_type(node2, DECLARATION) arena.set_start_offset(node2, 200) // Trigger growth const node3 = arena.create_node() // Verify old data is preserved - expect(arena.get_type(node1)).toBe(NODE_STYLESHEET) + expect(arena.get_type(node1)).toBe(STYLESHEET) expect(arena.get_start_offset(node1)).toBe(100) - expect(arena.get_type(node2)).toBe(NODE_DECLARATION) + expect(arena.get_type(node2)).toBe(DECLARATION) expect(arena.get_start_offset(node2)).toBe(200) // Verify new node can be used - arena.set_type(node3, NODE_STYLESHEET) - expect(arena.get_type(node3)).toBe(NODE_STYLESHEET) + arena.set_type(node3, STYLESHEET) + expect(arena.get_type(node3)).toBe(STYLESHEET) }) }) @@ -86,8 +86,8 @@ describe('CSSDataArena', () => { const arena = new CSSDataArena(10) const node = arena.create_node() - arena.set_type(node, NODE_STYLESHEET) - expect(arena.get_type(node)).toBe(NODE_STYLESHEET) + arena.set_type(node, STYLESHEET) + expect(arena.get_type(node)).toBe(STYLESHEET) }) test('should write and read node flags', () => { @@ -102,7 +102,7 @@ describe('CSSDataArena', () => { const arena = new CSSDataArena(10) const node = arena.create_node() - arena.set_type(node, NODE_DECLARATION) + arena.set_type(node, DECLARATION) arena.set_flags(node, FLAG_IMPORTANT) arena.set_start_offset(node, 100) arena.set_length(node, 50) @@ -110,7 +110,7 @@ describe('CSSDataArena', () => { arena.set_content_length(node, 30) arena.set_start_line(node, 5) - expect(arena.get_type(node)).toBe(NODE_DECLARATION) + expect(arena.get_type(node)).toBe(DECLARATION) expect(arena.get_flags(node)).toBe(FLAG_IMPORTANT) expect(arena.get_start_offset(node)).toBe(100) expect(arena.get_length(node)).toBe(50) @@ -124,15 +124,15 @@ describe('CSSDataArena', () => { const node1 = arena.create_node() const node2 = arena.create_node() - arena.set_type(node1, NODE_STYLESHEET) + arena.set_type(node1, STYLESHEET) arena.set_start_offset(node1, 0) - arena.set_type(node2, NODE_DECLARATION) + arena.set_type(node2, DECLARATION) arena.set_start_offset(node2, 100) - expect(arena.get_type(node1)).toBe(NODE_STYLESHEET) + expect(arena.get_type(node1)).toBe(STYLESHEET) expect(arena.get_start_offset(node1)).toBe(0) - expect(arena.get_type(node2)).toBe(NODE_DECLARATION) + expect(arena.get_type(node2)).toBe(DECLARATION) expect(arena.get_start_offset(node2)).toBe(100) }) }) diff --git a/src/arena.ts b/src/arena.ts index 2f00d36..27a631f 100644 --- a/src/arena.ts +++ b/src/arena.ts @@ -23,51 +23,48 @@ let BYTES_PER_NODE = 40 // Node type constants -export const NODE_STYLESHEET = 1 -export const NODE_STYLE_RULE = 2 -export const NODE_AT_RULE = 3 -export const NODE_DECLARATION = 4 -export const NODE_SELECTOR = 5 -export const NODE_COMMENT = 6 -export const NODE_BLOCK = 7 // Block container for declarations and nested rules +export const STYLESHEET = 1 +export const STYLE_RULE = 2 +export const AT_RULE = 3 +export const DECLARATION = 4 +export const SELECTOR = 5 +export const COMMENT = 6 +export const BLOCK = 7 // Block container for declarations and nested rules // Value node type constants (for declaration values) -export const NODE_VALUE_KEYWORD = 10 // identifier: red, auto, inherit -export const NODE_VALUE_NUMBER = 11 // number: 42, 3.14, -5 -export const NODE_VALUE_DIMENSION = 12 // number with unit: 10px, 2em, 50% -export const NODE_VALUE_STRING = 13 // quoted string: "hello", 'world' -export const NODE_VALUE_COLOR = 14 // hex color: #fff, #ff0000 -export const NODE_VALUE_FUNCTION = 15 // function: calc(), var(), url() -export const NODE_VALUE_OPERATOR = 16 // operator: +, -, *, /, comma -export const NODE_VALUE_PARENTHESIS = 17 // parenthesized expression: (100% - 50px) +export const IDENTIFIER = 10 // identifier: red, auto, inherit +export const NUMBER = 11 // number: 42, 3.14, -5 +export const DIMENSION = 12 // number with unit: 10px, 2em, 50% +export const STRING = 13 // quoted string: "hello", 'world' +export const HASH = 14 // hex color: #fff, #ff0000 +export const FUNCTION = 15 // function: calc(), var() +export const OPERATOR = 16 // operator: +, -, *, /, comma +export const PARENTHESIS = 17 // parenthesized expression: (100% - 50px) +export const URL = 18 // URL: url("file.css"), url(image.png), used in values and @import // Selector node type constants (for detailed selector parsing) -export const NODE_SELECTOR_LIST = 20 // comma-separated selectors -export const NODE_SELECTOR_TYPE = 21 // type selector: div, span, p -export const NODE_SELECTOR_CLASS = 22 // class selector: .classname -export const NODE_SELECTOR_ID = 23 // ID selector: #identifier -export const NODE_SELECTOR_ATTRIBUTE = 24 // attribute selector: [attr], [attr=value] -export const NODE_SELECTOR_PSEUDO_CLASS = 25 // pseudo-class: :hover, :nth-child() -export const NODE_SELECTOR_PSEUDO_ELEMENT = 26 // pseudo-element: ::before, ::after -export const NODE_SELECTOR_COMBINATOR = 27 // combinator: >, +, ~, space -export const NODE_SELECTOR_UNIVERSAL = 28 // universal selector: * -export const NODE_SELECTOR_NESTING = 29 // nesting selector: & -export const NODE_SELECTOR_NTH = 30 // An+B expression: 2n+1, odd, even -export const NODE_SELECTOR_NTH_OF = 31 // An+B with "of " syntax -export const NODE_SELECTOR_LANG = 56 // language identifier for :lang() pseudo-class +export const SELECTOR_LIST = 20 // comma-separated selectors +export const TYPE_SELECTOR = 21 // type selector: div, span, p +export const CLASS_SELECTOR = 22 // class selector: .classname +export const ID_SELECTOR = 23 // ID selector: #identifier +export const ATTRIBUTE_SELECTOR = 24 // attribute selector: [attr], [attr=value] +export const PSEUDO_CLASS_SELECTOR = 25 // pseudo-class: :hover, :nth-child() +export const PSEUDO_ELEMENT_SELECTOR = 26 // pseudo-element: ::before, ::after +export const COMBINATOR = 27 // combinator: >, +, ~, space +export const UNIVERSAL_SELECTOR = 28 // universal selector: * +export const NESTING_SELECTOR = 29 // nesting selector: & +export const NTH_SELECTOR = 30 // An+B expression: 2n+1, odd, even +export const NTH_OF_SELECTOR = 31 // An+B with "of " syntax +export const LANG_SELECTOR = 56 // language identifier for :lang() pseudo-class // At-rule prelude node type constants (for at-rule prelude parsing) -export const NODE_PRELUDE_MEDIA_QUERY = 32 // media query: screen, (min-width: 768px) -export const NODE_PRELUDE_MEDIA_FEATURE = 33 // media feature: (min-width: 768px) -export const NODE_PRELUDE_MEDIA_TYPE = 34 // media type: screen, print, all -export const NODE_PRELUDE_CONTAINER_QUERY = 35 // container query: sidebar (min-width: 400px) -export const NODE_PRELUDE_SUPPORTS_QUERY = 36 // supports query: (display: flex) -export const NODE_PRELUDE_LAYER_NAME = 37 // layer name: base, components -export const NODE_PRELUDE_IDENTIFIER = 38 // generic identifier: keyframe name, property name -export const NODE_PRELUDE_OPERATOR = 39 // logical operator: and, or, not -export const NODE_PRELUDE_IMPORT_URL = 40 // import URL: url("file.css") or "file.css" -export const NODE_PRELUDE_IMPORT_LAYER = 41 // import layer: layer or layer(name) -export const NODE_PRELUDE_IMPORT_SUPPORTS = 42 // import supports: supports(condition) +export const MEDIA_QUERY = 32 // media query: screen, (min-width: 768px) +export const MEDIA_FEATURE = 33 // media feature: (min-width: 768px) +export const MEDIA_TYPE = 34 // media type: screen, print, all +export const CONTAINER_QUERY = 35 // container query: sidebar (min-width: 400px) +export const SUPPORTS_QUERY = 36 // supports query: (display: flex) +export const LAYER_NAME = 37 // layer name: base, components +export const PRELUDE_OPERATOR = 38 // logical operator: and, or, not // Flag constants (bit-packed in 1 byte) export const FLAG_IMPORTANT = 1 << 0 // Has !important diff --git a/src/column-tracking.test.ts b/src/column-tracking.test.ts index 8049cba..ebb5889 100644 --- a/src/column-tracking.test.ts +++ b/src/column-tracking.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from 'vitest' import { parse } from './parse' -import { NODE_STYLE_RULE, NODE_DECLARATION, NODE_AT_RULE, NODE_SELECTOR_LIST } from './constants' +import { STYLE_RULE, DECLARATION, AT_RULE, SELECTOR_LIST } from './constants' describe('Column Tracking', () => { test('should track column for single-line CSS', () => { @@ -14,14 +14,14 @@ describe('Column Tracking', () => { // First rule (body) const rule = ast.first_child expect(rule).not.toBeNull() - expect(rule!.type).toBe(NODE_STYLE_RULE) + expect(rule!.type).toBe(STYLE_RULE) expect(rule!.line).toBe(1) expect(rule!.column).toBe(1) // Selector (body) const selector = rule!.first_child expect(selector).not.toBeNull() - expect(selector!.type).toBe(NODE_SELECTOR_LIST) + expect(selector!.type).toBe(SELECTOR_LIST) expect(selector!.line).toBe(1) expect(selector!.column).toBe(1) @@ -29,7 +29,7 @@ describe('Column Tracking', () => { const block = selector!.next_sibling const decl = block!.first_child expect(decl).not.toBeNull() - expect(decl!.type).toBe(NODE_DECLARATION) + expect(decl!.type).toBe(DECLARATION) expect(decl!.line).toBe(1) expect(decl!.column).toBe(8) }) @@ -47,13 +47,13 @@ describe('Column Tracking', () => { // First declaration (color: red) at line 2, column 3 const decl1 = block.first_child! - expect(decl1.type).toBe(NODE_DECLARATION) + expect(decl1.type).toBe(DECLARATION) expect(decl1.line).toBe(2) expect(decl1.column).toBe(3) // Second declaration (font-size: 16px) at line 3, column 3 const decl2 = decl1.next_sibling! - expect(decl2.type).toBe(NODE_DECLARATION) + expect(decl2.type).toBe(DECLARATION) expect(decl2.line).toBe(3) expect(decl2.column).toBe(3) }) @@ -64,19 +64,19 @@ describe('Column Tracking', () => { // At-rule should start at column 1 const atRule = ast.first_child! - expect(atRule.type).toBe(NODE_AT_RULE) + expect(atRule.type).toBe(AT_RULE) expect(atRule.line).toBe(1) expect(atRule.column).toBe(1) // Get the block, then find the nested style rule const block = atRule.block! let nestedRule = block.first_child - while (nestedRule && nestedRule.type !== NODE_STYLE_RULE) { + while (nestedRule && nestedRule.type !== STYLE_RULE) { nestedRule = nestedRule.next_sibling } expect(nestedRule).not.toBeNull() - expect(nestedRule!.type).toBe(NODE_STYLE_RULE) + expect(nestedRule!.type).toBe(STYLE_RULE) expect(nestedRule!.line).toBe(1) // Column 17 is where 'body' starts (beginning of selector) expect(nestedRule!.column).toBe(17) @@ -88,13 +88,13 @@ describe('Column Tracking', () => { // First rule at column 1 const rule1 = ast.first_child! - expect(rule1.type).toBe(NODE_STYLE_RULE) + expect(rule1.type).toBe(STYLE_RULE) expect(rule1.line).toBe(1) expect(rule1.column).toBe(1) // Second rule at column 19 const rule2 = rule1.next_sibling! - expect(rule2.type).toBe(NODE_STYLE_RULE) + expect(rule2.type).toBe(STYLE_RULE) expect(rule2.line).toBe(1) expect(rule2.column).toBe(19) }) @@ -105,7 +105,7 @@ describe('Column Tracking', () => { // Rule should start at column 5 (after 4 spaces) const rule = ast.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.line).toBe(1) expect(rule.column).toBe(5) }) @@ -117,7 +117,7 @@ describe('Column Tracking', () => { // Rule should start at column 15 (after comment and space) const rule = ast1.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.line).toBe(1) expect(rule.column).toBe(15) }) diff --git a/src/constants.ts b/src/constants.ts index a6e8559..f5ab10d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,45 +2,42 @@ // This breaks the barrel file chain and improves tree-shaking export { - NODE_STYLESHEET, - NODE_STYLE_RULE, - NODE_AT_RULE, - NODE_DECLARATION, - NODE_SELECTOR, - NODE_COMMENT, - NODE_BLOCK, - NODE_VALUE_KEYWORD, - NODE_VALUE_NUMBER, - NODE_VALUE_DIMENSION, - NODE_VALUE_STRING, - NODE_VALUE_COLOR, - NODE_VALUE_FUNCTION, - NODE_VALUE_OPERATOR, - NODE_VALUE_PARENTHESIS, - NODE_SELECTOR_LIST, - NODE_SELECTOR_TYPE, - NODE_SELECTOR_CLASS, - NODE_SELECTOR_ID, - NODE_SELECTOR_ATTRIBUTE, - NODE_SELECTOR_PSEUDO_CLASS, - NODE_SELECTOR_PSEUDO_ELEMENT, - NODE_SELECTOR_COMBINATOR, - NODE_SELECTOR_UNIVERSAL, - NODE_SELECTOR_NESTING, - NODE_SELECTOR_NTH, - NODE_SELECTOR_NTH_OF, - NODE_SELECTOR_LANG, - NODE_PRELUDE_MEDIA_QUERY, - NODE_PRELUDE_MEDIA_FEATURE, - NODE_PRELUDE_MEDIA_TYPE, - NODE_PRELUDE_CONTAINER_QUERY, - NODE_PRELUDE_SUPPORTS_QUERY, - NODE_PRELUDE_LAYER_NAME, - NODE_PRELUDE_IDENTIFIER, - NODE_PRELUDE_OPERATOR, - NODE_PRELUDE_IMPORT_URL, - NODE_PRELUDE_IMPORT_LAYER, - NODE_PRELUDE_IMPORT_SUPPORTS, + STYLESHEET, + STYLE_RULE, + AT_RULE, + DECLARATION, + SELECTOR, + COMMENT, + BLOCK, + IDENTIFIER, + NUMBER, + DIMENSION, + STRING, + HASH, + FUNCTION, + OPERATOR, + PARENTHESIS, + URL, + 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, FLAG_IMPORTANT, ATTR_OPERATOR_NONE, ATTR_OPERATOR_EQUAL, diff --git a/src/css-node.test.ts b/src/css-node.test.ts index ae9bac8..c017e68 100644 --- a/src/css-node.test.ts +++ b/src/css-node.test.ts @@ -2,16 +2,16 @@ import { describe, test, expect } from 'vitest' import { Parser, parse } from './parse' import { parse_selector } from './parse-selector' import { - NODE_DECLARATION, - NODE_STYLE_RULE, - NODE_AT_RULE, - NODE_SELECTOR_NTH, - NODE_SELECTOR_LIST, - NODE_SELECTOR_PSEUDO_CLASS, - NODE_VALUE_DIMENSION, - NODE_VALUE_NUMBER, - NODE_VALUE_FUNCTION, - NODE_SELECTOR_ATTRIBUTE, + DECLARATION, + STYLE_RULE, + AT_RULE, + NTH_SELECTOR, + SELECTOR_LIST, + PSEUDO_CLASS_SELECTOR, + DIMENSION, + NUMBER, + FUNCTION, + ATTRIBUTE_SELECTOR, } from './arena' describe('CSSNode', () => { @@ -29,7 +29,7 @@ describe('CSSNode', () => { types.push(child.type) } - expect(types).toEqual([NODE_DECLARATION, NODE_DECLARATION, NODE_DECLARATION]) + expect(types).toEqual([DECLARATION, DECLARATION, DECLARATION]) }) test('should work with spread operator', () => { @@ -39,8 +39,8 @@ describe('CSSNode', () => { const rules = [...root] expect(rules).toHaveLength(2) - expect(rules[0].type).toBe(NODE_STYLE_RULE) - expect(rules[1].type).toBe(NODE_STYLE_RULE) + expect(rules[0].type).toBe(STYLE_RULE) + expect(rules[1].type).toBe(STYLE_RULE) }) test('should work with Array.from', () => { @@ -53,7 +53,7 @@ describe('CSSNode', () => { const children = Array.from(block) expect(children).toHaveLength(1) - expect(children[0].type).toBe(NODE_STYLE_RULE) + expect(children[0].type).toBe(STYLE_RULE) }) test('should iterate over empty children', () => { @@ -79,7 +79,7 @@ describe('CSSNode', () => { const root = parser.parse() const media = root.first_child! - expect(media.type).toBe(NODE_AT_RULE) + expect(media.type).toBe(AT_RULE) expect(media.has_prelude).toBe(true) expect(media.prelude).toBe('(min-width: 768px)') }) @@ -90,7 +90,7 @@ describe('CSSNode', () => { const root = parser.parse() const supports = root.first_child! - expect(supports.type).toBe(NODE_AT_RULE) + expect(supports.type).toBe(AT_RULE) expect(supports.has_prelude).toBe(true) expect(supports.prelude).toBe('(display: grid)') }) @@ -101,7 +101,7 @@ describe('CSSNode', () => { const root = parser.parse() const layer = root.first_child! - expect(layer.type).toBe(NODE_AT_RULE) + expect(layer.type).toBe(AT_RULE) expect(layer.has_prelude).toBe(true) expect(layer.prelude).toBe('utilities') }) @@ -112,7 +112,7 @@ describe('CSSNode', () => { const root = parser.parse() const layer = root.first_child! - expect(layer.type).toBe(NODE_AT_RULE) + expect(layer.type).toBe(AT_RULE) expect(layer.has_prelude).toBe(false) expect(layer.prelude).toBeNull() }) @@ -123,7 +123,7 @@ describe('CSSNode', () => { const root = parser.parse() const keyframes = root.first_child! - expect(keyframes.type).toBe(NODE_AT_RULE) + expect(keyframes.type).toBe(AT_RULE) expect(keyframes.has_prelude).toBe(true) expect(keyframes.prelude).toBe('fadeIn') }) @@ -134,7 +134,7 @@ describe('CSSNode', () => { const root = parser.parse() const fontFace = root.first_child! - expect(fontFace.type).toBe(NODE_AT_RULE) + expect(fontFace.type).toBe(AT_RULE) expect(fontFace.has_prelude).toBe(false) expect(fontFace.prelude).toBeNull() }) @@ -145,7 +145,7 @@ describe('CSSNode', () => { const root = parser.parse() const page = root.first_child! - expect(page.type).toBe(NODE_AT_RULE) + expect(page.type).toBe(AT_RULE) expect(page.has_prelude).toBe(false) expect(page.prelude).toBeNull() }) @@ -156,7 +156,7 @@ describe('CSSNode', () => { const root = parser.parse() const importRule = root.first_child! - expect(importRule.type).toBe(NODE_AT_RULE) + expect(importRule.type).toBe(AT_RULE) expect(importRule.has_prelude).toBe(true) expect(importRule.prelude).not.toBeNull() }) @@ -200,7 +200,7 @@ describe('CSSNode', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.has_block).toBe(true) }) @@ -210,7 +210,7 @@ describe('CSSNode', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.has_block).toBe(true) }) @@ -220,7 +220,7 @@ describe('CSSNode', () => { const root = parser.parse() const media = root.first_child! - expect(media.type).toBe(NODE_AT_RULE) + expect(media.type).toBe(AT_RULE) expect(media.has_block).toBe(true) }) @@ -230,7 +230,7 @@ describe('CSSNode', () => { const root = parser.parse() const supports = root.first_child! - expect(supports.type).toBe(NODE_AT_RULE) + expect(supports.type).toBe(AT_RULE) expect(supports.has_block).toBe(true) }) @@ -240,7 +240,7 @@ describe('CSSNode', () => { const root = parser.parse() const layer = root.first_child! - expect(layer.type).toBe(NODE_AT_RULE) + expect(layer.type).toBe(AT_RULE) expect(layer.has_block).toBe(true) }) @@ -250,7 +250,7 @@ describe('CSSNode', () => { const root = parser.parse() const layer = root.first_child! - expect(layer.type).toBe(NODE_AT_RULE) + expect(layer.type).toBe(AT_RULE) expect(layer.has_block).toBe(true) }) @@ -260,7 +260,7 @@ describe('CSSNode', () => { const root = parser.parse() const fontFace = root.first_child! - expect(fontFace.type).toBe(NODE_AT_RULE) + expect(fontFace.type).toBe(AT_RULE) expect(fontFace.has_block).toBe(true) }) @@ -270,7 +270,7 @@ describe('CSSNode', () => { const root = parser.parse() const keyframes = root.first_child! - expect(keyframes.type).toBe(NODE_AT_RULE) + expect(keyframes.type).toBe(AT_RULE) expect(keyframes.has_block).toBe(true) }) @@ -280,7 +280,7 @@ describe('CSSNode', () => { const root = parser.parse() const importRule = root.first_child! - expect(importRule.type).toBe(NODE_AT_RULE) + expect(importRule.type).toBe(AT_RULE) expect(importRule.has_block).toBe(false) }) @@ -290,7 +290,7 @@ describe('CSSNode', () => { const root = parser.parse() const importRule = root.first_child! - expect(importRule.type).toBe(NODE_AT_RULE) + expect(importRule.type).toBe(AT_RULE) expect(importRule.has_block).toBe(false) expect(importRule.has_children).toBe(true) // Has prelude children expect(importRule.has_prelude).toBe(true) @@ -359,7 +359,7 @@ describe('CSSNode', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.has_declarations).toBe(true) }) @@ -369,7 +369,7 @@ describe('CSSNode', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.has_declarations).toBe(false) }) @@ -379,7 +379,7 @@ describe('CSSNode', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.has_declarations).toBe(false) }) @@ -389,7 +389,7 @@ describe('CSSNode', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.has_declarations).toBe(true) }) @@ -399,7 +399,7 @@ describe('CSSNode', () => { const root = parser.parse() const media = root.first_child! - expect(media.type).toBe(NODE_AT_RULE) + expect(media.type).toBe(AT_RULE) expect(media.has_declarations).toBe(false) }) }) @@ -410,7 +410,7 @@ describe('CSSNode', () => { const parser = new Parser(source) const root = parser.parse() - expect(root.type_name).toBe('stylesheet') + expect(root.type_name).toBe('StyleSheet') }) test('should return style_rule for style rules', () => { @@ -419,7 +419,7 @@ describe('CSSNode', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type_name).toBe('rule') + expect(rule.type_name).toBe('Rule') }) test('should return declaration for declarations', () => { @@ -430,7 +430,7 @@ describe('CSSNode', () => { const block = rule.block! const decl = block.first_child! - expect(decl.type_name).toBe('declaration') + expect(decl.type_name).toBe('Declaration') }) test('should return at_rule for at-rules', () => { @@ -439,7 +439,7 @@ describe('CSSNode', () => { const root = parser.parse() const media = root.first_child! - expect(media.type_name).toBe('atrule') + expect(media.type_name).toBe('Atrule') }) test('should return selector_list for selector lists', () => { @@ -449,7 +449,7 @@ describe('CSSNode', () => { const rule = root.first_child! const selectorList = rule.first_child! - expect(selectorList.type_name).toBe('selectorlist') + expect(selectorList.type_name).toBe('SelectorList') }) test('should return selector_type for type selectors', () => { @@ -461,7 +461,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const typeSelector = selector.first_child! - expect(typeSelector.type_name).toBe('type-selector') + expect(typeSelector.type_name).toBe('TypeSelector') }) test('should return selector_class for class selectors', () => { @@ -473,7 +473,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const classSelector = selector.first_child! - expect(classSelector.type_name).toBe('class-selector') + expect(classSelector.type_name).toBe('ClassSelector') }) test('should return selector_id for ID selectors', () => { @@ -485,7 +485,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const idSelector = selector.first_child! - expect(idSelector.type_name).toBe('id-selector') + expect(idSelector.type_name).toBe('IdSelector') }) test('should return selector_universal for universal selectors', () => { @@ -497,7 +497,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const universalSelector = selector.first_child! - expect(universalSelector.type_name).toBe('universal-selector') + expect(universalSelector.type_name).toBe('UniversalSelector') }) test('should return selector_attribute for attribute selectors', () => { @@ -509,7 +509,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const attrSelector = selector.first_child! - expect(attrSelector.type_name).toBe('attribute-selector') + expect(attrSelector.type_name).toBe('AttributeSelector') }) test('should return selector_pseudo_class for pseudo-class selectors', () => { @@ -521,7 +521,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const pseudoClass = selector.first_child! - expect(pseudoClass.type_name).toBe('pseudoclass-selector') + expect(pseudoClass.type_name).toBe('PseudoClassSelector') }) test('should return selector_pseudo_element for pseudo-element selectors', () => { @@ -533,7 +533,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const pseudoElement = selector.first_child! - expect(pseudoElement.type_name).toBe('pseudoelement-selector') + expect(pseudoElement.type_name).toBe('PseudoElementSelector') }) test('should return selector_combinator for combinators', () => { @@ -545,7 +545,7 @@ describe('CSSNode', () => { const selector = selectorList.first_child! const combinator = selector.first_child!.next_sibling! - expect(combinator.type_name).toBe('selector-combinator') + expect(combinator.type_name).toBe('Combinator') }) test('should return value_keyword for keyword values', () => { @@ -557,7 +557,7 @@ describe('CSSNode', () => { const decl = block.first_child! const value = decl.first_child! - expect(value.type_name).toBe('keyword') + expect(value.type_name).toBe('Identifier') }) test('should return value_number for numeric values', () => { @@ -569,7 +569,7 @@ describe('CSSNode', () => { const decl = block.first_child! const value = decl.first_child! - expect(value.type_name).toBe('number') + expect(value.type_name).toBe('Number') }) test('should return value_dimension for dimension values', () => { @@ -581,7 +581,7 @@ describe('CSSNode', () => { const decl = block.first_child! const value = decl.first_child! - expect(value.type_name).toBe('dimension') + expect(value.type_name).toBe('Dimension') }) test('should return value_string for string values', () => { @@ -593,7 +593,7 @@ describe('CSSNode', () => { const decl = block.first_child! const value = decl.first_child! - expect(value.type_name).toBe('string') + expect(value.type_name).toBe('String') }) test('should return value_color for color values', () => { @@ -605,7 +605,7 @@ describe('CSSNode', () => { const decl = block.first_child! const value = decl.first_child! - expect(value.type_name).toBe('color') + expect(value.type_name).toBe('Hash') }) test('should return value_function for function values', () => { @@ -617,7 +617,7 @@ describe('CSSNode', () => { const decl = block.first_child! const value = decl.first_child! - expect(value.type_name).toBe('function') + expect(value.type_name).toBe('Function') }) test('should return prelude_media_query for media query preludes', () => { @@ -627,7 +627,7 @@ describe('CSSNode', () => { const media = root.first_child! const prelude = media.first_child! - expect(prelude.type_name).toBe('media-query') + expect(prelude.type_name).toBe('MediaQuery') }) }) @@ -640,7 +640,7 @@ describe('CSSNode', () => { const nthOf = pseudo?.first_child // NODE_SELECTOR_NTH_OF expect(nthOf?.nth).not.toBeNull() - expect(nthOf?.nth?.type).toBe(NODE_SELECTOR_NTH) + expect(nthOf?.nth?.type).toBe(NTH_SELECTOR) expect(nthOf?.nth?.nth_a).toBe('2n') expect(nthOf?.nth?.nth_b).toBe('+1') }) @@ -652,7 +652,7 @@ describe('CSSNode', () => { const nthOf = pseudo?.first_child expect(nthOf?.selector).not.toBeNull() - expect(nthOf?.selector?.type).toBe(NODE_SELECTOR_LIST) + expect(nthOf?.selector?.type).toBe(SELECTOR_LIST) expect(nthOf?.selector?.text).toBe('.foo, #bar') }) @@ -705,9 +705,9 @@ describe('CSSNode', () => { const selector = result.first_child const pseudo = selector?.first_child - expect(pseudo?.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudo?.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudo?.selector_list).not.toBeNull() - expect(pseudo?.selector_list?.type).toBe(NODE_SELECTOR_LIST) + expect(pseudo?.selector_list?.type).toBe(SELECTOR_LIST) expect(pseudo?.selector_list?.text).toBe('.foo, #bar') }) @@ -1055,8 +1055,8 @@ describe('CSSNode', () => { const clone = decl.clone({ deep: false }) - expect(clone.type).toBe(NODE_DECLARATION) - expect(clone.type_name).toBe('declaration') + expect(clone.type).toBe(DECLARATION) + expect(clone.type_name).toBe('Declaration') expect(clone.text).toBe('color: red;') expect(clone.name).toBe('color') expect(clone.property).toBe('color') @@ -1071,7 +1071,7 @@ describe('CSSNode', () => { const shallow = decl.clone({ deep: false }) expect(shallow.children).toEqual([]) - expect(shallow.type).toBe(NODE_DECLARATION) + expect(shallow.type).toBe(DECLARATION) }) test('deep clone includes children as array', () => { @@ -1081,7 +1081,7 @@ describe('CSSNode', () => { const deep = decl.clone() expect(deep.children.length).toBe(2) - expect(deep.children[0].type).toBe(NODE_VALUE_DIMENSION) + expect(deep.children[0].type).toBe(DIMENSION) expect(deep.children[0].value).toBe(10) expect(deep.children[0].unit).toBe('px') expect(deep.children[1].value).toBe(20) @@ -1108,7 +1108,7 @@ describe('CSSNode', () => { const clone = decl.clone() expect(clone.children.length).toBe(1) - expect(clone.children[0].type).toBe(NODE_VALUE_FUNCTION) + expect(clone.children[0].type).toBe(FUNCTION) expect(clone.children[0].name).toBe('calc') // Function should have nested children expect(clone.children[0].children.length).toBeGreaterThan(0) @@ -1122,8 +1122,8 @@ describe('CSSNode', () => { const clone = decl.clone({ deep: false }) - expect(clone.type).toBe(NODE_DECLARATION) - expect(clone.type_name).toBe('declaration') + expect(clone.type).toBe(DECLARATION) + expect(clone.type_name).toBe('Declaration') expect(clone.property).toBe('color') expect(clone.name).toBe('color') expect(clone.value).toBe('red') @@ -1136,8 +1136,8 @@ describe('CSSNode', () => { const clone = atrule.clone({ deep: false }) - expect(clone.type).toBe(NODE_AT_RULE) - expect(clone.type_name).toBe('atrule') + expect(clone.type).toBe(AT_RULE) + expect(clone.type_name).toBe('Atrule') expect(clone.name).toBe('media') expect(clone.prelude).toBe('screen') }) @@ -1149,8 +1149,8 @@ describe('CSSNode', () => { const clone = dimension.clone({ deep: false }) - expect(clone.type).toBe(NODE_VALUE_DIMENSION) - expect(clone.type_name).toBe('dimension') + expect(clone.type).toBe(DIMENSION) + expect(clone.type_name).toBe('Dimension') expect(clone.value).toBe(100) expect(clone.unit).toBe('px') }) @@ -1162,7 +1162,7 @@ describe('CSSNode', () => { const clone = number.clone({ deep: false }) - expect(clone.type).toBe(NODE_VALUE_NUMBER) + expect(clone.type).toBe(NUMBER) expect(clone.value).toBe(0.5) expect(clone.unit).toBeUndefined() }) @@ -1174,8 +1174,8 @@ describe('CSSNode', () => { const clone = attribute.clone({ deep: false }) - expect(clone.type).toBe(NODE_SELECTOR_ATTRIBUTE) - expect(clone.type_name).toBe('attribute-selector') + expect(clone.type).toBe(ATTRIBUTE_SELECTOR) + expect(clone.type_name).toBe('AttributeSelector') expect(clone.attr_operator).toBeDefined() expect(clone.attr_flags).toBeDefined() }) @@ -1188,7 +1188,7 @@ describe('CSSNode', () => { const clone = nth.clone({ deep: false }) - expect(clone.type).toBe(NODE_SELECTOR_NTH) + expect(clone.type).toBe(NTH_SELECTOR) expect(clone.nth_a).toBe('2n') expect(clone.nth_b).toBe('+1') }) diff --git a/src/css-node.ts b/src/css-node.ts index aaabe56..b2f82aa 100644 --- a/src/css-node.ts +++ b/src/css-node.ts @@ -1,45 +1,42 @@ // CSSNode - Ergonomic wrapper over arena node indices import type { CSSDataArena } from './arena' import { - NODE_STYLESHEET, - NODE_STYLE_RULE, - NODE_AT_RULE, - NODE_DECLARATION, - NODE_SELECTOR, - NODE_COMMENT, - NODE_BLOCK, - NODE_VALUE_KEYWORD, - NODE_VALUE_NUMBER, - NODE_VALUE_DIMENSION, - NODE_VALUE_STRING, - NODE_VALUE_COLOR, - NODE_VALUE_FUNCTION, - NODE_VALUE_OPERATOR, - NODE_VALUE_PARENTHESIS, - NODE_SELECTOR_LIST, - NODE_SELECTOR_TYPE, - NODE_SELECTOR_CLASS, - NODE_SELECTOR_ID, - NODE_SELECTOR_ATTRIBUTE, - NODE_SELECTOR_PSEUDO_CLASS, - NODE_SELECTOR_PSEUDO_ELEMENT, - NODE_SELECTOR_COMBINATOR, - NODE_SELECTOR_UNIVERSAL, - NODE_SELECTOR_NESTING, - NODE_SELECTOR_NTH, - NODE_SELECTOR_NTH_OF, - NODE_SELECTOR_LANG, - NODE_PRELUDE_MEDIA_QUERY, - NODE_PRELUDE_MEDIA_FEATURE, - NODE_PRELUDE_MEDIA_TYPE, - NODE_PRELUDE_CONTAINER_QUERY, - NODE_PRELUDE_SUPPORTS_QUERY, - NODE_PRELUDE_LAYER_NAME, - NODE_PRELUDE_IDENTIFIER, - NODE_PRELUDE_OPERATOR, - NODE_PRELUDE_IMPORT_URL, - NODE_PRELUDE_IMPORT_LAYER, - NODE_PRELUDE_IMPORT_SUPPORTS, + STYLESHEET, + STYLE_RULE, + AT_RULE, + DECLARATION, + SELECTOR, + COMMENT, + BLOCK, + IDENTIFIER, + NUMBER, + DIMENSION, + STRING, + HASH, + FUNCTION, + OPERATOR, + PARENTHESIS, + URL, + 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, FLAG_IMPORTANT, FLAG_HAS_ERROR, FLAG_HAS_BLOCK, @@ -51,90 +48,84 @@ import { import { CHAR_MINUS_HYPHEN, CHAR_PLUS, is_whitespace } from './string-utils' import { parse_dimension } from './parse-utils' -// Type name lookup table - maps numeric type to human-readable string +// Type name lookup table - maps numeric type to CSSTree-compatible strings export const TYPE_NAMES: Record = { - [NODE_STYLESHEET]: 'stylesheet', - [NODE_STYLE_RULE]: 'rule', - [NODE_AT_RULE]: 'atrule', - [NODE_DECLARATION]: 'declaration', - [NODE_SELECTOR]: 'selector', - [NODE_COMMENT]: 'comment', - [NODE_BLOCK]: 'block', - [NODE_VALUE_KEYWORD]: 'keyword', - [NODE_VALUE_NUMBER]: 'number', - [NODE_VALUE_DIMENSION]: 'dimension', - [NODE_VALUE_STRING]: 'string', - [NODE_VALUE_COLOR]: 'color', - [NODE_VALUE_FUNCTION]: 'function', - [NODE_VALUE_OPERATOR]: 'operator', - [NODE_VALUE_PARENTHESIS]: 'parenthesis', - [NODE_SELECTOR_LIST]: 'selectorlist', - [NODE_SELECTOR_TYPE]: 'type-selector', - [NODE_SELECTOR_CLASS]: 'class-selector', - [NODE_SELECTOR_ID]: 'id-selector', - [NODE_SELECTOR_ATTRIBUTE]: 'attribute-selector', - [NODE_SELECTOR_PSEUDO_CLASS]: 'pseudoclass-selector', - [NODE_SELECTOR_PSEUDO_ELEMENT]: 'pseudoelement-selector', - [NODE_SELECTOR_COMBINATOR]: 'selector-combinator', - [NODE_SELECTOR_UNIVERSAL]: 'universal-selector', - [NODE_SELECTOR_NESTING]: 'nesting-selector', - [NODE_SELECTOR_NTH]: 'nth-selector', - [NODE_SELECTOR_NTH_OF]: 'nth-of-selector', - [NODE_SELECTOR_LANG]: 'lang-selector', - [NODE_PRELUDE_MEDIA_QUERY]: 'media-query', - [NODE_PRELUDE_MEDIA_FEATURE]: 'media-feature', - [NODE_PRELUDE_MEDIA_TYPE]: 'media-type', - [NODE_PRELUDE_CONTAINER_QUERY]: 'container-query', - [NODE_PRELUDE_SUPPORTS_QUERY]: 'supports-query', - [NODE_PRELUDE_LAYER_NAME]: 'layer-name', - [NODE_PRELUDE_IDENTIFIER]: 'identifier', - [NODE_PRELUDE_OPERATOR]: 'operator', - [NODE_PRELUDE_IMPORT_URL]: 'import-url', - [NODE_PRELUDE_IMPORT_LAYER]: 'import-layer', - [NODE_PRELUDE_IMPORT_SUPPORTS]: 'import-supports', + [STYLESHEET]: 'StyleSheet', + [STYLE_RULE]: 'Rule', + [AT_RULE]: 'Atrule', + [DECLARATION]: 'Declaration', + [SELECTOR]: 'Selector', + [COMMENT]: 'Comment', + [BLOCK]: 'Block', + [IDENTIFIER]: 'Identifier', + [NUMBER]: 'Number', + [DIMENSION]: 'Dimension', + [STRING]: 'String', + [HASH]: 'Hash', + [FUNCTION]: 'Function', + [OPERATOR]: 'Operator', + [PARENTHESIS]: 'Parentheses', + [URL]: 'Url', + [SELECTOR_LIST]: 'SelectorList', + [TYPE_SELECTOR]: 'TypeSelector', + [CLASS_SELECTOR]: 'ClassSelector', + [ID_SELECTOR]: 'IdSelector', + [ATTRIBUTE_SELECTOR]: 'AttributeSelector', + [PSEUDO_CLASS_SELECTOR]: 'PseudoClassSelector', + [PSEUDO_ELEMENT_SELECTOR]: 'PseudoElementSelector', + [COMBINATOR]: 'Combinator', + [UNIVERSAL_SELECTOR]: 'UniversalSelector', + [NESTING_SELECTOR]: 'NestingSelector', + [NTH_SELECTOR]: 'Nth', + [NTH_OF_SELECTOR]: 'NthOf', + [LANG_SELECTOR]: 'Lang', + [MEDIA_QUERY]: 'MediaQuery', + [MEDIA_FEATURE]: 'Feature', + [MEDIA_TYPE]: 'MediaType', + [CONTAINER_QUERY]: 'ContainerQuery', + [SUPPORTS_QUERY]: 'SupportsQuery', + [LAYER_NAME]: 'Layer', + [PRELUDE_OPERATOR]: 'Operator', } as const // Node type constants (numeric for performance) export type CSSNodeType = - | typeof NODE_STYLESHEET - | typeof NODE_STYLE_RULE - | typeof NODE_AT_RULE - | typeof NODE_DECLARATION - | typeof NODE_SELECTOR - | typeof NODE_COMMENT - | typeof NODE_BLOCK - | typeof NODE_VALUE_KEYWORD - | typeof NODE_VALUE_NUMBER - | typeof NODE_VALUE_DIMENSION - | typeof NODE_VALUE_STRING - | typeof NODE_VALUE_COLOR - | typeof NODE_VALUE_FUNCTION - | typeof NODE_VALUE_OPERATOR - | typeof NODE_VALUE_PARENTHESIS - | typeof NODE_SELECTOR_LIST - | typeof NODE_SELECTOR_TYPE - | typeof NODE_SELECTOR_CLASS - | typeof NODE_SELECTOR_ID - | typeof NODE_SELECTOR_ATTRIBUTE - | typeof NODE_SELECTOR_PSEUDO_CLASS - | typeof NODE_SELECTOR_PSEUDO_ELEMENT - | typeof NODE_SELECTOR_COMBINATOR - | typeof NODE_SELECTOR_UNIVERSAL - | typeof NODE_SELECTOR_NESTING - | typeof NODE_SELECTOR_NTH - | typeof NODE_SELECTOR_NTH_OF - | typeof NODE_SELECTOR_LANG - | typeof NODE_PRELUDE_MEDIA_QUERY - | typeof NODE_PRELUDE_MEDIA_FEATURE - | typeof NODE_PRELUDE_MEDIA_TYPE - | typeof NODE_PRELUDE_CONTAINER_QUERY - | typeof NODE_PRELUDE_SUPPORTS_QUERY - | typeof NODE_PRELUDE_LAYER_NAME - | typeof NODE_PRELUDE_IDENTIFIER - | typeof NODE_PRELUDE_OPERATOR - | typeof NODE_PRELUDE_IMPORT_URL - | typeof NODE_PRELUDE_IMPORT_LAYER - | typeof NODE_PRELUDE_IMPORT_SUPPORTS + | 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 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 // Options for cloning nodes export interface CloneOptions { @@ -235,7 +226,7 @@ export class CSSNode { // For string nodes: returns the string content without quotes get value(): string | number | null { // For dimension and number nodes, parse and return as number - if (this.type === NODE_VALUE_DIMENSION || this.type === NODE_VALUE_NUMBER) { + if (this.type === DIMENSION || this.type === NUMBER) { return parse_dimension(this.text).value } @@ -267,13 +258,13 @@ export class CSSNode { // Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") get unit(): string | null { - if (this.type !== NODE_VALUE_DIMENSION) return null + if (this.type !== DIMENSION) return null return parse_dimension(this.text).unit } // Check if this declaration has !important get is_important(): boolean | null { - if (this.type !== NODE_DECLARATION) return null + if (this.type !== DECLARATION) return null return this.arena.has_flag(this.index, FLAG_IMPORTANT) } @@ -305,23 +296,23 @@ export class CSSNode { // Get the block node (for style rules and at-rules with blocks) get block(): CSSNode | null { // For StyleRule: block is sibling after selector list - if (this.type === NODE_STYLE_RULE) { + if (this.type === STYLE_RULE) { let first = this.first_child if (!first) return null // Block is the sibling after selector list let blockNode = first.next_sibling - if (blockNode && blockNode.type === NODE_BLOCK) { + if (blockNode && blockNode.type === BLOCK) { return blockNode } return null } // For AtRule: block is last child (after prelude nodes) - if (this.type === NODE_AT_RULE) { + if (this.type === AT_RULE) { // Find last child that is a block let child = this.first_child while (child) { - if (child.type === NODE_BLOCK && !child.next_sibling) { + if (child.type === BLOCK && !child.next_sibling) { return child } child = child.next_sibling @@ -335,12 +326,12 @@ export class CSSNode { // Check if this block is empty (no declarations or rules, only comments allowed) get is_empty(): boolean { // Only valid on block nodes - if (this.type !== NODE_BLOCK) return false + if (this.type !== BLOCK) return false // Empty if no children, or all children are comments let child = this.first_child while (child) { - if (child.type !== NODE_COMMENT) { + if (child.type !== COMMENT) { return false } child = child.next_sibling @@ -418,7 +409,7 @@ export class CSSNode { // This allows formatters to distinguish :lang() from :hover get has_children(): boolean { // For pseudo-class/pseudo-element nodes, check if they have function syntax - if (this.type === NODE_SELECTOR_PSEUDO_CLASS || this.type === NODE_SELECTOR_PSEUDO_ELEMENT) { + if (this.type === PSEUDO_CLASS_SELECTOR || this.type === PSEUDO_ELEMENT_SELECTOR) { // If FLAG_HAS_PARENS is set, return true even if no actual children // This indicates that `()` is there but contains no children which can be caught by checking `.children.length` if (this.arena.has_flag(this.index, FLAG_HAS_PARENS)) { @@ -452,7 +443,7 @@ export class CSSNode { // Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") get nth_a(): string | null { - if (this.type !== NODE_SELECTOR_NTH) return null + if (this.type !== NTH_SELECTOR) return null let len = this.arena.get_content_length(this.index) if (len === 0) return null @@ -462,7 +453,7 @@ export class CSSNode { // Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") get nth_b(): string | null { - if (this.type !== NODE_SELECTOR_NTH) return null + if (this.type !== NTH_SELECTOR) return null let len = this.arena.get_value_length(this.index) if (len === 0) return null @@ -494,13 +485,13 @@ export class CSSNode { // Get the An+B formula node from :nth-child(2n+1 of .foo) get nth(): CSSNode | null { - if (this.type !== NODE_SELECTOR_NTH_OF) return null + if (this.type !== NTH_OF_SELECTOR) return null return this.first_child // First child is always NODE_SELECTOR_NTH } // Get the selector list from :nth-child(2n+1 of .foo) get selector(): CSSNode | null { - if (this.type !== NODE_SELECTOR_NTH_OF) return null + if (this.type !== NTH_OF_SELECTOR) return null let first = this.first_child return first ? first.next_sibling : null // Second child is NODE_SELECTOR_LIST } @@ -510,18 +501,18 @@ export class CSSNode { // Get selector list from pseudo-class functions // Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e) get selector_list(): CSSNode | null { - if (this.type !== NODE_SELECTOR_PSEUDO_CLASS) return null + if (this.type !== PSEUDO_CLASS_SELECTOR) return null let child = this.first_child if (!child) return null // For simple cases (:is, :not, :where, :has), first_child is the selector list - if (child.type === NODE_SELECTOR_LIST) { + if (child.type === SELECTOR_LIST) { return child } // For :nth-child(of) cases, need to look inside NODE_SELECTOR_NTH_OF - if (child.type === NODE_SELECTOR_NTH_OF) { + if (child.type === NTH_OF_SELECTOR) { // Use the convenience getter we just added return child.selector } @@ -534,11 +525,11 @@ export class CSSNode { // Iterator over first compound selector parts (zero allocation) // Yields parts before the first combinator *compound_parts(): IterableIterator { - if (this.type !== NODE_SELECTOR) return + if (this.type !== SELECTOR) return let child = this.first_child while (child) { - if (child.type === NODE_SELECTOR_COMBINATOR) break + if (child.type === COMBINATOR) break yield child child = child.next_sibling } @@ -547,12 +538,12 @@ export class CSSNode { // Get first compound selector as array // Returns array of parts before first combinator get first_compound(): CSSNode[] { - if (this.type !== NODE_SELECTOR) return [] + if (this.type !== SELECTOR) return [] let result: CSSNode[] = [] let child = this.first_child while (child) { - if (child.type === NODE_SELECTOR_COMBINATOR) break + if (child.type === COMBINATOR) break result.push(child) child = child.next_sibling } @@ -562,14 +553,14 @@ export class CSSNode { // Split selector into compound selectors // Returns array of compound arrays split by combinators get all_compounds(): CSSNode[][] { - if (this.type !== NODE_SELECTOR) return [] + if (this.type !== SELECTOR) return [] let compounds: CSSNode[][] = [] let current_compound: CSSNode[] = [] let child = this.first_child while (child) { - if (child.type === NODE_SELECTOR_COMBINATOR) { + if (child.type === COMBINATOR) { if (current_compound.length > 0) { compounds.push(current_compound) current_compound = [] @@ -589,11 +580,11 @@ export class CSSNode { // Check if selector is compound (no combinators) get is_compound(): boolean { - if (this.type !== NODE_SELECTOR) return false + if (this.type !== SELECTOR) return false let child = this.first_child while (child) { - if (child.type === NODE_SELECTOR_COMBINATOR) return false + if (child.type === COMBINATOR) return false child = child.next_sibling } return true @@ -601,14 +592,14 @@ export class CSSNode { // Get text of first compound selector (no node allocation) get first_compound_text(): string { - if (this.type !== NODE_SELECTOR) return '' + if (this.type !== SELECTOR) return '' let start = -1 let end = -1 let child = this.first_child while (child) { - if (child.type === NODE_SELECTOR_COMBINATOR) break + if (child.type === COMBINATOR) break if (start === -1) start = child.offset end = child.offset + child.length @@ -656,7 +647,7 @@ export class CSSNode { // 2. Extract type-specific properties (only if meaningful) if (this.name) plain.name = this.name - if (this.type === NODE_DECLARATION) plain.property = this.name + if (this.type === DECLARATION) plain.property = this.name // 3. Handle value types if (this.value !== undefined && this.value !== null) { @@ -665,21 +656,21 @@ export class CSSNode { } // 4. Extract prelude for at-rules - if (this.type === NODE_AT_RULE && this.prelude) { + if (this.type === AT_RULE && this.prelude) { plain.prelude = this.prelude } // 5. Extract flags - if (this.type === NODE_DECLARATION) plain.is_important = this.is_important + if (this.type === DECLARATION) plain.is_important = this.is_important plain.is_vendor_prefixed = this.is_vendor_prefixed plain.has_error = this.has_error // 6. Extract selector-specific properties - if (this.type === NODE_SELECTOR_ATTRIBUTE) { + if (this.type === ATTRIBUTE_SELECTOR) { plain.attr_operator = this.attr_operator plain.attr_flags = this.attr_flags } - if (this.type === NODE_SELECTOR_NTH || this.type === NODE_SELECTOR_NTH_OF) { + if (this.type === NTH_SELECTOR || this.type === NTH_OF_SELECTOR) { plain.nth_a = this.nth_a plain.nth_b = this.nth_b } diff --git a/src/index.ts b/src/index.ts index a29d72a..a33b7c5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,74 +30,5 @@ export { // Constants - imported from dedicated constants file // This improves tree-shaking by avoiding the parse module if only constants are needed -export { - NODE_STYLE_RULE, - NODE_AT_RULE, - NODE_COMMENT, - NODE_DECLARATION, - NODE_SELECTOR, - NODE_STYLESHEET, - NODE_VALUE_KEYWORD, - NODE_VALUE_NUMBER, - NODE_VALUE_DIMENSION, - NODE_VALUE_STRING, - NODE_VALUE_COLOR, - NODE_VALUE_FUNCTION, - NODE_VALUE_OPERATOR, - NODE_VALUE_PARENTHESIS, - NODE_SELECTOR_LIST, - NODE_SELECTOR_TYPE, - NODE_SELECTOR_CLASS, - NODE_SELECTOR_ID, - NODE_SELECTOR_ATTRIBUTE, - NODE_SELECTOR_PSEUDO_CLASS, - NODE_SELECTOR_PSEUDO_ELEMENT, - NODE_SELECTOR_COMBINATOR, - NODE_SELECTOR_UNIVERSAL, - NODE_SELECTOR_NESTING, - NODE_SELECTOR_NTH, - NODE_SELECTOR_NTH_OF, - NODE_SELECTOR_LANG, - NODE_PRELUDE_MEDIA_QUERY, - NODE_PRELUDE_MEDIA_FEATURE, - NODE_PRELUDE_MEDIA_TYPE, - NODE_PRELUDE_CONTAINER_QUERY, - NODE_PRELUDE_SUPPORTS_QUERY, - NODE_PRELUDE_LAYER_NAME, - NODE_PRELUDE_IDENTIFIER, - NODE_PRELUDE_OPERATOR, - NODE_PRELUDE_IMPORT_URL, - NODE_PRELUDE_IMPORT_LAYER, - NODE_PRELUDE_IMPORT_SUPPORTS, - FLAG_IMPORTANT, -} from './constants' -export { - TOKEN_IDENT, - TOKEN_FUNCTION, - TOKEN_AT_KEYWORD, - TOKEN_HASH, - TOKEN_STRING, - TOKEN_BAD_STRING, - TOKEN_URL, - TOKEN_BAD_URL, - TOKEN_DELIM, - TOKEN_NUMBER, - TOKEN_PERCENTAGE, - TOKEN_DIMENSION, - TOKEN_WHITESPACE, - TOKEN_CDO, - TOKEN_CDC, - TOKEN_COLON, - TOKEN_SEMICOLON, - TOKEN_COMMA, - TOKEN_LEFT_BRACKET, - TOKEN_RIGHT_BRACKET, - TOKEN_LEFT_PAREN, - TOKEN_RIGHT_PAREN, - TOKEN_LEFT_BRACE, - TOKEN_RIGHT_BRACE, - TOKEN_COMMENT, - TOKEN_EOF, - type Token, - type TokenType, -} from './token-types' +export * from './constants' +export * from './token-types' diff --git a/src/parse-anplusb.test.ts b/src/parse-anplusb.test.ts index 213c0bd..8029ac5 100644 --- a/src/parse-anplusb.test.ts +++ b/src/parse-anplusb.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' import { ANplusBParser } from './parse-anplusb' -import { CSSDataArena, NODE_SELECTOR_NTH } from './arena' +import { CSSDataArena, NTH_SELECTOR } from './arena' import { CSSNode } from './css-node' // Helper to parse An+B expression @@ -18,7 +18,7 @@ describe('ANplusBParser', () => { it('should parse positive integer', () => { const node = parse_anplusb('3')! expect(node).not.toBeNull() - expect(node.type).toBe(NODE_SELECTOR_NTH) + expect(node.type).toBe(NTH_SELECTOR) expect(node.nth_a).toBe(null) expect(node.nth_b).toBe('3') expect(node.text).toBe('3') diff --git a/src/parse-anplusb.ts b/src/parse-anplusb.ts index 7f690eb..5e03298 100644 --- a/src/parse-anplusb.ts +++ b/src/parse-anplusb.ts @@ -5,7 +5,7 @@ // https://github.com/csstree/csstree/blob/56afb6dd761149099cd3cdfb0a38e15e8cc0a71a/lib/syntax/node/AnPlusB.js#L106-L271 import { Lexer } from './lexer' -import { NODE_SELECTOR_NTH, CSSDataArena } from './arena' +import { NTH_SELECTOR, CSSDataArena } from './arena' import { TOKEN_IDENT, TOKEN_NUMBER, TOKEN_DIMENSION, TOKEN_DELIM, type TokenType } from './token-types' import { CHAR_MINUS_HYPHEN, CHAR_PLUS } from './string-utils' import { skip_whitespace_forward } from './parse-utils' @@ -264,15 +264,9 @@ export class ANplusBParser { this.lexer.pos = skip_whitespace_forward(this.source, this.lexer.pos, this.expr_end) } - private create_anplusb_node( - start: number, - a_start: number, - a_end: number, - b_start: number, - b_end: number, - ): number { + private create_anplusb_node(start: number, a_start: number, a_end: number, b_start: number, b_end: number): number { const node = this.arena.create_node() - this.arena.set_type(node, NODE_SELECTOR_NTH) + this.arena.set_type(node, NTH_SELECTOR) this.arena.set_start_offset(node, start) this.arena.set_length(node, this.lexer.pos - start) this.arena.set_start_line(node, this.lexer.line) diff --git a/src/parse-atrule-prelude.test.ts b/src/parse-atrule-prelude.test.ts index 7859d2a..0b18295 100644 --- a/src/parse-atrule-prelude.test.ts +++ b/src/parse-atrule-prelude.test.ts @@ -2,19 +2,17 @@ import { describe, it, test, expect } from 'vitest' import { parse } from './parse' import { parse_atrule_prelude } from './parse-atrule-prelude' import { - NODE_AT_RULE, - NODE_BLOCK, - NODE_PRELUDE_MEDIA_QUERY, - NODE_PRELUDE_MEDIA_FEATURE, - NODE_PRELUDE_MEDIA_TYPE, - NODE_PRELUDE_CONTAINER_QUERY, - NODE_PRELUDE_SUPPORTS_QUERY, - NODE_PRELUDE_LAYER_NAME, - NODE_PRELUDE_IDENTIFIER, - NODE_PRELUDE_OPERATOR, - NODE_PRELUDE_IMPORT_URL, - NODE_PRELUDE_IMPORT_LAYER, - NODE_PRELUDE_IMPORT_SUPPORTS, + AT_RULE, + BLOCK, + MEDIA_QUERY, + MEDIA_FEATURE, + MEDIA_TYPE, + CONTAINER_QUERY, + SUPPORTS_QUERY, + LAYER_NAME, + IDENTIFIER, + PRELUDE_OPERATOR, + URL, } from './arena' describe('At-Rule Prelude Parser', () => { @@ -24,7 +22,7 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child - expect(atRule?.type).toBe(NODE_AT_RULE) + expect(atRule?.type).toBe(AT_RULE) expect(atRule?.name).toBe('media') // Should have prelude children @@ -32,11 +30,11 @@ describe('At-Rule Prelude Parser', () => { expect(children.length).toBeGreaterThan(0) // First child should be a media query - expect(children[0].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(MEDIA_QUERY) // Query should have a media type child const queryChildren = children[0].children - expect(queryChildren.some((c) => c.type === NODE_PRELUDE_MEDIA_TYPE)).toBe(true) + expect(queryChildren.some((c) => c.type === MEDIA_TYPE)).toBe(true) }) it('should parse media feature', () => { @@ -45,14 +43,14 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children[0].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(MEDIA_QUERY) // Query should have a media feature child const queryChildren = children[0].children - expect(queryChildren.some((c) => c.type === NODE_PRELUDE_MEDIA_FEATURE)).toBe(true) + expect(queryChildren.some((c) => c.type === MEDIA_FEATURE)).toBe(true) // Feature should have content - const feature = queryChildren.find((c) => c.type === NODE_PRELUDE_MEDIA_FEATURE) + const feature = queryChildren.find((c) => c.type === MEDIA_FEATURE) expect(feature?.value).toContain('min-width') }) @@ -62,7 +60,7 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] const queryChildren = children[0].children - const feature = queryChildren.find((c) => c.type === NODE_PRELUDE_MEDIA_FEATURE) + const feature = queryChildren.find((c) => c.type === MEDIA_FEATURE) expect(feature?.value).toBe('min-width: 768px') }) @@ -73,13 +71,13 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children[0].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(MEDIA_QUERY) const queryChildren = children[0].children // Should have: media type, operator, media feature - expect(queryChildren.some((c) => c.type === NODE_PRELUDE_MEDIA_TYPE)).toBe(true) - expect(queryChildren.some((c) => c.type === NODE_PRELUDE_OPERATOR)).toBe(true) - expect(queryChildren.some((c) => c.type === NODE_PRELUDE_MEDIA_FEATURE)).toBe(true) + expect(queryChildren.some((c) => c.type === MEDIA_TYPE)).toBe(true) + expect(queryChildren.some((c) => c.type === PRELUDE_OPERATOR)).toBe(true) + expect(queryChildren.some((c) => c.type === MEDIA_FEATURE)).toBe(true) }) it('should parse multiple media features', () => { @@ -89,7 +87,7 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] const queryChildren = children[0].children - const features = queryChildren.filter((c) => c.type === NODE_PRELUDE_MEDIA_FEATURE) + const features = queryChildren.filter((c) => c.type === MEDIA_FEATURE) expect(features.length).toBe(2) }) @@ -100,7 +98,7 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] // Should have 2 media query nodes - const queries = children.filter((c) => c.type === NODE_PRELUDE_MEDIA_QUERY) + const queries = children.filter((c) => c.type === MEDIA_QUERY) expect(queries.length).toBe(2) }) }) @@ -111,12 +109,12 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child - expect(atRule?.type).toBe(NODE_AT_RULE) + expect(atRule?.type).toBe(AT_RULE) expect(atRule?.name).toBe('container') const children = atRule?.children || [] expect(children.length).toBeGreaterThan(0) - expect(children[0].type).toBe(NODE_PRELUDE_CONTAINER_QUERY) + expect(children[0].type).toBe(CONTAINER_QUERY) }) it('should parse named container query', () => { @@ -125,12 +123,12 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children[0].type).toBe(NODE_PRELUDE_CONTAINER_QUERY) + expect(children[0].type).toBe(CONTAINER_QUERY) const queryChildren = children[0].children // Should have name and feature - expect(queryChildren.some((c) => c.type === NODE_PRELUDE_IDENTIFIER)).toBe(true) - expect(queryChildren.some((c) => c.type === NODE_PRELUDE_MEDIA_FEATURE)).toBe(true) + expect(queryChildren.some((c) => c.type === IDENTIFIER)).toBe(true) + expect(queryChildren.some((c) => c.type === MEDIA_FEATURE)).toBe(true) }) }) @@ -140,13 +138,13 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child - expect(atRule?.type).toBe(NODE_AT_RULE) + expect(atRule?.type).toBe(AT_RULE) expect(atRule?.name).toBe('supports') const children = atRule?.children || [] - expect(children.some((c) => c.type === NODE_PRELUDE_SUPPORTS_QUERY)).toBe(true) + expect(children.some((c) => c.type === SUPPORTS_QUERY)).toBe(true) - const query = children.find((c) => c.type === NODE_PRELUDE_SUPPORTS_QUERY) + const query = children.find((c) => c.type === SUPPORTS_QUERY) expect(query?.value).toContain('display') expect(query?.value).toContain('flex') }) @@ -156,7 +154,7 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child const children = atRule?.children || [] - const query = children.find((c) => c.type === NODE_PRELUDE_SUPPORTS_QUERY) + const query = children.find((c) => c.type === SUPPORTS_QUERY) expect(query?.value).toBe('display: flex') }) @@ -168,8 +166,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] // Should have 2 queries and 1 operator - const queries = children.filter((c) => c.type === NODE_PRELUDE_SUPPORTS_QUERY) - const operators = children.filter((c) => c.type === NODE_PRELUDE_OPERATOR) + const queries = children.filter((c) => c.type === SUPPORTS_QUERY) + const operators = children.filter((c) => c.type === PRELUDE_OPERATOR) expect(queries.length).toBe(2) expect(operators.length).toBe(1) @@ -182,13 +180,13 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child - expect(atRule?.type).toBe(NODE_AT_RULE) + expect(atRule?.type).toBe(AT_RULE) expect(atRule?.name).toBe('layer') // Filter out block node to get only prelude children - const children = atRule?.children.filter((c) => c.type !== NODE_BLOCK) || [] + const children = atRule?.children.filter((c) => c.type !== BLOCK) || [] expect(children.length).toBe(1) - expect(children[0].type).toBe(NODE_PRELUDE_LAYER_NAME) + expect(children[0].type).toBe(LAYER_NAME) expect(children[0].text).toBe('base') }) @@ -200,13 +198,13 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(3) - expect(children[0].type).toBe(NODE_PRELUDE_LAYER_NAME) + expect(children[0].type).toBe(LAYER_NAME) expect(children[0].text).toBe('base') - expect(children[1].type).toBe(NODE_PRELUDE_LAYER_NAME) + expect(children[1].type).toBe(LAYER_NAME) expect(children[1].text).toBe('components') - expect(children[2].type).toBe(NODE_PRELUDE_LAYER_NAME) + expect(children[2].type).toBe(LAYER_NAME) expect(children[2].text).toBe('utilities') }) }) @@ -217,13 +215,13 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child - expect(atRule?.type).toBe(NODE_AT_RULE) + expect(atRule?.type).toBe(AT_RULE) expect(atRule?.name).toBe('keyframes') // Filter out block node to get only prelude children - const children = atRule?.children.filter((c) => c.type !== NODE_BLOCK) || [] + const children = atRule?.children.filter((c) => c.type !== BLOCK) || [] expect(children.length).toBe(1) - expect(children[0].type).toBe(NODE_PRELUDE_IDENTIFIER) + expect(children[0].type).toBe(IDENTIFIER) expect(children[0].text).toBe('slidein') }) }) @@ -234,13 +232,13 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child - expect(atRule?.type).toBe(NODE_AT_RULE) + expect(atRule?.type).toBe(AT_RULE) expect(atRule?.name).toBe('property') // Filter out block node to get only prelude children - const children = atRule?.children.filter((c) => c.type !== NODE_BLOCK) || [] + const children = atRule?.children.filter((c) => c.type !== BLOCK) || [] expect(children.length).toBe(1) - expect(children[0].type).toBe(NODE_PRELUDE_IDENTIFIER) + expect(children[0].type).toBe(IDENTIFIER) expect(children[0].text).toBe('--my-color') }) }) @@ -251,14 +249,14 @@ describe('At-Rule Prelude Parser', () => { const ast = parse(css) const atRule = ast.first_child - expect(atRule?.type).toBe(NODE_AT_RULE) + expect(atRule?.type).toBe(AT_RULE) expect(atRule?.name).toBe('font-face') // @font-face has no prelude, children should be declarations const children = atRule?.children || [] if (children.length > 0) { // If parse_values is enabled, there might be declaration children - expect(children[0].type).not.toBe(NODE_PRELUDE_IDENTIFIER) + expect(children[0].type).not.toBe(IDENTIFIER) } }) }) @@ -270,7 +268,7 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children.some((c) => c.type === NODE_PRELUDE_MEDIA_QUERY)).toBe(true) + expect(children.some((c) => c.type === MEDIA_QUERY)).toBe(true) }) it('should not parse preludes when disabled', () => { @@ -279,7 +277,7 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children.some((c) => c.type === NODE_PRELUDE_MEDIA_QUERY)).toBe(false) + expect(children.some((c) => c.type === MEDIA_QUERY)).toBe(false) }) }) @@ -302,7 +300,7 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBeGreaterThan(0) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) + expect(children[0].type).toBe(URL) expect(children[0].text).toBe('url("styles.css")') }) @@ -313,7 +311,7 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBeGreaterThan(0) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) + expect(children[0].type).toBe(URL) expect(children[0].text).toBe('"styles.css"') }) @@ -324,8 +322,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(LAYER_NAME) expect(children[1].text).toBe('layer') expect(children[1].name).toBe('') }) @@ -337,8 +335,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(LAYER_NAME) expect(children[1].text).toBe('LAYER') expect(children[1].name).toBe('') }) @@ -350,8 +348,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(LAYER_NAME) expect(children[1].text).toBe('layer(utilities)') expect(children[1].name).toBe('utilities') }) @@ -362,7 +360,7 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) + expect(children[1].type).toBe(LAYER_NAME) expect(children[1].name).toBe('utilities') }) @@ -372,7 +370,7 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) + expect(children[1].type).toBe(LAYER_NAME) expect(children[1].name).toBe('utilities') }) @@ -382,7 +380,7 @@ describe('At-Rule Prelude Parser', () => { const atRule = ast.first_child const children = atRule?.children || [] - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) + expect(children[1].type).toBe(LAYER_NAME) expect(children[1].name).toBe('named.nested') }) @@ -393,8 +391,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_SUPPORTS) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(SUPPORTS_QUERY) expect(children[1].text).toBe('supports(display: grid)') }) @@ -405,8 +403,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(MEDIA_QUERY) }) it('should parse with media feature', () => { @@ -416,8 +414,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(MEDIA_QUERY) }) it('should parse with combined media query', () => { @@ -427,8 +425,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(MEDIA_QUERY) }) it('should parse with layer and media query', () => { @@ -438,9 +436,9 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(3) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) - expect(children[2].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(LAYER_NAME) + expect(children[2].type).toBe(MEDIA_QUERY) }) it('should parse with layer and supports', () => { @@ -450,9 +448,9 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(3) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) - expect(children[2].type).toBe(NODE_PRELUDE_IMPORT_SUPPORTS) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(LAYER_NAME) + expect(children[2].type).toBe(SUPPORTS_QUERY) }) it('should parse with supports and media query', () => { @@ -462,9 +460,9 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(3) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_SUPPORTS) - expect(children[2].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(SUPPORTS_QUERY) + expect(children[2].type).toBe(MEDIA_QUERY) }) it('should parse with all features combined', () => { @@ -474,10 +472,10 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(4) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) - expect(children[2].type).toBe(NODE_PRELUDE_IMPORT_SUPPORTS) - expect(children[3].type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(LAYER_NAME) + expect(children[2].type).toBe(SUPPORTS_QUERY) + expect(children[3].type).toBe(MEDIA_QUERY) }) it('should parse with complex supports condition', () => { @@ -487,8 +485,8 @@ describe('At-Rule Prelude Parser', () => { const children = atRule?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(children[1].type).toBe(NODE_PRELUDE_IMPORT_SUPPORTS) + expect(children[0].type).toBe(URL) + expect(children[1].type).toBe(SUPPORTS_QUERY) expect(children[1].text).toContain('supports(') }) @@ -574,7 +572,7 @@ describe('parse_atrule_prelude()', () => { const result = parse_atrule_prelude('layer', 'utilities') expect(result.length).toBe(1) - expect(result[0].type).toBe(NODE_PRELUDE_LAYER_NAME) + expect(result[0].type).toBe(LAYER_NAME) expect(result[0].text).toBe('utilities') }) @@ -596,7 +594,7 @@ describe('parse_atrule_prelude()', () => { const result = parse_atrule_prelude('keyframes', 'slide-in') expect(result.length).toBe(1) - expect(result[0].type).toBe(NODE_PRELUDE_IDENTIFIER) + expect(result[0].type).toBe(IDENTIFIER) expect(result[0].text).toBe('slide-in') }) @@ -613,7 +611,7 @@ describe('parse_atrule_prelude()', () => { const result = parse_atrule_prelude('property', '--my-color') expect(result.length).toBe(1) - expect(result[0].type).toBe(NODE_PRELUDE_IDENTIFIER) + expect(result[0].type).toBe(IDENTIFIER) expect(result[0].text).toBe('--my-color') }) }) @@ -623,7 +621,7 @@ describe('parse_atrule_prelude()', () => { const result = parse_atrule_prelude('import', 'url("styles.css")') expect(result.length).toBeGreaterThan(0) - expect(result[0].type).toBe(NODE_PRELUDE_IMPORT_URL) + expect(result[0].type).toBe(URL) expect(result[0].text).toBe('url("styles.css")') }) @@ -631,7 +629,7 @@ describe('parse_atrule_prelude()', () => { const result = parse_atrule_prelude('import', '"styles.css"') expect(result.length).toBeGreaterThan(0) - expect(result[0].type).toBe(NODE_PRELUDE_IMPORT_URL) + expect(result[0].type).toBe(URL) expect(result[0].text).toBe('"styles.css"') }) @@ -639,16 +637,16 @@ describe('parse_atrule_prelude()', () => { const result = parse_atrule_prelude('import', 'url("base.css") layer(framework)') expect(result.length).toBeGreaterThanOrEqual(2) - expect(result[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(result[1].type).toBe(NODE_PRELUDE_IMPORT_LAYER) + expect(result[0].type).toBe(URL) + expect(result[1].type).toBe(LAYER_NAME) }) test('should parse import with supports', () => { const result = parse_atrule_prelude('import', 'url("modern.css") supports(display: grid)') expect(result.length).toBeGreaterThanOrEqual(2) - expect(result[0].type).toBe(NODE_PRELUDE_IMPORT_URL) - expect(result[1].type).toBe(NODE_PRELUDE_IMPORT_SUPPORTS) + expect(result[0].type).toBe(URL) + expect(result[1].type).toBe(SUPPORTS_QUERY) }) }) @@ -837,7 +835,7 @@ describe('parse_atrule_prelude()', () => { // First child should be media query const mediaQuery = children[0] - expect(mediaQuery.type).toBe(NODE_PRELUDE_MEDIA_QUERY) + expect(mediaQuery.type).toBe(MEDIA_QUERY) expect(mediaQuery.text).toBe('screen and (min-width: 768px)') expect(mediaQuery.text.length).toBe(29) }) @@ -850,7 +848,7 @@ describe('parse_atrule_prelude()', () => { const mediaQuery = children[0] const queryChildren = mediaQuery?.children || [] - const mediaType = queryChildren.find((c) => c.type === NODE_PRELUDE_MEDIA_TYPE) + const mediaType = queryChildren.find((c) => c.type === MEDIA_TYPE) expect(mediaType?.text).toBe('screen') expect(mediaType?.text.length).toBe(6) }) @@ -863,7 +861,7 @@ describe('parse_atrule_prelude()', () => { const mediaQuery = children[0] const queryChildren = mediaQuery?.children || [] - const mediaFeature = queryChildren.find((c) => c.type === NODE_PRELUDE_MEDIA_FEATURE) + const mediaFeature = queryChildren.find((c) => c.type === MEDIA_FEATURE) expect(mediaFeature?.text).toBe('(min-width: 768px)') expect(mediaFeature?.text.length).toBe(18) }) @@ -874,7 +872,7 @@ describe('parse_atrule_prelude()', () => { const atRule = ast.first_child const children = atRule?.children || [] - const containerQuery = children.find((c) => c.type === NODE_PRELUDE_CONTAINER_QUERY) + const containerQuery = children.find((c) => c.type === CONTAINER_QUERY) expect(containerQuery?.text).toBe('sidebar (min-width: 400px)') expect(containerQuery?.text.length).toBe(26) }) @@ -885,7 +883,7 @@ describe('parse_atrule_prelude()', () => { const atRule = ast.first_child const children = atRule?.children || [] - const supportsQuery = children.find((c) => c.type === NODE_PRELUDE_SUPPORTS_QUERY) + const supportsQuery = children.find((c) => c.type === SUPPORTS_QUERY) expect(supportsQuery?.text).toBe('(display: flex)') expect(supportsQuery?.text.length).toBe(15) }) @@ -896,7 +894,7 @@ describe('parse_atrule_prelude()', () => { const atRule = ast.first_child const children = atRule?.children || [] - const layerName = children.find((c) => c.type === NODE_PRELUDE_LAYER_NAME) + const layerName = children.find((c) => c.type === LAYER_NAME) expect(layerName?.text).toBe('utilities') expect(layerName?.text.length).toBe(9) }) @@ -907,7 +905,7 @@ describe('parse_atrule_prelude()', () => { const atRule = ast.first_child const children = atRule?.children || [] - const importUrl = children.find((c) => c.type === NODE_PRELUDE_IMPORT_URL) + const importUrl = children.find((c) => c.type === URL) expect(importUrl?.text).toBe('url("styles.css")') expect(importUrl?.text.length).toBe(17) }) @@ -918,7 +916,7 @@ describe('parse_atrule_prelude()', () => { const atRule = ast.first_child const children = atRule?.children || [] - const importLayer = children.find((c) => c.type === NODE_PRELUDE_IMPORT_LAYER) + const importLayer = children.find((c) => c.type === LAYER_NAME) expect(importLayer?.text).toBe('layer(utilities)') expect(importLayer?.text.length).toBe(16) }) @@ -929,7 +927,7 @@ describe('parse_atrule_prelude()', () => { const atRule = ast.first_child const children = atRule?.children || [] - const importSupports = children.find((c) => c.type === NODE_PRELUDE_IMPORT_SUPPORTS) + const importSupports = children.find((c) => c.type === SUPPORTS_QUERY) expect(importSupports?.text).toBe('supports(display: flex)') expect(importSupports?.text.length).toBe(23) }) @@ -942,7 +940,7 @@ describe('parse_atrule_prelude()', () => { const mediaQuery = children[0] const queryChildren = mediaQuery?.children || [] - const operator = queryChildren.find((c) => c.type === NODE_PRELUDE_OPERATOR) + const operator = queryChildren.find((c) => c.type === PRELUDE_OPERATOR) expect(operator?.text).toBe('and') expect(operator?.text.length).toBe(3) }) diff --git a/src/parse-atrule-prelude.ts b/src/parse-atrule-prelude.ts index 4c6c0e9..f98c089 100644 --- a/src/parse-atrule-prelude.ts +++ b/src/parse-atrule-prelude.ts @@ -2,17 +2,15 @@ import { Lexer } from './lexer' import { CSSDataArena, - NODE_PRELUDE_MEDIA_QUERY, - NODE_PRELUDE_MEDIA_FEATURE, - NODE_PRELUDE_MEDIA_TYPE, - NODE_PRELUDE_CONTAINER_QUERY, - NODE_PRELUDE_SUPPORTS_QUERY, - NODE_PRELUDE_LAYER_NAME, - NODE_PRELUDE_IDENTIFIER, - NODE_PRELUDE_OPERATOR, - NODE_PRELUDE_IMPORT_URL, - NODE_PRELUDE_IMPORT_LAYER, - NODE_PRELUDE_IMPORT_SUPPORTS, + MEDIA_QUERY, + MEDIA_FEATURE, + MEDIA_TYPE, + CONTAINER_QUERY, + SUPPORTS_QUERY, + LAYER_NAME, + IDENTIFIER, + PRELUDE_OPERATOR, + URL, } from './arena' import { TOKEN_IDENT, @@ -165,11 +163,11 @@ export class AtRulePreludeParser { if (this.is_and_or_not(text)) { // Logical operator - let op = this.create_node(NODE_PRELUDE_OPERATOR, this.lexer.token_start, this.lexer.token_end) + let op = this.create_node(PRELUDE_OPERATOR, this.lexer.token_start, this.lexer.token_end) components.push(op) } else { // Media type: screen, print, all - let media_type = this.create_node(NODE_PRELUDE_MEDIA_TYPE, this.lexer.token_start, this.lexer.token_end) + let media_type = this.create_node(MEDIA_TYPE, this.lexer.token_start, this.lexer.token_end) components.push(media_type) } } else { @@ -181,7 +179,7 @@ export class AtRulePreludeParser { if (components.length === 0) return null // Create media query node - let query_node = this.create_node(NODE_PRELUDE_MEDIA_QUERY, query_start, this.lexer.pos) + let query_node = this.create_node(MEDIA_QUERY, query_start, this.lexer.pos) // Append components as children for (let component of components) { @@ -215,7 +213,7 @@ export class AtRulePreludeParser { let feature_end = this.lexer.token_end // After ')' // Create media feature node - let feature = this.create_node(NODE_PRELUDE_MEDIA_FEATURE, feature_start, feature_end) + let feature = this.create_node(MEDIA_FEATURE, feature_start, feature_end) // Store feature content (without parentheses) in value fields, trimmed let trimmed = trim_boundaries(this.source, content_start, content_end) @@ -255,11 +253,11 @@ export class AtRulePreludeParser { if (this.is_and_or_not(text)) { // Logical operator - let op = this.create_node(NODE_PRELUDE_OPERATOR, this.lexer.token_start, this.lexer.token_end) + let op = this.create_node(PRELUDE_OPERATOR, this.lexer.token_start, this.lexer.token_end) components.push(op) } else { // Container name or other identifier - let name = this.create_node(NODE_PRELUDE_IDENTIFIER, this.lexer.token_start, this.lexer.token_end) + let name = this.create_node(IDENTIFIER, this.lexer.token_start, this.lexer.token_end) components.push(name) } } @@ -268,7 +266,7 @@ export class AtRulePreludeParser { if (components.length === 0) return [] // Create container query node - let query_node = this.create_node(NODE_PRELUDE_CONTAINER_QUERY, query_start, this.lexer.pos) + let query_node = this.create_node(CONTAINER_QUERY, query_start, this.lexer.pos) // Append components as children for (let component of components) { @@ -313,7 +311,7 @@ export class AtRulePreludeParser { let feature_end = this.lexer.token_end // Create supports query node - let query = this.create_node(NODE_PRELUDE_SUPPORTS_QUERY, feature_start, feature_end) + let query = this.create_node(SUPPORTS_QUERY, feature_start, feature_end) // Store query content in value fields, trimmed let trimmed = trim_boundaries(this.source, content_start, content_end) @@ -330,7 +328,7 @@ export class AtRulePreludeParser { let text = this.source.substring(this.lexer.token_start, this.lexer.token_end) if (this.is_and_or_not(text)) { - let op = this.create_node(NODE_PRELUDE_OPERATOR, this.lexer.token_start, this.lexer.token_end) + let op = this.create_node(PRELUDE_OPERATOR, this.lexer.token_start, this.lexer.token_end) nodes.push(op) } } @@ -352,7 +350,7 @@ export class AtRulePreludeParser { let token_type = this.lexer.token_type if (token_type === TOKEN_IDENT) { // Layer name - let layer = this.create_node(NODE_PRELUDE_LAYER_NAME, this.lexer.token_start, this.lexer.token_end) + let layer = this.create_node(LAYER_NAME, this.lexer.token_start, this.lexer.token_end) nodes.push(layer) } else if (token_type === TOKEN_COMMA) { // Skip comma separator @@ -376,7 +374,7 @@ export class AtRulePreludeParser { if (this.lexer.token_type !== TOKEN_IDENT) return [] // Create identifier node - let ident = this.create_node(NODE_PRELUDE_IDENTIFIER, this.lexer.token_start, this.lexer.token_end) + let ident = this.create_node(IDENTIFIER, this.lexer.token_start, this.lexer.token_end) return [ident] } @@ -458,7 +456,7 @@ export class AtRulePreludeParser { } // Create URL node - let url_node = this.create_node(NODE_PRELUDE_IMPORT_URL, url_start, url_end) + let url_node = this.create_node(URL, url_start, url_end) return url_node } @@ -505,7 +503,7 @@ export class AtRulePreludeParser { } // Create layer node - let layer_node = this.create_node(NODE_PRELUDE_IMPORT_LAYER, layer_start, layer_end) + let layer_node = this.create_node(LAYER_NAME, layer_start, layer_end) // Store the layer name (content inside parentheses), trimmed if (content_length > 0) { @@ -557,7 +555,7 @@ export class AtRulePreludeParser { } // Create supports node - let supports_node = this.create_node(NODE_PRELUDE_IMPORT_SUPPORTS, supports_start, supports_end) + let supports_node = this.create_node(SUPPORTS_QUERY, supports_start, supports_end) return supports_node } diff --git a/src/parse-options.test.ts b/src/parse-options.test.ts index a0c1f85..85a1bd7 100644 --- a/src/parse-options.test.ts +++ b/src/parse-options.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' import { Parser } from './parse' -import { NODE_SELECTOR_LIST, NODE_DECLARATION, NODE_VALUE_KEYWORD } from './arena' +import { SELECTOR_LIST, DECLARATION, IDENTIFIER } from './arena' describe('Parser Options', () => { const css = 'body { color: red; }' @@ -15,15 +15,15 @@ describe('Parser Options', () => { // All selectors wrapped in NODE_SELECTOR_LIST const selector = rule?.first_child expect(selector).not.toBeNull() - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) // Check value is parsed with detailed structure const block = selector?.next_sibling const declaration = block?.first_child expect(declaration).not.toBeNull() - expect(declaration?.type).toBe(NODE_DECLARATION) + expect(declaration?.type).toBe(DECLARATION) expect(declaration?.has_children).toBe(true) - expect(declaration?.first_child?.type).toBe(NODE_VALUE_KEYWORD) + expect(declaration?.first_child?.type).toBe(IDENTIFIER) }) it('should parse values and selectors with explicit options', () => { @@ -34,13 +34,13 @@ describe('Parser Options', () => { // Check selector is parsed // Simple selector (just "body") returns NODE_SELECTOR_LIST directly const selector = rule?.first_child - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) // Check value is parsed const block = selector?.next_sibling const declaration = block?.first_child expect(declaration?.has_children).toBe(true) - expect(declaration?.first_child?.type).toBe(NODE_VALUE_KEYWORD) + expect(declaration?.first_child?.type).toBe(IDENTIFIER) }) }) @@ -54,13 +54,13 @@ describe('Parser Options', () => { // Simple selector (just "body") returns NODE_SELECTOR_LIST directly const selector = rule?.first_child expect(selector).not.toBeNull() - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) // Declaration should exist but have no value children const block = selector?.next_sibling const declaration = block?.first_child expect(declaration).not.toBeNull() - expect(declaration?.type).toBe(NODE_DECLARATION) + expect(declaration?.type).toBe(DECLARATION) expect(declaration?.property).toBe('color') expect(declaration?.value).toBe('red') // Raw value text still available expect(declaration?.has_children).toBe(false) // No detailed value nodes @@ -102,7 +102,7 @@ describe('Parser Options', () => { // Selector should exist but be simple (just NODE_SELECTOR_LIST, no detailed structure) const selector = rule?.first_child expect(selector).not.toBeNull() - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) expect(selector?.text).toBe('body') expect(selector?.has_children).toBe(false) // No detailed selector nodes @@ -110,7 +110,7 @@ describe('Parser Options', () => { const block = selector?.next_sibling const declaration = block?.first_child expect(declaration?.has_children).toBe(true) - expect(declaration?.first_child?.type).toBe(NODE_VALUE_KEYWORD) + expect(declaration?.first_child?.type).toBe(IDENTIFIER) }) it('should handle complex selectors without parsing', () => { @@ -119,7 +119,7 @@ describe('Parser Options', () => { const rule = root.first_child const selector = rule?.first_child - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) expect(selector?.text).toBe('div.container#app') expect(selector?.has_children).toBe(false) }) @@ -130,7 +130,7 @@ describe('Parser Options', () => { const rule = root.first_child const selector = rule?.first_child - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) expect(selector?.text).toBe('div, p, span') expect(selector?.has_children).toBe(false) }) @@ -144,14 +144,14 @@ describe('Parser Options', () => { // Selector should be simple const selector = rule?.first_child - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) expect(selector?.text).toBe('body') expect(selector?.has_children).toBe(false) // Declaration should have no value children const block = selector?.next_sibling const declaration = block?.first_child - expect(declaration?.type).toBe(NODE_DECLARATION) + expect(declaration?.type).toBe(DECLARATION) expect(declaration?.property).toBe('color') expect(declaration?.value).toBe('red') expect(declaration?.has_children).toBe(false) @@ -169,7 +169,7 @@ describe('Parser Options', () => { const rule = root.first_child const selector = rule?.first_child - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) expect(selector?.has_children).toBe(false) const block = selector?.next_sibling @@ -244,7 +244,7 @@ describe('Parser Options', () => { const declaration = block?.first_child // Should use defaults (both enabled) - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) expect(declaration?.has_children).toBe(true) }) @@ -257,7 +257,7 @@ describe('Parser Options', () => { const declaration = block?.first_child // Selector should still be parsed (default true) - expect(selector?.type).toBe(NODE_SELECTOR_LIST) + expect(selector?.type).toBe(SELECTOR_LIST) // Values should not be parsed (explicitly false) expect(declaration?.has_children).toBe(false) }) diff --git a/src/parse-selector.test.ts b/src/parse-selector.test.ts index d76f5b7..4350132 100644 --- a/src/parse-selector.test.ts +++ b/src/parse-selector.test.ts @@ -2,20 +2,20 @@ import { describe, it, expect, test } from 'vitest' import { SelectorParser, parse_selector } from './parse-selector' import { ATTR_OPERATOR_EQUAL, CSSDataArena } from './arena' import { - NODE_SELECTOR, - NODE_SELECTOR_LIST, - NODE_SELECTOR_TYPE, - NODE_SELECTOR_CLASS, - NODE_SELECTOR_ID, - NODE_SELECTOR_ATTRIBUTE, - NODE_SELECTOR_PSEUDO_CLASS, - NODE_SELECTOR_PSEUDO_ELEMENT, - NODE_SELECTOR_COMBINATOR, - NODE_SELECTOR_UNIVERSAL, - NODE_SELECTOR_NESTING, - NODE_SELECTOR_NTH, - NODE_SELECTOR_NTH_OF, - NODE_SELECTOR_LANG, + SELECTOR, + 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, ATTR_FLAG_NONE, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, @@ -26,19 +26,19 @@ describe('parse_selector() function', () => { it('should parse and return a CSSNode', () => { const node = parse_selector('div.container') expect(node).toBeDefined() - expect(node.type).toBe(NODE_SELECTOR_LIST) + expect(node.type).toBe(SELECTOR_LIST) expect(node.text).toBe('div.container') }) it('should parse type selector', () => { const node = parse_selector('div') - expect(node.type).toBe(NODE_SELECTOR_LIST) + expect(node.type).toBe(SELECTOR_LIST) const firstSelector = node.first_child - expect(firstSelector?.type).toBe(NODE_SELECTOR) + expect(firstSelector?.type).toBe(SELECTOR) const typeNode = firstSelector?.first_child - expect(typeNode?.type).toBe(NODE_SELECTOR_TYPE) + expect(typeNode?.type).toBe(TYPE_SELECTOR) expect(typeNode?.text).toBe('div') }) @@ -47,7 +47,7 @@ describe('parse_selector() function', () => { const firstSelector = node.first_child const classNode = firstSelector?.first_child - expect(classNode?.type).toBe(NODE_SELECTOR_CLASS) + expect(classNode?.type).toBe(CLASS_SELECTOR) expect(classNode?.name).toBe('.my-class') }) @@ -56,7 +56,7 @@ describe('parse_selector() function', () => { const firstSelector = node.first_child const idNode = firstSelector?.first_child - expect(idNode?.type).toBe(NODE_SELECTOR_ID) + expect(idNode?.type).toBe(ID_SELECTOR) expect(idNode?.name).toBe('#my-id') }) @@ -66,9 +66,9 @@ describe('parse_selector() function', () => { const children = firstSelector?.children || [] expect(children.length).toBe(3) - expect(children[0].type).toBe(NODE_SELECTOR_TYPE) - expect(children[1].type).toBe(NODE_SELECTOR_CLASS) - expect(children[2].type).toBe(NODE_SELECTOR_ID) + expect(children[0].type).toBe(TYPE_SELECTOR) + expect(children[1].type).toBe(CLASS_SELECTOR) + expect(children[2].type).toBe(ID_SELECTOR) }) it('should parse complex selector with descendant combinator', () => { @@ -77,9 +77,9 @@ describe('parse_selector() function', () => { const children = firstSelector?.children || [] expect(children.length).toBe(3) // div, combinator, .container - expect(children[0].type).toBe(NODE_SELECTOR_TYPE) - expect(children[1].type).toBe(NODE_SELECTOR_COMBINATOR) - expect(children[2].type).toBe(NODE_SELECTOR_CLASS) + expect(children[0].type).toBe(TYPE_SELECTOR) + expect(children[1].type).toBe(COMBINATOR) + expect(children[2].type).toBe(CLASS_SELECTOR) }) it('should parse selector list', () => { @@ -87,9 +87,9 @@ describe('parse_selector() function', () => { const selectors = node.children expect(selectors.length).toBe(3) - expect(selectors[0].first_child?.type).toBe(NODE_SELECTOR_TYPE) - expect(selectors[1].first_child?.type).toBe(NODE_SELECTOR_TYPE) - expect(selectors[2].first_child?.type).toBe(NODE_SELECTOR_TYPE) + expect(selectors[0].first_child?.type).toBe(TYPE_SELECTOR) + expect(selectors[1].first_child?.type).toBe(TYPE_SELECTOR) + expect(selectors[2].first_child?.type).toBe(TYPE_SELECTOR) }) }) @@ -139,16 +139,16 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) expect(getNodeText(arena, source, rootNode)).toBe('div') // First child is NODE_SELECTOR wrapper const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) // First child of wrapper is the actual type const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(child)).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, child)).toBe('div') }) @@ -159,13 +159,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(child)).toBe(CLASS_SELECTOR) expect(getNodeText(arena, source, child)).toBe('.my-class') expect(getNodeContent(arena, source, child)).toBe('.my-class') }) @@ -177,13 +177,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ID) + expect(arena.get_type(child)).toBe(ID_SELECTOR) expect(getNodeText(arena, source, child)).toBe('#my-id') expect(getNodeContent(arena, source, child)).toBe('#my-id') }) @@ -195,13 +195,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_UNIVERSAL) + expect(arena.get_type(child)).toBe(UNIVERSAL_SELECTOR) expect(getNodeText(arena, source, child)).toBe('*') }) @@ -212,13 +212,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_NESTING) + expect(arena.get_type(child)).toBe(NESTING_SELECTOR) expect(getNodeText(arena, source, child)).toBe('&') }) }) @@ -231,18 +231,18 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) // Get the NODE_SELECTOR wrapper const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) // Compound selector has multiple children const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[0])).toBe('div') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[1])).toBe(CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('.container') }) @@ -253,13 +253,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_ID) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(ID_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('#app') }) @@ -272,12 +272,12 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(4) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('.foo') - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[2])).toBe(CLASS_SELECTOR) expect(getNodeContent(arena, source, children[2])).toBe('.bar') - expect(arena.get_type(children[3])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[3])).toBe(CLASS_SELECTOR) expect(getNodeContent(arena, source, children[3])).toBe('.baz') }) @@ -290,11 +290,11 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(3) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[0])).toBe('div') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[1])).toBe(CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('.container') - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_ID) + expect(arena.get_type(children[2])).toBe(ID_SELECTOR) expect(getNodeContent(arena, source, children[2])).toBe('#app') }) }) @@ -309,8 +309,8 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('hover') }) @@ -323,8 +323,8 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('nth-child') expect(getNodeText(arena, source, children[1])).toBe(':nth-child(2n+1)') }) @@ -338,10 +338,10 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(3) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('focus') - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[2])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[2])).toBe('valid') }) @@ -354,7 +354,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[1])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('is') }) @@ -367,7 +367,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[1])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('not') }) }) @@ -382,8 +382,8 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_ELEMENT) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(PSEUDO_ELEMENT_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('before') }) @@ -396,8 +396,8 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('after') }) @@ -410,7 +410,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_ELEMENT) + expect(arena.get_type(children[1])).toBe(PSEUDO_ELEMENT_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('first-line') }) }) @@ -419,7 +419,7 @@ describe('SelectorParser', () => { it('should indicate :lang() has function syntax even when empty', () => { const root = parse_selector(':lang()') const pseudoClass = root.first_child!.first_child! - expect(pseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudoClass.name).toBe('lang') expect(pseudoClass.has_children).toBe(true) // Function syntax, even if empty }) @@ -427,7 +427,7 @@ describe('SelectorParser', () => { it('should indicate :lang(en) has function syntax with children', () => { const root = parse_selector(':lang(en)') const pseudoClass = root.first_child!.first_child! - expect(pseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudoClass.name).toBe('lang') expect(pseudoClass.has_children).toBe(true) // Function syntax with content }) @@ -435,7 +435,7 @@ describe('SelectorParser', () => { it('should indicate :hover has no function syntax', () => { const root = parse_selector(':hover') const pseudoClass = root.first_child!.first_child! - expect(pseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudoClass.name).toBe('hover') expect(pseudoClass.has_children).toBe(false) // Not a function }) @@ -443,7 +443,7 @@ describe('SelectorParser', () => { it('should indicate :is() has function syntax even when empty', () => { const root = parse_selector(':is()') const pseudoClass = root.first_child!.first_child! - expect(pseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudoClass.name).toBe('is') expect(pseudoClass.has_children).toBe(true) // Function syntax, even if empty }) @@ -451,7 +451,7 @@ describe('SelectorParser', () => { it('should indicate :has() has function syntax even when empty', () => { const root = parse_selector(':has()') const pseudoClass = root.first_child!.first_child! - expect(pseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudoClass.name).toBe('has') expect(pseudoClass.has_children).toBe(true) // Function syntax, even if empty }) @@ -459,7 +459,7 @@ describe('SelectorParser', () => { it('should indicate :nth-child() has function syntax even when empty', () => { const root = parse_selector(':nth-child()') const pseudoClass = root.first_child!.first_child! - expect(pseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudoClass.name).toBe('nth-child') expect(pseudoClass.has_children).toBe(true) // Function syntax, even if empty }) @@ -467,7 +467,7 @@ describe('SelectorParser', () => { it('should indicate ::before has no function syntax', () => { const root = parse_selector('::before') const pseudoElement = root.first_child!.first_child! - expect(pseudoElement.type).toBe(NODE_SELECTOR_PSEUDO_ELEMENT) + expect(pseudoElement.type).toBe(PSEUDO_ELEMENT_SELECTOR) expect(pseudoElement.name).toBe('before') expect(pseudoElement.has_children).toBe(false) // Not a function }) @@ -475,7 +475,7 @@ describe('SelectorParser', () => { it('should indicate ::slotted() has function syntax even when empty', () => { const root = parse_selector('::slotted()') const pseudoElement = root.first_child!.first_child! - expect(pseudoElement.type).toBe(NODE_SELECTOR_PSEUDO_ELEMENT) + expect(pseudoElement.type).toBe(PSEUDO_ELEMENT_SELECTOR) expect(pseudoElement.name).toBe('slotted') expect(pseudoElement.has_children).toBe(true) // Function syntax, even if empty }) @@ -489,13 +489,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(getNodeText(arena, source, child)).toBe('[disabled]') expect(getNodeContent(arena, source, child)).toBe('disabled') }) @@ -507,13 +507,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(getNodeText(arena, source, child)).toBe('[type="text"]') // Content now stores just the attribute name expect(getNodeContent(arena, source, child)).toBe('type') @@ -526,13 +526,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(getNodeText(arena, source, child)).toBe('[class^="btn-"]') }) @@ -545,8 +545,8 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_TYPE) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(children[0])).toBe(TYPE_SELECTOR) + expect(arena.get_type(children[1])).toBe(ATTRIBUTE_SELECTOR) }) it('should trim whitespace from attribute selectors', () => { @@ -557,7 +557,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) // Content now stores just the attribute name expect(getNodeContent(arena, source, child)).toBe('data-test') // Full text still includes brackets @@ -572,7 +572,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) // Content now stores just the attribute name expect(getNodeContent(arena, source, child)).toBe('data-test') }) @@ -585,7 +585,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) // Content now stores just the attribute name expect(getNodeContent(arena, source, child)).toBe('data-test') }) @@ -598,7 +598,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(getNodeText(arena, source, child)).toBe('[type="text" i]') expect(getNodeContent(arena, source, child)).toBe('type') expect(arena.get_attr_flags(child)).toBe(ATTR_FLAG_CASE_INSENSITIVE) @@ -610,11 +610,11 @@ describe('SelectorParser', () => { expect(root).not.toBeNull() if (!root) return - expect(root.type).toBe(NODE_SELECTOR_LIST) + expect(root.type).toBe(SELECTOR_LIST) let selector = root.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) let attr = selector.first_child! - expect(attr.type).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(attr.type).toBe(ATTRIBUTE_SELECTOR) expect(attr.attr_flags).toBe(ATTR_FLAG_CASE_INSENSITIVE) expect(attr.attr_operator).toBe(ATTR_OPERATOR_EQUAL) }) @@ -627,7 +627,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(getNodeText(arena, source, child)).toBe('[type="text" s]') expect(getNodeContent(arena, source, child)).toBe('type') expect(arena.get_attr_flags(child)).toBe(ATTR_FLAG_CASE_SENSITIVE) @@ -641,7 +641,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(arena.get_attr_flags(child)).toBe(ATTR_FLAG_CASE_INSENSITIVE) }) @@ -653,7 +653,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(arena.get_attr_flags(child)).toBe(ATTR_FLAG_CASE_INSENSITIVE) }) @@ -665,7 +665,7 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(arena.get_type(child)).toBe(ATTRIBUTE_SELECTOR) expect(arena.get_attr_flags(child)).toBe(ATTR_FLAG_NONE) }) @@ -707,7 +707,7 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) @@ -716,7 +716,7 @@ describe('SelectorParser', () => { // Should have: compound(div), combinator(space), compound(p) const hasDescendantCombinator = children.some((child) => { const type = arena.get_type(child) - return type === NODE_SELECTOR_COMBINATOR + return type === COMBINATOR }) expect(hasDescendantCombinator).toBe(true) }) @@ -732,7 +732,7 @@ describe('SelectorParser', () => { const hasCombinator = children.some((child) => { const type = arena.get_type(child) - if (type === NODE_SELECTOR_COMBINATOR) { + if (type === COMBINATOR) { return getNodeText(arena, source, child).includes('>') } return false @@ -751,7 +751,7 @@ describe('SelectorParser', () => { const hasCombinator = children.some((child) => { const type = arena.get_type(child) - if (type === NODE_SELECTOR_COMBINATOR) { + if (type === COMBINATOR) { return getNodeText(arena, source, child).includes('+') } return false @@ -770,7 +770,7 @@ describe('SelectorParser', () => { const hasCombinator = children.some((child) => { const type = arena.get_type(child) - if (type === NODE_SELECTOR_COMBINATOR) { + if (type === COMBINATOR) { return getNodeText(arena, source, child).includes('~') } return false @@ -787,7 +787,7 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) // List contains the two selectors const children = getChildren(arena, source, rootNode) @@ -801,7 +801,7 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) // List contains the three selectors const children = getChildren(arena, source, rootNode) @@ -815,7 +815,7 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) // List contains 3 NODE_SELECTOR wrappers: div.container, .wrapper > p, #app const children = getChildren(arena, source, rootNode) @@ -831,7 +831,7 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) }) it('should parse form selector', () => { @@ -863,7 +863,7 @@ describe('SelectorParser', () => { const children = getChildren(arena, source, selectorWrapper) const combinators = children.filter((child) => { - return arena.get_type(child) === NODE_SELECTOR_COMBINATOR + return arena.get_type(child) === COMBINATOR }) expect(combinators.length).toBeGreaterThan(0) @@ -878,67 +878,67 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(child)).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, child)).toBe('where') }) it('should parse :has(a) pseudo-class', () => { const root = parse_selector('div:has(a)') - expect(root.first_child?.type).toBe(NODE_SELECTOR) + expect(root.first_child?.type).toBe(SELECTOR) expect(root.first_child!.children).toHaveLength(2) const [_, has] = root.first_child!.children - expect(has.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(has.type).toBe(PSEUDO_CLASS_SELECTOR) expect(has.text).toBe(':has(a)') // Check children of :has() - should contain selector list with > combinator and p type selector expect(has.has_children).toBe(true) const selectorList = has.first_child! - expect(selectorList.type).toBe(NODE_SELECTOR_LIST) + expect(selectorList.type).toBe(SELECTOR_LIST) // Selector list contains one selector const selector = selectorList.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) const selectorParts = selector.children expect(selectorParts).toHaveLength(1) - expect(selectorParts[0].type).toBe(NODE_SELECTOR_TYPE) + expect(selectorParts[0].type).toBe(TYPE_SELECTOR) expect(selectorParts[0].text).toBe('a') }) it('should parse :has(> p) pseudo-class', () => { const root = parse_selector('div:has(> p)') - expect(root.first_child?.type).toBe(NODE_SELECTOR) + expect(root.first_child?.type).toBe(SELECTOR) expect(root.first_child!.children).toHaveLength(2) const [div, has] = root.first_child!.children - expect(div.type).toBe(NODE_SELECTOR_TYPE) + expect(div.type).toBe(TYPE_SELECTOR) expect(div.text).toBe('div') - expect(has.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(has.type).toBe(PSEUDO_CLASS_SELECTOR) expect(has.text).toBe(':has(> p)') // Check children of :has() - should contain selector list with > combinator and p type selector expect(has.has_children).toBe(true) const selectorList = has.first_child! - expect(selectorList.type).toBe(NODE_SELECTOR_LIST) + expect(selectorList.type).toBe(SELECTOR_LIST) // Selector list contains one selector const selector = selectorList.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) const selectorParts = selector.children expect(selectorParts).toHaveLength(2) - expect(selectorParts[0].type).toBe(NODE_SELECTOR_COMBINATOR) + expect(selectorParts[0].type).toBe(COMBINATOR) expect(selectorParts[0].text).toBe('>') - expect(selectorParts[1].type).toBe(NODE_SELECTOR_TYPE) + expect(selectorParts[1].type).toBe(TYPE_SELECTOR) expect(selectorParts[1].text).toBe('p') }) @@ -950,9 +950,9 @@ describe('SelectorParser', () => { const parts = selector.children expect(parts).toHaveLength(2) - expect(parts[0].type).toBe(NODE_SELECTOR_COMBINATOR) + expect(parts[0].type).toBe(COMBINATOR) expect(parts[0].text).toBe('+') - expect(parts[1].type).toBe(NODE_SELECTOR_TYPE) + expect(parts[1].type).toBe(TYPE_SELECTOR) expect(parts[1].text).toBe('p') }) @@ -964,9 +964,9 @@ describe('SelectorParser', () => { const parts = selector.children expect(parts).toHaveLength(2) - expect(parts[0].type).toBe(NODE_SELECTOR_COMBINATOR) + expect(parts[0].type).toBe(COMBINATOR) expect(parts[0].text).toBe('~') - expect(parts[1].type).toBe(NODE_SELECTOR_TYPE) + expect(parts[1].type).toBe(TYPE_SELECTOR) expect(parts[1].text).toBe('p') }) @@ -977,7 +977,7 @@ describe('SelectorParser', () => { const selector = selectorList.first_child! expect(selector.children).toHaveLength(1) - expect(selector.children[0].type).toBe(NODE_SELECTOR_TYPE) + expect(selector.children[0].type).toBe(TYPE_SELECTOR) expect(selector.children[0].text).toBe('p') }) @@ -1007,7 +1007,7 @@ describe('SelectorParser', () => { const root = parse_selector('div:has()') const has = root.first_child!.children[1] - expect(has.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(has.type).toBe(PSEUDO_CLASS_SELECTOR) expect(has.text).toBe(':has()') expect(has.has_children).toBe(true) // Has function syntax (parentheses) }) @@ -1021,8 +1021,8 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_NESTING) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[0])).toBe(NESTING_SELECTOR) + expect(arena.get_type(children[1])).toBe(CLASS_SELECTOR) }) it('should parse nesting selector with descendant combinator as single selector', () => { @@ -1032,7 +1032,7 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) // Should have only ONE selector, not two const selectorWrappers = getChildren(arena, source, rootNode) @@ -1042,9 +1042,9 @@ describe('SelectorParser', () => { const selectorWrapper = selectorWrappers[0] const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(3) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_NESTING) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_COMBINATOR) - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[0])).toBe(NESTING_SELECTOR) + expect(arena.get_type(children[1])).toBe(COMBINATOR) + expect(arena.get_type(children[2])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[2])).toBe('span') }) @@ -1062,10 +1062,10 @@ describe('SelectorParser', () => { const selectorWrapper = selectorWrappers[0] const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(3) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_NESTING) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(NESTING_SELECTOR) + expect(arena.get_type(children[1])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[1]).trim()).toBe('>') - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[2])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[2])).toBe('div') }) }) @@ -1085,9 +1085,9 @@ describe('SelectorParser', () => { const selectorWrapper = selectorWrappers[0] const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('>') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('a') }) @@ -1103,9 +1103,9 @@ describe('SelectorParser', () => { const selectorWrapper = selectorWrappers[0] const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('+') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('div') }) @@ -1121,9 +1121,9 @@ describe('SelectorParser', () => { const selectorWrapper = selectorWrappers[0] const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('~') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('span') }) @@ -1141,16 +1141,16 @@ describe('SelectorParser', () => { // Should have: combinator (>), type (a), class (.link), id (#nav), attribute ([href]), pseudo-class (:hover) expect(children.length).toBeGreaterThanOrEqual(6) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('>') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('a') - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[2])).toBe(CLASS_SELECTOR) expect(getNodeText(arena, source, children[2])).toBe('.link') - expect(arena.get_type(children[3])).toBe(NODE_SELECTOR_ID) + expect(arena.get_type(children[3])).toBe(ID_SELECTOR) expect(getNodeText(arena, source, children[3])).toBe('#nav') - expect(arena.get_type(children[4])).toBe(NODE_SELECTOR_ATTRIBUTE) - expect(arena.get_type(children[5])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[4])).toBe(ATTRIBUTE_SELECTOR) + expect(arena.get_type(children[5])).toBe(PSEUDO_CLASS_SELECTOR) }) it('should parse multiple selectors with leading combinators', () => { @@ -1166,25 +1166,25 @@ describe('SelectorParser', () => { // First selector: > a let children = getChildren(arena, source, selectorWrappers[0]) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('>') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('a') // Second selector: ~ span children = getChildren(arena, source, selectorWrappers[1]) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('~') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('span') // Third selector: + div children = getChildren(arena, source, selectorWrappers[2]) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('+') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('div') }) @@ -1200,9 +1200,9 @@ describe('SelectorParser', () => { const selectorWrapper = selectorWrappers[0] const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('>') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('a') }) @@ -1220,12 +1220,12 @@ describe('SelectorParser', () => { // Should have: combinator (>), type (div), combinator (descendant), type (span) expect(children).toHaveLength(4) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_COMBINATOR) + expect(arena.get_type(children[0])).toBe(COMBINATOR) expect(getNodeText(arena, source, children[0]).trim()).toBe('>') - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[1])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[1])).toBe('div') - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_COMBINATOR) - expect(arena.get_type(children[3])).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(children[2])).toBe(COMBINATOR) + expect(arena.get_type(children[3])).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, children[3])).toBe('span') }) }) @@ -1267,13 +1267,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(child)).toBe(CLASS_SELECTOR) expect(getNodeContent(arena, source, child)).toBe('.my-class-123') }) @@ -1284,13 +1284,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_TYPE) + expect(arena.get_type(child)).toBe(TYPE_SELECTOR) expect(getNodeText(arena, source, child)).toBe('custom-element') }) }) @@ -1303,13 +1303,13 @@ describe('SelectorParser', () => { if (!rootNode) return // Root is NODE_SELECTOR_LIST - expect(arena.get_type(rootNode)).toBe(NODE_SELECTOR_LIST) + expect(arena.get_type(rootNode)).toBe(SELECTOR_LIST) const selectorWrapper = arena.get_first_child(rootNode) - expect(arena.get_type(selectorWrapper)).toBe(NODE_SELECTOR) + expect(arena.get_type(selectorWrapper)).toBe(SELECTOR) const child = arena.get_first_child(selectorWrapper) - expect(arena.get_type(child)).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(child)).toBe(CLASS_SELECTOR) expect(getNodeContent(arena, source, child)).toBe('.block__element--modifier') }) @@ -1322,9 +1322,9 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(3) - expect(arena.get_type(children[0])).toBe(NODE_SELECTOR_CLASS) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_CLASS) - expect(arena.get_type(children[2])).toBe(NODE_SELECTOR_CLASS) + expect(arena.get_type(children[0])).toBe(CLASS_SELECTOR) + expect(arena.get_type(children[1])).toBe(CLASS_SELECTOR) + expect(arena.get_type(children[2])).toBe(CLASS_SELECTOR) }) it('should parse table selector', () => { @@ -1346,36 +1346,36 @@ describe('SelectorParser', () => { const selectorWrapper = arena.get_first_child(rootNode) const children = getChildren(arena, source, selectorWrapper) expect(children).toHaveLength(2) - expect(arena.get_type(children[1])).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(arena.get_type(children[1])).toBe(PSEUDO_CLASS_SELECTOR) expect(getNodeContent(arena, source, children[1])).toBe('nth-of-type') }) it('should parse ul:has(:nth-child(1 of li))', () => { const root = parse_selector('ul:has(:nth-child(1 of li))') - expect(root.first_child?.type).toBe(NODE_SELECTOR) + expect(root.first_child?.type).toBe(SELECTOR) expect(root.first_child!.children).toHaveLength(2) const [ul, has] = root.first_child!.children - expect(ul.type).toBe(NODE_SELECTOR_TYPE) + expect(ul.type).toBe(TYPE_SELECTOR) expect(ul.text).toBe('ul') - expect(has.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(has.type).toBe(PSEUDO_CLASS_SELECTOR) expect(has.text).toBe(':has(:nth-child(1 of li))') }) it('should parse :nth-child(1)', () => { const root = parse_selector(':nth-child(1)') - expect(root.first_child?.type).toBe(NODE_SELECTOR) + expect(root.first_child?.type).toBe(SELECTOR) expect(root.first_child!.children).toHaveLength(1) const nth_child = root.first_child!.first_child! - expect(nth_child.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(nth_child.type).toBe(PSEUDO_CLASS_SELECTOR) expect(nth_child.text).toBe(':nth-child(1)') // Should have An+B child node expect(nth_child.children).toHaveLength(1) const anplusb = nth_child.first_child! - expect(anplusb.type).toBe(NODE_SELECTOR_NTH) + expect(anplusb.type).toBe(NTH_SELECTOR) expect(anplusb.nth_a).toBe(null) // No 'a' coefficient, just 'b' expect(anplusb.nth_b).toBe('1') }) @@ -1383,16 +1383,16 @@ describe('SelectorParser', () => { it('should parse :nth-child(2n+1)', () => { const root = parse_selector(':nth-child(2n+1)') - expect(root.first_child?.type).toBe(NODE_SELECTOR) + expect(root.first_child?.type).toBe(SELECTOR) expect(root.first_child!.children).toHaveLength(1) const nth_child = root.first_child!.first_child! - expect(nth_child.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(nth_child.type).toBe(PSEUDO_CLASS_SELECTOR) expect(nth_child.text).toBe(':nth-child(2n+1)') // Should have An+B child node expect(nth_child.children).toHaveLength(1) const anplusb = nth_child.first_child! - expect(anplusb.type).toBe(NODE_SELECTOR_NTH) + expect(anplusb.type).toBe(NTH_SELECTOR) expect(anplusb.nth_a).toBe('2n') expect(anplusb.nth_b).toBe('+1') expect(anplusb.text).toBe('2n+1') @@ -1401,30 +1401,30 @@ describe('SelectorParser', () => { it('should parse :nth-child(2n of .selector)', () => { const root = parse_selector(':nth-child(2n of .selector)') - expect(root.first_child?.type).toBe(NODE_SELECTOR) + expect(root.first_child?.type).toBe(SELECTOR) expect(root.first_child!.children).toHaveLength(1) const nth_child = root.first_child!.first_child! - expect(nth_child.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(nth_child.type).toBe(PSEUDO_CLASS_SELECTOR) expect(nth_child.text).toBe(':nth-child(2n of .selector)') // Should have NTH_OF child node (An+B with selector) expect(nth_child.children).toHaveLength(1) const nth_of = nth_child.first_child! - expect(nth_of.type).toBe(NODE_SELECTOR_NTH_OF) + expect(nth_of.type).toBe(NTH_OF_SELECTOR) expect(nth_of.text).toBe('2n of .selector') // NTH_OF has two children: An+B and selector list expect(nth_of.children).toHaveLength(2) const anplusb = nth_of.first_child! - expect(anplusb.type).toBe(NODE_SELECTOR_NTH) + expect(anplusb.type).toBe(NTH_SELECTOR) expect(anplusb.nth_a).toBe('2n') expect(anplusb.nth_b).toBe(null) // Second child is the selector list const selectorList = nth_of.children[1] - expect(selectorList.type).toBe(NODE_SELECTOR_LIST) + expect(selectorList.type).toBe(SELECTOR_LIST) const selector = selectorList.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) expect(selector.first_child!.text).toBe('.selector') }) @@ -1432,37 +1432,37 @@ describe('SelectorParser', () => { const root = parse_selector(':is(a, b)') // Root is selector list - expect(root.type).toBe(NODE_SELECTOR_LIST) + expect(root.type).toBe(SELECTOR_LIST) // First selector in the list const selector = root.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) // Selector has :is() pseudo-class const isPseudoClass = selector.first_child! - expect(isPseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(isPseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(isPseudoClass.text).toBe(':is(a, b)') // :is() has 1 child: a selector list expect(isPseudoClass.children).toHaveLength(1) const innerSelectorList = isPseudoClass.first_child! - expect(innerSelectorList.type).toBe(NODE_SELECTOR_LIST) + expect(innerSelectorList.type).toBe(SELECTOR_LIST) // The selector list has 2 children: selector for 'a' and selector for 'b' expect(innerSelectorList.children).toHaveLength(2) // First selector: 'a' const selectorA = innerSelectorList.children[0] - expect(selectorA.type).toBe(NODE_SELECTOR) + expect(selectorA.type).toBe(SELECTOR) expect(selectorA.children).toHaveLength(1) - expect(selectorA.children[0].type).toBe(NODE_SELECTOR_TYPE) + expect(selectorA.children[0].type).toBe(TYPE_SELECTOR) expect(selectorA.children[0].text).toBe('a') // Second selector: 'b' const selectorB = innerSelectorList.children[1] - expect(selectorB.type).toBe(NODE_SELECTOR) + expect(selectorB.type).toBe(SELECTOR) expect(selectorB.children).toHaveLength(1) - expect(selectorB.children[0].type).toBe(NODE_SELECTOR_TYPE) + expect(selectorB.children[0].type).toBe(TYPE_SELECTOR) expect(selectorB.children[0].text).toBe('b') }) @@ -1470,15 +1470,15 @@ describe('SelectorParser', () => { const root = parse_selector(':lang("nl", "de")') // Root is selector list - expect(root.type).toBe(NODE_SELECTOR_LIST) + expect(root.type).toBe(SELECTOR_LIST) // First selector in the list const selector = root.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) // Selector has :lang() pseudo-class const langPseudoClass = selector.first_child! - expect(langPseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(langPseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(langPseudoClass.text).toBe(':lang("nl", "de")') // :lang() has 2 children - language identifiers @@ -1487,12 +1487,12 @@ describe('SelectorParser', () => { // First language identifier: "nl" const lang1 = langPseudoClass.children[0] - expect(lang1.type).toBe(NODE_SELECTOR_LANG) + expect(lang1.type).toBe(LANG_SELECTOR) expect(lang1.text).toBe('"nl"') // Second language identifier: "de" const lang2 = langPseudoClass.children[1] - expect(lang2.type).toBe(NODE_SELECTOR_LANG) + expect(lang2.type).toBe(LANG_SELECTOR) expect(lang2.text).toBe('"de"') }) @@ -1502,7 +1502,7 @@ describe('SelectorParser', () => { const selector = root.first_child! const langPseudoClass = selector.first_child! - expect(langPseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(langPseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(langPseudoClass.text).toBe(':lang(en, fr)') // :lang() has 2 children - language identifiers @@ -1510,12 +1510,12 @@ describe('SelectorParser', () => { // First language identifier: en const lang1 = langPseudoClass.children[0] - expect(lang1.type).toBe(NODE_SELECTOR_LANG) + expect(lang1.type).toBe(LANG_SELECTOR) expect(lang1.text).toBe('en') // Second language identifier: fr const lang2 = langPseudoClass.children[1] - expect(lang2.type).toBe(NODE_SELECTOR_LANG) + expect(lang2.type).toBe(LANG_SELECTOR) expect(lang2.text).toBe('fr') }) @@ -1525,14 +1525,14 @@ describe('SelectorParser', () => { const selector = root.first_child! const langPseudoClass = selector.first_child! - expect(langPseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(langPseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(langPseudoClass.text).toBe(':lang(en-US)') // :lang() has 1 child - single language identifier expect(langPseudoClass.children).toHaveLength(1) const lang1 = langPseudoClass.children[0] - expect(lang1.type).toBe(NODE_SELECTOR_LANG) + expect(lang1.type).toBe(LANG_SELECTOR) expect(lang1.text).toBe('en-US') }) @@ -1542,14 +1542,14 @@ describe('SelectorParser', () => { const selector = root.first_child! const langPseudoClass = selector.first_child! - expect(langPseudoClass.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(langPseudoClass.type).toBe(PSEUDO_CLASS_SELECTOR) expect(langPseudoClass.text).toBe(':lang("*-Latn")') // :lang() has 1 child - wildcard language identifier expect(langPseudoClass.children).toHaveLength(1) const lang1 = langPseudoClass.children[0] - expect(lang1.type).toBe(NODE_SELECTOR_LANG) + expect(lang1.type).toBe(LANG_SELECTOR) expect(lang1.text).toBe('"*-Latn"') }) }) @@ -1559,7 +1559,7 @@ describe('parse_selector()', () => { test('should parse simple type selector', () => { const result = parse_selector('div') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('div') expect(result.has_children).toBe(true) }) @@ -1567,7 +1567,7 @@ describe('parse_selector()', () => { test('should parse class selector', () => { const result = parse_selector('.classname') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('.classname') expect(result.has_children).toBe(true) }) @@ -1575,7 +1575,7 @@ describe('parse_selector()', () => { test('should parse ID selector', () => { const result = parse_selector('#identifier') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('#identifier') expect(result.has_children).toBe(true) }) @@ -1583,7 +1583,7 @@ describe('parse_selector()', () => { test('should parse compound selector', () => { const result = parse_selector('div.class#id') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('div.class#id') expect(result.has_children).toBe(true) }) @@ -1591,7 +1591,7 @@ describe('parse_selector()', () => { test('should parse complex selector with combinator', () => { const result = parse_selector('div.class > p#id') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('div.class > p#id') expect(result.has_children).toBe(true) }) @@ -1599,7 +1599,7 @@ describe('parse_selector()', () => { test('should parse selector list', () => { const result = parse_selector('h1, h2, h3') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('h1, h2, h3') expect(result.has_children).toBe(true) }) @@ -1607,7 +1607,7 @@ describe('parse_selector()', () => { test('should parse pseudo-class selector', () => { const result = parse_selector('a:hover') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('a:hover') expect(result.has_children).toBe(true) }) @@ -1615,7 +1615,7 @@ describe('parse_selector()', () => { test('should parse pseudo-class with function', () => { const result = parse_selector(':nth-child(2n+1)') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe(':nth-child(2n+1)') expect(result.has_children).toBe(true) }) @@ -1623,14 +1623,14 @@ describe('parse_selector()', () => { test('should parse unknown pseudo-class without parens', () => { let root = parse_selector(':hello') let pseudo = root.first_child?.first_child - expect(pseudo?.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudo?.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudo?.has_children).toBe(false) }) test('should parse unknown pseudo-class with empty parens', () => { let root = parse_selector(':hello()') let pseudo = root.first_child?.first_child - expect(pseudo?.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudo?.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudo?.has_children).toBe(true) expect(pseudo?.children.length).toBe(0) }) @@ -1638,7 +1638,7 @@ describe('parse_selector()', () => { test('should parse attribute selector', () => { const result = parse_selector('[href^="https"]') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('[href^="https"]') expect(result.has_children).toBe(true) }) @@ -1646,7 +1646,7 @@ describe('parse_selector()', () => { test('should parse universal selector', () => { const result = parse_selector('*') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('*') expect(result.has_children).toBe(true) }) @@ -1654,7 +1654,7 @@ describe('parse_selector()', () => { test('should parse nesting selector', () => { const result = parse_selector('& .child') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('& .child') expect(result.has_children).toBe(true) }) @@ -1662,7 +1662,7 @@ describe('parse_selector()', () => { test('should parse descendant combinator', () => { const result = parse_selector('div span') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('div span') expect(result.has_children).toBe(true) }) @@ -1670,7 +1670,7 @@ describe('parse_selector()', () => { test('should parse child combinator', () => { const result = parse_selector('ul > li') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ul > li') expect(result.has_children).toBe(true) }) @@ -1678,7 +1678,7 @@ describe('parse_selector()', () => { test('should parse adjacent sibling combinator', () => { const result = parse_selector('h1 + p') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('h1 + p') expect(result.has_children).toBe(true) }) @@ -1686,7 +1686,7 @@ describe('parse_selector()', () => { test('should parse general sibling combinator', () => { const result = parse_selector('h1 ~ p') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('h1 ~ p') expect(result.has_children).toBe(true) }) @@ -1694,7 +1694,7 @@ describe('parse_selector()', () => { test('should parse modern pseudo-classes', () => { const result = parse_selector(':is(h1, h2, h3)') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe(':is(h1, h2, h3)') expect(result.has_children).toBe(true) }) @@ -1702,7 +1702,7 @@ describe('parse_selector()', () => { test('should parse :where() pseudo-class', () => { const result = parse_selector(':where(.a, .b)') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe(':where(.a, .b)') expect(result.has_children).toBe(true) }) @@ -1710,7 +1710,7 @@ describe('parse_selector()', () => { test('should parse :has() pseudo-class', () => { const result = parse_selector('div:has(> img)') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('div:has(> img)') expect(result.has_children).toBe(true) }) @@ -1718,7 +1718,7 @@ describe('parse_selector()', () => { test('should parse empty selector', () => { const result = parse_selector('') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('') }) @@ -1744,15 +1744,15 @@ describe('parse_selector()', () => { test('should parse ns|* (namespace with universal selector)', () => { const result = parse_selector('ns|*') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|*') const selector = result.first_child - expect(selector?.type).toBe(NODE_SELECTOR) + expect(selector?.type).toBe(SELECTOR) expect(selector?.text).toBe('ns|*') const universal = selector?.first_child - expect(universal?.type).toBe(NODE_SELECTOR_UNIVERSAL) + expect(universal?.type).toBe(UNIVERSAL_SELECTOR) expect(universal?.text).toBe('ns|*') expect(universal?.name).toBe('ns') }) @@ -1760,14 +1760,14 @@ describe('parse_selector()', () => { test('should parse ns|div (namespace with type selector)', () => { const result = parse_selector('ns|div') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|div') const selector = result.first_child - expect(selector?.type).toBe(NODE_SELECTOR) + expect(selector?.type).toBe(SELECTOR) const typeSelector = selector?.first_child - expect(typeSelector?.type).toBe(NODE_SELECTOR_TYPE) + expect(typeSelector?.type).toBe(TYPE_SELECTOR) expect(typeSelector?.text).toBe('ns|div') expect(typeSelector?.name).toBe('ns') }) @@ -1775,12 +1775,12 @@ describe('parse_selector()', () => { test('should parse *|* (any namespace with universal selector)', () => { const result = parse_selector('*|*') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('*|*') const selector = result.first_child const universal = selector?.first_child - expect(universal?.type).toBe(NODE_SELECTOR_UNIVERSAL) + expect(universal?.type).toBe(UNIVERSAL_SELECTOR) expect(universal?.text).toBe('*|*') expect(universal?.name).toBe('*') }) @@ -1788,12 +1788,12 @@ describe('parse_selector()', () => { test('should parse *|div (any namespace with type selector)', () => { const result = parse_selector('*|div') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('*|div') const selector = result.first_child const typeSelector = selector?.first_child - expect(typeSelector?.type).toBe(NODE_SELECTOR_TYPE) + expect(typeSelector?.type).toBe(TYPE_SELECTOR) expect(typeSelector?.text).toBe('*|div') expect(typeSelector?.name).toBe('*') }) @@ -1801,12 +1801,12 @@ describe('parse_selector()', () => { test('should parse |* (empty namespace with universal selector)', () => { const result = parse_selector('|*') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('|*') const selector = result.first_child const universal = selector?.first_child - expect(universal?.type).toBe(NODE_SELECTOR_UNIVERSAL) + expect(universal?.type).toBe(UNIVERSAL_SELECTOR) expect(universal?.text).toBe('|*') // Empty namespace should result in empty name expect(universal?.name).toBe('|') @@ -1815,12 +1815,12 @@ describe('parse_selector()', () => { test('should parse |div (empty namespace with type selector)', () => { const result = parse_selector('|div') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('|div') const selector = result.first_child const typeSelector = selector?.first_child - expect(typeSelector?.type).toBe(NODE_SELECTOR_TYPE) + expect(typeSelector?.type).toBe(TYPE_SELECTOR) expect(typeSelector?.text).toBe('|div') // Empty namespace should result in empty name expect(typeSelector?.name).toBe('|') @@ -1829,69 +1829,69 @@ describe('parse_selector()', () => { test('should parse namespace selector with class', () => { const result = parse_selector('ns|div.class') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|div.class') const selector = result.first_child const children = selector?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_SELECTOR_TYPE) + expect(children[0].type).toBe(TYPE_SELECTOR) expect(children[0].text).toBe('ns|div') expect(children[0].name).toBe('ns') - expect(children[1].type).toBe(NODE_SELECTOR_CLASS) + expect(children[1].type).toBe(CLASS_SELECTOR) }) test('should parse namespace selector with ID', () => { const result = parse_selector('ns|*#id') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|*#id') const selector = result.first_child const children = selector?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_SELECTOR_UNIVERSAL) + expect(children[0].type).toBe(UNIVERSAL_SELECTOR) expect(children[0].text).toBe('ns|*') - expect(children[1].type).toBe(NODE_SELECTOR_ID) + expect(children[1].type).toBe(ID_SELECTOR) }) test('should parse namespace selector in complex selector', () => { const result = parse_selector('ns|div > *|span') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|div > *|span') const selector = result.first_child const children = selector?.children || [] expect(children.length).toBe(3) // div, >, span - expect(children[0].type).toBe(NODE_SELECTOR_TYPE) + expect(children[0].type).toBe(TYPE_SELECTOR) expect(children[0].text).toBe('ns|div') - expect(children[1].type).toBe(NODE_SELECTOR_COMBINATOR) - expect(children[2].type).toBe(NODE_SELECTOR_TYPE) + expect(children[1].type).toBe(COMBINATOR) + expect(children[2].type).toBe(TYPE_SELECTOR) expect(children[2].text).toBe('*|span') }) test('should parse namespace selector in selector list', () => { const result = parse_selector('ns|div, |span, *|p') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|div, |span, *|p') const selectors = result.children expect(selectors.length).toBe(3) const firstType = selectors[0].first_child - expect(firstType?.type).toBe(NODE_SELECTOR_TYPE) + expect(firstType?.type).toBe(TYPE_SELECTOR) expect(firstType?.text).toBe('ns|div') expect(firstType?.name).toBe('ns') const secondType = selectors[1].first_child - expect(secondType?.type).toBe(NODE_SELECTOR_TYPE) + expect(secondType?.type).toBe(TYPE_SELECTOR) expect(secondType?.text).toBe('|span') expect(secondType?.name).toBe('|') const thirdType = selectors[2].first_child - expect(thirdType?.type).toBe(NODE_SELECTOR_TYPE) + expect(thirdType?.type).toBe(TYPE_SELECTOR) expect(thirdType?.text).toBe('*|p') expect(thirdType?.name).toBe('*') }) @@ -1899,40 +1899,40 @@ describe('parse_selector()', () => { test('should parse namespace selector with attribute', () => { const result = parse_selector('ns|div[attr="value"]') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|div[attr="value"]') const selector = result.first_child const children = selector?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_SELECTOR_TYPE) + expect(children[0].type).toBe(TYPE_SELECTOR) expect(children[0].name).toBe('ns') - expect(children[1].type).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(children[1].type).toBe(ATTRIBUTE_SELECTOR) }) test('should parse namespace selector with pseudo-class', () => { const result = parse_selector('ns|a:hover') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('ns|a:hover') const selector = result.first_child const children = selector?.children || [] expect(children.length).toBe(2) - expect(children[0].type).toBe(NODE_SELECTOR_TYPE) + expect(children[0].type).toBe(TYPE_SELECTOR) expect(children[0].name).toBe('ns') - expect(children[1].type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(children[1].type).toBe(PSEUDO_CLASS_SELECTOR) }) test('should parse namespace with various identifiers', () => { const result = parse_selector('svg|rect') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('svg|rect') const selector = result.first_child const typeSelector = selector?.first_child - expect(typeSelector?.type).toBe(NODE_SELECTOR_TYPE) + expect(typeSelector?.type).toBe(TYPE_SELECTOR) expect(typeSelector?.text).toBe('svg|rect') expect(typeSelector?.name).toBe('svg') }) @@ -1940,39 +1940,39 @@ describe('parse_selector()', () => { test('should parse long namespace identifier', () => { const result = parse_selector('myNamespace|element') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe('myNamespace|element') const selector = result.first_child const typeSelector = selector?.first_child - expect(typeSelector?.type).toBe(NODE_SELECTOR_TYPE) + expect(typeSelector?.type).toBe(TYPE_SELECTOR) expect(typeSelector?.name).toBe('myNamespace') }) test('should handle namespace in nested pseudo-class', () => { const result = parse_selector(':is(ns|div, *|span)') - expect(result.type).toBe(NODE_SELECTOR_LIST) + expect(result.type).toBe(SELECTOR_LIST) expect(result.text).toBe(':is(ns|div, *|span)') const selector = result.first_child const pseudo = selector?.first_child - expect(pseudo?.type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(pseudo?.type).toBe(PSEUDO_CLASS_SELECTOR) expect(pseudo?.name).toBe('is') // The content should contain namespace selectors const nestedList = pseudo?.first_child - expect(nestedList?.type).toBe(NODE_SELECTOR_LIST) + expect(nestedList?.type).toBe(SELECTOR_LIST) const nestedSelectors = nestedList?.children || [] expect(nestedSelectors.length).toBe(2) const firstNestedType = nestedSelectors[0].first_child - expect(firstNestedType?.type).toBe(NODE_SELECTOR_TYPE) + expect(firstNestedType?.type).toBe(TYPE_SELECTOR) expect(firstNestedType?.text).toBe('ns|div') const secondNestedType = nestedSelectors[1].first_child - expect(secondNestedType?.type).toBe(NODE_SELECTOR_TYPE) + expect(secondNestedType?.type).toBe(TYPE_SELECTOR) expect(secondNestedType?.text).toBe('*|span') }) }) diff --git a/src/parse-selector.ts b/src/parse-selector.ts index 6bbc900..3091767 100644 --- a/src/parse-selector.ts +++ b/src/parse-selector.ts @@ -2,19 +2,19 @@ import { Lexer } from './lexer' import { CSSDataArena } from './arena' import { - NODE_SELECTOR, - NODE_SELECTOR_LIST, - NODE_SELECTOR_TYPE, - NODE_SELECTOR_CLASS, - NODE_SELECTOR_ID, - NODE_SELECTOR_ATTRIBUTE, - NODE_SELECTOR_PSEUDO_CLASS, - NODE_SELECTOR_PSEUDO_ELEMENT, - NODE_SELECTOR_COMBINATOR, - NODE_SELECTOR_UNIVERSAL, - NODE_SELECTOR_NESTING, - NODE_SELECTOR_NTH_OF, - NODE_SELECTOR_LANG, + SELECTOR, + SELECTOR_LIST, + TYPE_SELECTOR, + CLASS_SELECTOR, + ID_SELECTOR, + ATTRIBUTE_SELECTOR, + PSEUDO_CLASS_SELECTOR, + PSEUDO_ELEMENT_SELECTOR, + COMBINATOR, + UNIVERSAL_SELECTOR, + NESTING_SELECTOR, + NTH_OF_SELECTOR, + LANG_SELECTOR, FLAG_VENDOR_PREFIXED, FLAG_HAS_PARENS, ATTR_OPERATOR_NONE, @@ -113,7 +113,7 @@ export class SelectorParser { let complex_selector = this.parse_complex_selector(allow_relative) if (complex_selector !== null) { // Wrap the complex selector (chain of components) in a NODE_SELECTOR - let selector_wrapper = this.create_node(NODE_SELECTOR, selector_start, this.lexer.pos) + let selector_wrapper = this.create_node(SELECTOR, selector_start, this.lexer.pos) // Find the last component in the chain let last_component = complex_selector @@ -148,7 +148,7 @@ export class SelectorParser { // Always wrap in selector list node, even for single selectors if (selectors.length >= 1) { let list_node = this.arena.create_node() - this.arena.set_type(list_node, NODE_SELECTOR_LIST) + this.arena.set_type(list_node, SELECTOR_LIST) this.arena.set_start_offset(list_node, list_start) this.arena.set_length(list_node, this.lexer.pos - list_start) this.arena.set_start_line(list_node, list_line) @@ -190,7 +190,7 @@ export class SelectorParser { let ch = this.source.charCodeAt(this.lexer.token_start) if (ch === CHAR_GREATER_THAN || ch === CHAR_PLUS || ch === CHAR_TILDE) { // Found leading combinator (>, +, ~) - this is a relative selector - let combinator = this.create_node(NODE_SELECTOR_COMBINATOR, this.lexer.token_start, this.lexer.token_end) + let combinator = this.create_node(COMBINATOR, this.lexer.token_start, this.lexer.token_end) components.push(combinator) this.skip_whitespace() // Continue to parse the rest normally @@ -306,7 +306,7 @@ export class SelectorParser { case TOKEN_HASH: // ID selector: #id - return this.create_node(NODE_SELECTOR_ID, start, end) + return this.create_node(ID_SELECTOR, start, end) case TOKEN_DELIM: // Could be: . (class), * (universal), & (nesting), | (namespace) @@ -319,7 +319,7 @@ export class SelectorParser { return this.parse_universal_or_namespace_selector(start, end) } else if (ch === CHAR_AMPERSAND) { // & - nesting selector - return this.create_node(NODE_SELECTOR_NESTING, start, end) + return this.create_node(NESTING_SELECTOR, start, end) } else if (ch === CHAR_PIPE) { // | - empty namespace prefix (|E or |*) return this.parse_empty_namespace_selector(start) @@ -358,10 +358,10 @@ export class SelectorParser { let node_type: number if (this.lexer.token_type === TOKEN_IDENT) { // ns|type - node_type = NODE_SELECTOR_TYPE + node_type = TYPE_SELECTOR } else if (this.lexer.token_type === TOKEN_DELIM && this.source.charCodeAt(this.lexer.token_start) === CHAR_ASTERISK) { // ns|* - node_type = NODE_SELECTOR_UNIVERSAL + node_type = UNIVERSAL_SELECTOR } else { // Invalid - restore position this.lexer.restore_position(saved) @@ -388,7 +388,7 @@ export class SelectorParser { } // Regular type selector (no namespace) - return this.create_node(NODE_SELECTOR_TYPE, start, end) + return this.create_node(TYPE_SELECTOR, start, end) } // Parse universal selector or namespace selector (*|E or *|*) @@ -404,7 +404,7 @@ export class SelectorParser { } // Regular universal selector (no namespace) - return this.create_node(NODE_SELECTOR_UNIVERSAL, start, end) + return this.create_node(UNIVERSAL_SELECTOR, start, end) } // Parse empty namespace selector (|E or |*) @@ -440,7 +440,7 @@ export class SelectorParser { let ch = this.source.charCodeAt(this.lexer.token_start) if (is_combinator(ch)) { // > + ~ (combinator text excludes leading whitespace) - return this.create_node(NODE_SELECTOR_COMBINATOR, this.lexer.token_start, this.lexer.token_end) + return this.create_node(COMBINATOR, this.lexer.token_start, this.lexer.token_end) } } @@ -457,7 +457,7 @@ export class SelectorParser { break } } - return this.create_node(NODE_SELECTOR_COMBINATOR, whitespace_start, this.lexer.pos) + return this.create_node(COMBINATOR, whitespace_start, this.lexer.pos) } // No combinator found, reset position @@ -478,7 +478,7 @@ export class SelectorParser { return null } - return this.create_node(NODE_SELECTOR_CLASS, dot_pos, this.lexer.token_end) + return this.create_node(CLASS_SELECTOR, dot_pos, this.lexer.token_end) } // Parse attribute selector ([attr], [attr=value], etc.) @@ -504,7 +504,7 @@ export class SelectorParser { } } - let node = this.create_node(NODE_SELECTOR_ATTRIBUTE, start, end) + let node = this.create_node(ATTRIBUTE_SELECTOR, start, end) // Parse the content inside brackets to extract name, operator, and value this.parse_attribute_content(node, content_start, content_end) @@ -684,11 +684,7 @@ export class SelectorParser { let token_type = this.lexer.token_type if (token_type === TOKEN_IDENT) { - let node = this.create_node( - is_pseudo_element ? NODE_SELECTOR_PSEUDO_ELEMENT : NODE_SELECTOR_PSEUDO_CLASS, - start, - this.lexer.token_end, - ) + let node = this.create_node(is_pseudo_element ? PSEUDO_ELEMENT_SELECTOR : PSEUDO_CLASS_SELECTOR, start, this.lexer.token_end) // Content is the pseudo name (without colons) this.arena.set_content_start(node, this.lexer.token_start) @@ -742,7 +738,7 @@ export class SelectorParser { } } - let node = this.create_node(is_pseudo_element ? NODE_SELECTOR_PSEUDO_ELEMENT : NODE_SELECTOR_PSEUDO_CLASS, start, end) + let node = this.create_node(is_pseudo_element ? PSEUDO_ELEMENT_SELECTOR : PSEUDO_CLASS_SELECTOR, start, end) // Content is the function name (without colons and parentheses) this.arena.set_content_start(node, func_name_start) this.arena.set_content_length(node, func_name_end - func_name_start) @@ -842,7 +838,7 @@ export class SelectorParser { // Accept both strings and identifiers if (token_type === TOKEN_STRING || token_type === TOKEN_IDENT) { // Create language identifier node - let lang_node = this.create_node(NODE_SELECTOR_LANG, token_start, token_end) + let lang_node = this.create_node(LANG_SELECTOR, token_start, token_end) // Link as child of :lang() pseudo-class if (first_child === null) { @@ -905,7 +901,7 @@ export class SelectorParser { // Create NTH_OF wrapper let of_node = this.arena.create_node() - this.arena.set_type(of_node, NODE_SELECTOR_NTH_OF) + this.arena.set_type(of_node, NTH_OF_SELECTOR) this.arena.set_start_offset(of_node, start) this.arena.set_length(of_node, end - start) this.arena.set_start_line(of_node, this.lexer.line) @@ -980,7 +976,7 @@ export function parse_selector(source: string): CSSNode { if (selector_index === null) { // Return empty selector list node if parsing failed const empty = arena.create_node() - arena.set_type(empty, NODE_SELECTOR_LIST) + arena.set_type(empty, SELECTOR_LIST) arena.set_start_offset(empty, 0) arena.set_length(empty, 0) arena.set_start_line(empty, 1) diff --git a/src/parse-value.test.ts b/src/parse-value.test.ts index 45f6c6c..b826999 100644 --- a/src/parse-value.test.ts +++ b/src/parse-value.test.ts @@ -1,15 +1,6 @@ import { describe, it, expect } from 'vitest' import { Parser } from './parse' -import { - NODE_VALUE_KEYWORD, - NODE_VALUE_NUMBER, - NODE_VALUE_DIMENSION, - NODE_VALUE_STRING, - NODE_VALUE_COLOR, - NODE_VALUE_FUNCTION, - NODE_VALUE_OPERATOR, - NODE_VALUE_PARENTHESIS, -} from './arena' +import { IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL } from './arena' describe('ValueParser', () => { describe('Simple values', () => { @@ -21,7 +12,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('red') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[0].type).toBe(IDENTIFIER) expect(decl?.values[0].text).toBe('red') }) @@ -33,7 +24,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('0.5') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_NUMBER) + expect(decl?.values[0].type).toBe(NUMBER) expect(decl?.values[0].text).toBe('0.5') }) @@ -45,7 +36,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('100px') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].type).toBe(DIMENSION) expect(decl?.values[0].text).toBe('100px') expect(decl?.values[0].value).toBe(100) expect(decl?.values[0].unit).toBe('px') @@ -59,7 +50,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('3em') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].type).toBe(DIMENSION) expect(decl?.values[0].text).toBe('3em') expect(decl?.values[0].value).toBe(3) expect(decl?.values[0].unit).toBe('em') @@ -73,7 +64,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('50%') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].type).toBe(DIMENSION) expect(decl?.values[0].text).toBe('50%') }) @@ -85,7 +76,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('"hello"') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_STRING) + expect(decl?.values[0].type).toBe(STRING) expect(decl?.values[0].text).toBe('"hello"') }) @@ -97,7 +88,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('#ff0000') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_COLOR) + expect(decl?.values[0].type).toBe(HASH) expect(decl?.values[0].text).toBe('#ff0000') }) }) @@ -110,11 +101,11 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(3) - expect(decl?.values[0].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[0].type).toBe(IDENTIFIER) expect(decl?.values[0].text).toBe('Arial') - expect(decl?.values[1].type).toBe(NODE_VALUE_OPERATOR) + expect(decl?.values[1].type).toBe(OPERATOR) expect(decl?.values[1].text).toBe(',') - expect(decl?.values[2].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[2].type).toBe(IDENTIFIER) expect(decl?.values[2].text).toBe('sans-serif') }) @@ -125,13 +116,13 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(4) - expect(decl?.values[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].type).toBe(DIMENSION) expect(decl?.values[0].text).toBe('10px') - expect(decl?.values[1].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[1].type).toBe(DIMENSION) expect(decl?.values[1].text).toBe('20px') - expect(decl?.values[2].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[2].type).toBe(DIMENSION) expect(decl?.values[2].text).toBe('30px') - expect(decl?.values[3].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[3].type).toBe(DIMENSION) expect(decl?.values[3].text).toBe('40px') }) @@ -142,11 +133,11 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(3) - expect(decl?.values[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].type).toBe(DIMENSION) expect(decl?.values[0].text).toBe('1px') - expect(decl?.values[1].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[1].type).toBe(IDENTIFIER) expect(decl?.values[1].text).toBe('solid') - expect(decl?.values[2].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[2].type).toBe(IDENTIFIER) expect(decl?.values[2].text).toBe('red') }) }) @@ -159,7 +150,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[0].type).toBe(FUNCTION) expect(decl?.values[0].name).toBe('rgb') expect(decl?.values[0].text).toBe('rgb(255, 0, 0)') }) @@ -172,15 +163,15 @@ describe('ValueParser', () => { const func = decl?.values[0] expect(func?.children).toHaveLength(5) - expect(func?.children[0].type).toBe(NODE_VALUE_NUMBER) + expect(func?.children[0].type).toBe(NUMBER) expect(func?.children[0].text).toBe('255') - expect(func?.children[1].type).toBe(NODE_VALUE_OPERATOR) + expect(func?.children[1].type).toBe(OPERATOR) expect(func?.children[1].text).toBe(',') - expect(func?.children[2].type).toBe(NODE_VALUE_NUMBER) + expect(func?.children[2].type).toBe(NUMBER) expect(func?.children[2].text).toBe('0') - expect(func?.children[3].type).toBe(NODE_VALUE_OPERATOR) + expect(func?.children[3].type).toBe(OPERATOR) expect(func?.children[3].text).toBe(',') - expect(func?.children[4].type).toBe(NODE_VALUE_NUMBER) + expect(func?.children[4].type).toBe(NUMBER) expect(func?.children[4].text).toBe('0') }) @@ -191,14 +182,14 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[0].type).toBe(FUNCTION) expect(decl?.values[0].name).toBe('calc') expect(decl?.values[0].children).toHaveLength(3) - expect(decl?.values[0].children[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].children[0].type).toBe(DIMENSION) expect(decl?.values[0].children[0].text).toBe('100%') - expect(decl?.values[0].children[1].type).toBe(NODE_VALUE_OPERATOR) + expect(decl?.values[0].children[1].type).toBe(OPERATOR) expect(decl?.values[0].children[1].text).toBe('-') - expect(decl?.values[0].children[2].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].children[2].type).toBe(DIMENSION) expect(decl?.values[0].children[2].text).toBe('20px') }) @@ -209,10 +200,10 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[0].type).toBe(FUNCTION) expect(decl?.values[0].name).toBe('var') expect(decl?.values[0].children).toHaveLength(1) - expect(decl?.values[0].children[0].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[0].children[0].type).toBe(IDENTIFIER) expect(decl?.values[0].children[0].text).toBe('--primary-color') }) @@ -223,10 +214,10 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[0].type).toBe(URL) expect(decl?.values[0].name).toBe('url') expect(decl?.values[0].children).toHaveLength(1) - expect(decl?.values[0].children[0].type).toBe(NODE_VALUE_STRING) + expect(decl?.values[0].children[0].type).toBe(STRING) expect(decl?.values[0].children[0].text).toBe('"image.png"') }) @@ -237,7 +228,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(URL) expect(func?.name).toBe('url') // URL function should not parse children - content is available via node.value @@ -253,7 +244,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(FUNCTION) expect(func?.name).toBe('src') expect(func?.has_children).toBe(false) expect(func?.text).toBe('src(myfont.woff2)') @@ -267,7 +258,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(URL) expect(func?.name).toBe('url') expect(func?.has_children).toBe(false) expect(func?.value).toBe('data:image/png;base64,iVBORw0KGg') @@ -280,7 +271,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(URL) expect(func?.name).toBe('url') expect(func?.has_children).toBe(false) expect(func?.value).toBe('data:image/svg+xml,') @@ -293,7 +284,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(FUNCTION) expect(func?.name).toBe('calc') expect(func?.text).toBe('calc(100% - 20px)') expect(func?.value).toBe('100% - 20px') @@ -307,7 +298,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(FUNCTION) expect(func?.name).toBe('var') expect(func?.text).toBe('var(--primary-color)') expect(func?.value).toBe('--primary-color') @@ -323,9 +314,9 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values.length).toBeGreaterThan(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[0].type).toBe(URL) expect(decl?.values[0].name).toBe('url') - expect(decl?.values[1].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[1].type).toBe(IDENTIFIER) expect(decl?.values[1].text).toBe('no-repeat') }) @@ -336,9 +327,9 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(2) - expect(decl?.values[0].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[0].type).toBe(FUNCTION) expect(decl?.values[0].name).toBe('translateX') - expect(decl?.values[1].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[1].type).toBe(FUNCTION) expect(decl?.values[1].name).toBe('rotate') }) @@ -349,10 +340,10 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(2) - expect(decl?.values[0].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[0].type).toBe(FUNCTION) expect(decl?.values[0].name).toBe('blur') expect(decl?.values[0].children[0].text).toBe('5px') - expect(decl?.values[1].type).toBe(NODE_VALUE_FUNCTION) + expect(decl?.values[1].type).toBe(FUNCTION) expect(decl?.values[1].name).toBe('brightness') expect(decl?.values[1].children[0].text).toBe('1.2') }) @@ -377,7 +368,7 @@ describe('ValueParser', () => { expect(decl?.value).toBe('red') expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_KEYWORD) + expect(decl?.values[0].type).toBe(IDENTIFIER) expect(decl?.values[0].text).toBe('red') expect(decl?.is_important).toBe(true) }) @@ -389,7 +380,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].type).toBe(DIMENSION) expect(decl?.values[0].text).toBe('-10px') }) @@ -400,7 +391,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_DIMENSION) + expect(decl?.values[0].type).toBe(DIMENSION) expect(decl?.values[0].text).toBe('0px') }) @@ -411,7 +402,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child expect(decl?.values).toHaveLength(1) - expect(decl?.values[0].type).toBe(NODE_VALUE_NUMBER) + expect(decl?.values[0].type).toBe(NUMBER) expect(decl?.values[0].text).toBe('0') }) }) @@ -423,7 +414,7 @@ describe('ValueParser', () => { const rule = root.first_child const decl = rule?.first_child?.next_sibling?.first_child - expect(decl?.values[1].type).toBe(NODE_VALUE_OPERATOR) + expect(decl?.values[1].type).toBe(OPERATOR) expect(decl?.values[1].text).toBe(',') }) @@ -434,7 +425,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.children[1].type).toBe(NODE_VALUE_OPERATOR) + expect(func?.children[1].type).toBe(OPERATOR) expect(func?.children[1].text).toBe('-') }) @@ -445,7 +436,7 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - const operators = func?.children.filter((n) => n.type === NODE_VALUE_OPERATOR) + const operators = func?.children.filter((n) => n.type === OPERATOR) expect(operators).toHaveLength(4) expect(operators?.[0].text).toBe('+') expect(operators?.[1].text).toBe('*') @@ -462,30 +453,30 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(FUNCTION) expect(func?.name).toBe('calc') expect(func?.children).toHaveLength(3) // First child should be a parenthesis node - expect(func?.children[0].type).toBe(NODE_VALUE_PARENTHESIS) + expect(func?.children[0].type).toBe(PARENTHESIS) expect(func?.children[0].text).toBe('(100% - 50px)') // Check parenthesis content const parenNode = func?.children[0] expect(parenNode?.children).toHaveLength(3) - expect(parenNode?.children[0].type).toBe(NODE_VALUE_DIMENSION) + expect(parenNode?.children[0].type).toBe(DIMENSION) expect(parenNode?.children[0].text).toBe('100%') - expect(parenNode?.children[1].type).toBe(NODE_VALUE_OPERATOR) + expect(parenNode?.children[1].type).toBe(OPERATOR) expect(parenNode?.children[1].text).toBe('-') - expect(parenNode?.children[2].type).toBe(NODE_VALUE_DIMENSION) + expect(parenNode?.children[2].type).toBe(DIMENSION) expect(parenNode?.children[2].text).toBe('50px') // Second child should be division operator - expect(func?.children[1].type).toBe(NODE_VALUE_OPERATOR) + expect(func?.children[1].type).toBe(OPERATOR) expect(func?.children[1].text).toBe('/') // Third child should be number - expect(func?.children[2].type).toBe(NODE_VALUE_NUMBER) + expect(func?.children[2].type).toBe(NUMBER) expect(func?.children[2].text).toBe('2') }) @@ -496,35 +487,35 @@ describe('ValueParser', () => { const decl = rule?.first_child?.next_sibling?.first_child const func = decl?.values[0] - expect(func?.type).toBe(NODE_VALUE_FUNCTION) + expect(func?.type).toBe(FUNCTION) expect(func?.name).toBe('calc') // The calc function should have 3 children: parenthesis + operator + parenthesis expect(func?.children).toHaveLength(3) - expect(func?.children[0].type).toBe(NODE_VALUE_PARENTHESIS) + expect(func?.children[0].type).toBe(PARENTHESIS) expect(func?.children[0].text).toBe('((100% - var(--x)) / 12 * 6)') - expect(func?.children[1].type).toBe(NODE_VALUE_OPERATOR) + expect(func?.children[1].type).toBe(OPERATOR) expect(func?.children[1].text).toBe('+') - expect(func?.children[2].type).toBe(NODE_VALUE_PARENTHESIS) + expect(func?.children[2].type).toBe(PARENTHESIS) expect(func?.children[2].text).toBe('(-1 * var(--y))') // Check first parenthesis has nested parenthesis and preserves structure const firstParen = func?.children[0] expect(firstParen?.children).toHaveLength(5) // paren + / + 12 + * + 6 - expect(firstParen?.children[0].type).toBe(NODE_VALUE_PARENTHESIS) + expect(firstParen?.children[0].type).toBe(PARENTHESIS) expect(firstParen?.children[0].text).toBe('(100% - var(--x))') // Check nested parenthesis has function const nestedParen = firstParen?.children[0] - expect(nestedParen?.children[2].type).toBe(NODE_VALUE_FUNCTION) + expect(nestedParen?.children[2].type).toBe(FUNCTION) expect(nestedParen?.children[2].name).toBe('var') // Check second parenthesis has content const secondParen = func?.children[2] expect(secondParen?.children).toHaveLength(3) // -1 * var(--y) - expect(secondParen?.children[0].type).toBe(NODE_VALUE_NUMBER) + expect(secondParen?.children[0].type).toBe(NUMBER) expect(secondParen?.children[0].text).toBe('-1') - expect(secondParen?.children[2].type).toBe(NODE_VALUE_FUNCTION) + expect(secondParen?.children[2].type).toBe(FUNCTION) expect(secondParen?.children[2].name).toBe('var') }) }) diff --git a/src/parse-value.ts b/src/parse-value.ts index d94aca7..508d10b 100644 --- a/src/parse-value.ts +++ b/src/parse-value.ts @@ -1,16 +1,6 @@ // Value Parser - Parses CSS declaration values into structured AST nodes import { Lexer } from './lexer' -import { - CSSDataArena, - NODE_VALUE_KEYWORD, - NODE_VALUE_NUMBER, - NODE_VALUE_DIMENSION, - NODE_VALUE_STRING, - NODE_VALUE_COLOR, - NODE_VALUE_FUNCTION, - NODE_VALUE_OPERATOR, - NODE_VALUE_PARENTHESIS, -} from './arena' +import { CSSDataArena, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL } from './arena' import { TOKEN_IDENT, TOKEN_NUMBER, @@ -97,20 +87,20 @@ export class ValueParser { switch (token_type) { case TOKEN_IDENT: - return this.create_node(NODE_VALUE_KEYWORD, start, end) + return this.create_node(IDENTIFIER, start, end) case TOKEN_NUMBER: - return this.create_node(NODE_VALUE_NUMBER, start, end) + return this.create_node(NUMBER, start, end) case TOKEN_PERCENTAGE: case TOKEN_DIMENSION: - return this.create_node(NODE_VALUE_DIMENSION, start, end) + return this.create_node(DIMENSION, start, end) case TOKEN_STRING: - return this.create_node(NODE_VALUE_STRING, start, end) + return this.create_node(STRING, start, end) case TOKEN_HASH: - return this.create_node(NODE_VALUE_COLOR, start, end) + return this.create_node(HASH, start, end) case TOKEN_FUNCTION: return this.parse_function_node(start, end) @@ -119,7 +109,7 @@ export class ValueParser { return this.parse_operator_node(start, end) case TOKEN_COMMA: - return this.create_node(NODE_VALUE_OPERATOR, start, end) + return this.create_node(OPERATOR, start, end) case TOKEN_LEFT_PAREN: return this.parse_parenthesis_node(start, end) @@ -142,7 +132,7 @@ export class ValueParser { } private create_operator_node(start: number, end: number): number { - return this.create_node(NODE_VALUE_OPERATOR, start, end) + return this.create_node(OPERATOR, start, end) } private parse_operator_node(start: number, end: number): number | null { @@ -156,20 +146,20 @@ export class ValueParser { } private parse_function_node(start: number, end: number): number { - // Create function node - let node = this.arena.create_node() - this.arena.set_type(node, NODE_VALUE_FUNCTION) - this.arena.set_start_offset(node, start) - // Function name is everything before the '(' // The lexer's TOKEN_FUNCTION includes the '(' at the end let name_end = end - 1 // Exclude the '(' - this.arena.set_content_start(node, start) - this.arena.set_content_length(node, name_end - start) // Get function name to check for special handling let func_name = this.source.substring(start, name_end).toLowerCase() + // Create URL or function node based on function name + let node = this.arena.create_node() + this.arena.set_type(node, func_name === 'url' ? URL : FUNCTION) + this.arena.set_start_offset(node, start) + this.arena.set_content_start(node, start) + this.arena.set_content_length(node, name_end - start) + // Special handling for url() and src() functions with unquoted content: // Don't parse contents to preserve URLs with dots, base64, inline SVGs, etc. // Users can extract the full URL from the function's text property @@ -295,7 +285,7 @@ export class ValueParser { private parse_parenthesis_node(start: number, end: number): number { // Create parenthesis node let node = this.arena.create_node() - this.arena.set_type(node, NODE_VALUE_PARENTHESIS) + this.arena.set_type(node, PARENTHESIS) this.arena.set_start_offset(node, start) // Parse parenthesized content (everything until matching ')') diff --git a/src/parse.test.ts b/src/parse.test.ts index 44cbbbf..ecdca39 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -1,17 +1,17 @@ import { describe, test, expect } from 'vitest' import { Parser, parse } from './parse' import { - NODE_STYLESHEET, - NODE_STYLE_RULE, - NODE_AT_RULE, - NODE_DECLARATION, - NODE_BLOCK, - NODE_SELECTOR_LIST, - NODE_SELECTOR, - NODE_SELECTOR_PSEUDO_CLASS, - NODE_SELECTOR_TYPE, - NODE_SELECTOR_ATTRIBUTE, - NODE_SELECTOR_NESTING, + STYLESHEET, + STYLE_RULE, + AT_RULE, + DECLARATION, + BLOCK, + SELECTOR_LIST, + SELECTOR, + PSEUDO_CLASS_SELECTOR, + TYPE_SELECTOR, + ATTRIBUTE_SELECTOR, + NESTING_SELECTOR, } from './constants' import { ATTR_OPERATOR_PIPE_EQUAL } from './arena' @@ -31,7 +31,7 @@ describe('Parser', () => { const parser = new Parser('') const root = parser.parse() - expect(root.type).toBe(NODE_STYLESHEET) + expect(root.type).toBe(STYLESHEET) expect(root.offset).toBe(0) expect(root.length).toBe(0) expect(root.has_children).toBe(false) @@ -41,7 +41,7 @@ describe('Parser', () => { const parser = new Parser(' \n\n ') const root = parser.parse() - expect(root.type).toBe(NODE_STYLESHEET) + expect(root.type).toBe(STYLESHEET) expect(root.has_children).toBe(false) }) @@ -49,7 +49,7 @@ describe('Parser', () => { const parser = new Parser('/* comment */') const root = parser.parse() - expect(root.type).toBe(NODE_STYLESHEET) + expect(root.type).toBe(STYLESHEET) // TODO: Once we parse comments, verify they're added as children }) }) @@ -62,7 +62,7 @@ describe('Parser', () => { expect(root.has_children).toBe(true) const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.offset).toBe(0) expect(rule.length).toBeGreaterThan(0) }) @@ -88,8 +88,8 @@ describe('Parser', () => { const root = parser.parse() const [rule1, rule2] = root.children - expect(rule1.type).toBe(NODE_STYLE_RULE) - expect(rule2.type).toBe(NODE_STYLE_RULE) + expect(rule1.type).toBe(STYLE_RULE) + expect(rule2.type).toBe(STYLE_RULE) expect(rule2.next_sibling).toBe(null) }) @@ -123,9 +123,9 @@ describe('Parser', () => { const selectorlist = rule.first_child! const selector = selectorlist.first_child! - expect(selector.type).toBe(NODE_SELECTOR) - expect(selector.children[0].type).toBe(NODE_SELECTOR_TYPE) - expect(selector.children[1].type).toBe(NODE_SELECTOR_PSEUDO_CLASS) + expect(selector.type).toBe(SELECTOR) + expect(selector.children[0].type).toBe(TYPE_SELECTOR) + expect(selector.children[1].type).toBe(PSEUDO_CLASS_SELECTOR) expect(selector.children[2]).toBeUndefined() const pseudo = selector.children[1] expect(pseudo.text).toBe(':has(a)') @@ -138,9 +138,9 @@ describe('Parser', () => { const rule = root.first_child! const selectorlist = rule.first_child! const selector = selectorlist.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) const s = selector.children[0] - expect(s.type).toBe(NODE_SELECTOR_ATTRIBUTE) + expect(s.type).toBe(ATTRIBUTE_SELECTOR) expect(s.attr_operator).toEqual(ATTR_OPERATOR_PIPE_EQUAL) expect(s.name).toBe('root') expect(s.value).toBe('"test"') @@ -157,7 +157,7 @@ describe('Parser', () => { const [_selector, block] = rule.children const declaration = block.first_child! - expect(declaration.type).toBe(NODE_DECLARATION) + expect(declaration.type).toBe(DECLARATION) expect(declaration.is_important).toBe(false) }) @@ -183,8 +183,8 @@ describe('Parser', () => { const [_selector, block] = rule.children const [decl1, decl2] = block.children - expect(decl1.type).toBe(NODE_DECLARATION) - expect(decl2.type).toBe(NODE_DECLARATION) + expect(decl1.type).toBe(DECLARATION) + expect(decl2.type).toBe(DECLARATION) expect(decl2.next_sibling).toBe(null) }) @@ -197,7 +197,7 @@ describe('Parser', () => { const [_selector, block] = rule.children const declaration = block.first_child! - expect(declaration.type).toBe(NODE_DECLARATION) + expect(declaration.type).toBe(DECLARATION) expect(declaration.is_important).toBe(true) }) @@ -210,7 +210,7 @@ describe('Parser', () => { const [_selector, block] = rule.children const declaration = block.first_child! - expect(declaration.type).toBe(NODE_DECLARATION) + expect(declaration.type).toBe(DECLARATION) expect(declaration.is_important).toBe(true) }) @@ -223,7 +223,7 @@ describe('Parser', () => { const [_selector, block] = rule.children const declaration = block.first_child! - expect(declaration.type).toBe(NODE_DECLARATION) + expect(declaration.type).toBe(DECLARATION) expect(declaration.is_important).toBe(true) }) @@ -236,7 +236,7 @@ describe('Parser', () => { const [_selector, block] = rule.children const declaration = block.first_child! - expect(declaration.type).toBe(NODE_DECLARATION) + expect(declaration.type).toBe(DECLARATION) }) test('should parse complex declaration value', () => { @@ -248,7 +248,7 @@ describe('Parser', () => { const [_selector, block] = rule.children const declaration = block.first_child! - expect(declaration.type).toBe(NODE_DECLARATION) + expect(declaration.type).toBe(DECLARATION) expect(declaration.name).toBe('background') }) }) @@ -261,7 +261,7 @@ describe('Parser', () => { const root = parser.parse() const atRule = root.first_child! - expect(atRule.type).toBe(NODE_AT_RULE) + expect(atRule.type).toBe(AT_RULE) expect(atRule.name).toBe('import') expect(atRule.has_children).toBe(false) expect(atRule.length).toBe(25) @@ -273,7 +273,7 @@ describe('Parser', () => { const root = parser.parse() const atRule = root.first_child! - expect(atRule.type).toBe(NODE_AT_RULE) + expect(atRule.type).toBe(AT_RULE) expect(atRule.name).toBe('namespace') expect(atRule.length).toBe(45) }) @@ -286,13 +286,13 @@ describe('Parser', () => { const root = parser.parse() const media = root.first_child! - expect(media.type).toBe(NODE_AT_RULE) + expect(media.type).toBe(AT_RULE) expect(media.name).toBe('MEDIA') expect(media.has_children).toBe(true) // Should parse as conditional (containing rules) const block = media.block! const nestedRule = block.first_child! - expect(nestedRule.type).toBe(NODE_STYLE_RULE) + expect(nestedRule.type).toBe(STYLE_RULE) }) test('should parse @Font-Face (mixed case declaration at-rule)', () => { @@ -301,14 +301,14 @@ describe('Parser', () => { const root = parser.parse() const fontFace = root.first_child! - expect(fontFace.type).toBe(NODE_AT_RULE) + expect(fontFace.type).toBe(AT_RULE) expect(fontFace.name).toBe('Font-Face') expect(fontFace.length).toBe(60) expect(fontFace.has_children).toBe(true) // Should parse as declaration at-rule (containing declarations) const block = fontFace.block! const decl = block.first_child! - expect(decl.type).toBe(NODE_DECLARATION) + expect(decl.type).toBe(DECLARATION) }) test('should parse @SUPPORTS (uppercase conditional at-rule)', () => { @@ -317,7 +317,7 @@ describe('Parser', () => { const root = parser.parse() const supports = root.first_child! - expect(supports.type).toBe(NODE_AT_RULE) + expect(supports.type).toBe(AT_RULE) expect(supports.name).toBe('SUPPORTS') expect(supports.has_children).toBe(true) }) @@ -330,14 +330,14 @@ describe('Parser', () => { const root = parser.parse() const media = root.first_child! - expect(media.type).toBe(NODE_AT_RULE) + expect(media.type).toBe(AT_RULE) expect(media.name).toBe('media') expect(media.has_children).toBe(true) expect(media.length).toBe(50) const block = media.block! const nestedRule = block.first_child! - expect(nestedRule.type).toBe(NODE_STYLE_RULE) + expect(nestedRule.type).toBe(STYLE_RULE) expect(nestedRule.length).toBe(20) }) @@ -347,7 +347,7 @@ describe('Parser', () => { const root = parser.parse() const layer = root.first_child! - expect(layer.type).toBe(NODE_AT_RULE) + expect(layer.type).toBe(AT_RULE) expect(layer.name).toBe('layer') expect(layer.has_children).toBe(true) }) @@ -358,7 +358,7 @@ describe('Parser', () => { const root = parser.parse() const layer = root.first_child! - expect(layer.type).toBe(NODE_AT_RULE) + expect(layer.type).toBe(AT_RULE) expect(layer.name).toBe('layer') expect(layer.has_children).toBe(true) }) @@ -369,7 +369,7 @@ describe('Parser', () => { const root = parser.parse() const supports = root.first_child! - expect(supports.type).toBe(NODE_AT_RULE) + expect(supports.type).toBe(AT_RULE) expect(supports.name).toBe('supports') expect(supports.has_children).toBe(true) }) @@ -380,7 +380,7 @@ describe('Parser', () => { const root = parser.parse() const container = root.first_child! - expect(container.type).toBe(NODE_AT_RULE) + expect(container.type).toBe(AT_RULE) expect(container.name).toBe('container') expect(container.has_children).toBe(true) }) @@ -393,15 +393,15 @@ describe('Parser', () => { const root = parser.parse() const fontFace = root.first_child! - expect(fontFace.type).toBe(NODE_AT_RULE) + expect(fontFace.type).toBe(AT_RULE) expect(fontFace.name).toBe('font-face') expect(fontFace.has_children).toBe(true) // Should have declarations as children const block = fontFace.block! const [decl1, decl2] = block.children - expect(decl1.type).toBe(NODE_DECLARATION) - expect(decl2.type).toBe(NODE_DECLARATION) + expect(decl1.type).toBe(DECLARATION) + expect(decl2.type).toBe(DECLARATION) }) test('should parse @page', () => { @@ -410,12 +410,12 @@ describe('Parser', () => { const root = parser.parse() const page = root.first_child! - expect(page.type).toBe(NODE_AT_RULE) + expect(page.type).toBe(AT_RULE) expect(page.name).toBe('page') const block = page.block! const decl = block.first_child! - expect(decl.type).toBe(NODE_DECLARATION) + expect(decl.type).toBe(DECLARATION) }) test('should parse @counter-style', () => { @@ -424,12 +424,12 @@ describe('Parser', () => { const root = parser.parse() const counterStyle = root.first_child! - expect(counterStyle.type).toBe(NODE_AT_RULE) + expect(counterStyle.type).toBe(AT_RULE) expect(counterStyle.name).toBe('counter-style') const block = counterStyle.block! const decl = block.first_child! - expect(decl.type).toBe(NODE_DECLARATION) + expect(decl.type).toBe(DECLARATION) }) }) @@ -445,14 +445,14 @@ describe('Parser', () => { const supports_block = supports.block! const media = supports_block.first_child! - expect(media.type).toBe(NODE_AT_RULE) + expect(media.type).toBe(AT_RULE) expect(media.name).toBe('media') expect(media.text).toBe('@media (min-width: 768px) { body { color: red; } }') expect(media.length).toBe(50) const media_block = media.block! const rule = media_block.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) expect(rule.length).toBe(20) }) }) @@ -481,14 +481,14 @@ describe('Parser', () => { let root = parser.parse() let parent = root.first_child! - expect(parent.type).toBe(NODE_STYLE_RULE) + expect(parent.type).toBe(STYLE_RULE) let [_selector, block] = parent.children let [decl, nested_rule] = block.children - expect(decl.type).toBe(NODE_DECLARATION) + expect(decl.type).toBe(DECLARATION) expect(decl.name).toBe('color') - expect(nested_rule.type).toBe(NODE_STYLE_RULE) + expect(nested_rule.type).toBe(STYLE_RULE) let nested_selector = nested_rule.first_child! // With parseSelectors enabled, selector is now detailed expect(nested_selector.text).toBe('& .child') @@ -503,7 +503,7 @@ describe('Parser', () => { let [_selector, block] = parent.children let [_decl, nested_rule] = block.children - expect(nested_rule.type).toBe(NODE_STYLE_RULE) + expect(nested_rule.type).toBe(STYLE_RULE) let nested_selector = nested_rule.first_child! expect(nested_selector.text).toBe('.child') }) @@ -517,8 +517,8 @@ describe('Parser', () => { let [_selector, block] = parent.children let [nested1, nested2] = block.children - expect(nested1.type).toBe(NODE_STYLE_RULE) - expect(nested2.type).toBe(NODE_STYLE_RULE) + expect(nested1.type).toBe(STYLE_RULE) + expect(nested2.type).toBe(STYLE_RULE) }) test('should parse deeply nested rules', () => { @@ -530,17 +530,17 @@ describe('Parser', () => { expect(a.length).toBe(32) let [_selector_a, block_a] = a.children let b = block_a.first_child! - expect(b.type).toBe(NODE_STYLE_RULE) + expect(b.type).toBe(STYLE_RULE) expect(b.length).toBe(25) let [_selector_b, block_b] = b.children let c = block_b.first_child! - expect(c.type).toBe(NODE_STYLE_RULE) + expect(c.type).toBe(STYLE_RULE) expect(c.length).toBe(18) let [_selector_c, block_c] = c.children let decl = block_c.first_child! - expect(decl.type).toBe(NODE_DECLARATION) + expect(decl.type).toBe(DECLARATION) expect(decl.name).toBe('color') }) @@ -553,13 +553,13 @@ describe('Parser', () => { let [_selector, block] = card.children let [decl, media] = block.children - expect(decl.type).toBe(NODE_DECLARATION) - expect(media.type).toBe(NODE_AT_RULE) + expect(decl.type).toBe(DECLARATION) + expect(media.type).toBe(AT_RULE) expect(media.name).toBe('media') let media_block = media.block! let nested_decl = media_block.first_child! - expect(nested_decl.type).toBe(NODE_DECLARATION) + expect(nested_decl.type).toBe(DECLARATION) expect(nested_decl.name).toBe('padding') }) @@ -607,15 +607,15 @@ describe('Parser', () => { let [_selector, block] = card.children let [decl1, title, decl2, body] = block.children - expect(decl1.type).toBe(NODE_DECLARATION) + expect(decl1.type).toBe(DECLARATION) expect(decl1.name).toBe('color') - expect(title.type).toBe(NODE_STYLE_RULE) + expect(title.type).toBe(STYLE_RULE) - expect(decl2.type).toBe(NODE_DECLARATION) + expect(decl2.type).toBe(DECLARATION) expect(decl2.name).toBe('padding') - expect(body.type).toBe(NODE_STYLE_RULE) + expect(body.type).toBe(STYLE_RULE) }) describe('Relaxed nesting (CSS Nesting Module Level 1)', () => { @@ -625,11 +625,11 @@ describe('Parser', () => { let root = parser.parse() let parent = root.first_child! - expect(parent.type).toBe(NODE_STYLE_RULE) + expect(parent.type).toBe(STYLE_RULE) let [_selector, block] = parent.children let nested_rule = block.first_child! - expect(nested_rule.type).toBe(NODE_STYLE_RULE) + expect(nested_rule.type).toBe(STYLE_RULE) let nested_selector = nested_rule.first_child! expect(nested_selector.text).toBe('> a') @@ -645,7 +645,7 @@ describe('Parser', () => { let parent = root.first_child! let [_selector, block] = parent.children let nested_rule = block.first_child! - expect(nested_rule.type).toBe(NODE_STYLE_RULE) + expect(nested_rule.type).toBe(STYLE_RULE) let nested_selector = nested_rule.first_child! expect(nested_selector.text).toBe('+ span') @@ -660,7 +660,7 @@ describe('Parser', () => { let parent = root.first_child! let [_selector, block] = parent.children let nested_rule = block.first_child! - expect(nested_rule.type).toBe(NODE_STYLE_RULE) + expect(nested_rule.type).toBe(STYLE_RULE) let nested_selector = nested_rule.first_child! expect(nested_selector.text).toBe('~ div') @@ -676,15 +676,15 @@ describe('Parser', () => { let [_selector, block] = parent.children let [rule1, rule2, rule3] = block.children - expect(rule1.type).toBe(NODE_STYLE_RULE) + expect(rule1.type).toBe(STYLE_RULE) expect(rule1.first_child!.text).toBe('> a') expect(rule1.first_child!.has_children).toBe(true) - expect(rule2.type).toBe(NODE_STYLE_RULE) + expect(rule2.type).toBe(STYLE_RULE) expect(rule2.first_child!.text).toBe('~ span') expect(rule2.first_child!.has_children).toBe(true) - expect(rule3.type).toBe(NODE_STYLE_RULE) + expect(rule3.type).toBe(STYLE_RULE) expect(rule3.first_child!.text).toBe('+ div') expect(rule3.first_child!.has_children).toBe(true) }) @@ -711,13 +711,13 @@ describe('Parser', () => { let a = root.first_child! let [_selector_a, block_a] = a.children let b = block_a.first_child! - expect(b.type).toBe(NODE_STYLE_RULE) + expect(b.type).toBe(STYLE_RULE) expect(b.first_child!.text).toBe('> .b') expect(b.first_child!.has_children).toBe(true) let [_selector_b, block_b] = b.children let c = block_b.first_child! - expect(c.type).toBe(NODE_STYLE_RULE) + expect(c.type).toBe(STYLE_RULE) expect(c.first_child!.text).toBe('> .c') expect(c.first_child!.has_children).toBe(true) }) @@ -731,10 +731,10 @@ describe('Parser', () => { let [_selector, block] = parent.children let [normal, combinator] = block.children - expect(normal.type).toBe(NODE_STYLE_RULE) + expect(normal.type).toBe(STYLE_RULE) expect(normal.first_child!.text).toBe('.normal') - expect(combinator.type).toBe(NODE_STYLE_RULE) + expect(combinator.type).toBe(STYLE_RULE) expect(combinator.first_child!.text).toBe('> .combinator') expect(combinator.first_child!.has_children).toBe(true) }) @@ -748,13 +748,13 @@ describe('Parser', () => { let root = parser.parse() let keyframes = root.first_child! - expect(keyframes.type).toBe(NODE_AT_RULE) + expect(keyframes.type).toBe(AT_RULE) expect(keyframes.name).toBe('keyframes') let block = keyframes.block! let [from_rule, to_rule] = block.children - expect(from_rule.type).toBe(NODE_STYLE_RULE) - expect(to_rule.type).toBe(NODE_STYLE_RULE) + expect(from_rule.type).toBe(STYLE_RULE) + expect(to_rule.type).toBe(STYLE_RULE) let from_selector = from_rule.first_child! expect(from_selector.text).toBe('from') @@ -772,9 +772,9 @@ describe('Parser', () => { let block = keyframes.block! let [rule0, rule50, rule100] = block.children - expect(rule0.type).toBe(NODE_STYLE_RULE) - expect(rule50.type).toBe(NODE_STYLE_RULE) - expect(rule100.type).toBe(NODE_STYLE_RULE) + expect(rule0.type).toBe(STYLE_RULE) + expect(rule50.type).toBe(STYLE_RULE) + expect(rule100.type).toBe(STYLE_RULE) let selector0 = rule0.first_child! expect(selector0.text).toBe('0%') @@ -804,13 +804,13 @@ describe('Parser', () => { let [_selector, block] = parent.children let nest = block.first_child! - expect(nest.type).toBe(NODE_AT_RULE) + expect(nest.type).toBe(AT_RULE) expect(nest.name).toBe('nest') expect(nest.has_children).toBe(true) let nest_block = nest.block! let decl = nest_block.first_child! - expect(decl.type).toBe(NODE_DECLARATION) + expect(decl.type).toBe(DECLARATION) expect(decl.name).toBe('color') }) @@ -823,7 +823,7 @@ describe('Parser', () => { let [_selector, block] = a.children let nest = block.first_child! - expect(nest.type).toBe(NODE_AT_RULE) + expect(nest.type).toBe(AT_RULE) expect(nest.name).toBe('nest') }) }) @@ -853,7 +853,7 @@ describe('Parser', () => { let root = parser.parse() let rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) // Only has selector and empty block expect(rule.children.length).toBe(2) }) @@ -866,7 +866,7 @@ describe('Parser', () => { let rule = root.first_child! let [_selector, block] = rule.children let decl = block.first_child! - expect(decl.type).toBe(NODE_DECLARATION) + expect(decl.type).toBe(DECLARATION) }) test('should handle multiple semicolons', () => { @@ -914,7 +914,7 @@ describe('Parser', () => { let root = parser.parse() let a = root.first_child! - expect(a.type).toBe(NODE_STYLE_RULE) + expect(a.type).toBe(STYLE_RULE) }) test('should handle trailing comma in selector', () => { @@ -923,7 +923,7 @@ describe('Parser', () => { let root = parser.parse() let rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) }) }) @@ -1077,7 +1077,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper // Selector has detailed parsing enabled by default expect(selector.has_children).toBe(true) // Navigate: selector -> type selector (input) -> pseudo-class (next sibling) @@ -1094,7 +1094,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoClass = typeSelector.next_sibling! expect(pseudoClass.name).toBe('-moz-focusring') @@ -1108,7 +1108,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoClass = typeSelector.next_sibling! expect(pseudoClass.name).toBe('-ms-input-placeholder') @@ -1122,7 +1122,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoElement = typeSelector.next_sibling! expect(pseudoElement.name).toBe('-webkit-scrollbar') @@ -1136,7 +1136,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoElement = typeSelector.next_sibling! expect(pseudoElement.name).toBe('-moz-selection') @@ -1150,7 +1150,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoElement = typeSelector.next_sibling! expect(pseudoElement.name).toBe('-webkit-input-placeholder') @@ -1164,7 +1164,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoClass = typeSelector.next_sibling! expect(pseudoClass.name).toBe('-webkit-any') @@ -1178,7 +1178,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoClass = typeSelector.next_sibling! expect(pseudoClass.name).toBe('hover') @@ -1192,7 +1192,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper let typeSelector = selector.first_child! let pseudoElement = typeSelector.next_sibling! expect(pseudoElement.name).toBe('before') @@ -1207,21 +1207,21 @@ describe('Parser', () => { let [rule1, rule2, rule3] = root.children let selectorList1 = rule1.first_child! - let selector1 = selectorList1.first_child! // NODE_SELECTOR wrapper + let selector1 = selectorList1.first_child! // SELECTOR wrapper let typeSelector1 = selector1.first_child! let pseudo1 = typeSelector1.next_sibling! expect(pseudo1.name).toBe('-webkit-scrollbar') expect(pseudo1.is_vendor_prefixed).toBe(true) let selectorList2 = rule2.first_child! - let selector2 = selectorList2.first_child! // NODE_SELECTOR wrapper + let selector2 = selectorList2.first_child! // SELECTOR wrapper let typeSelector2 = selector2.first_child! let pseudo2 = typeSelector2.next_sibling! expect(pseudo2.name).toBe('-webkit-scrollbar-thumb') expect(pseudo2.is_vendor_prefixed).toBe(true) let selectorList3 = rule3.first_child! - let selector3 = selectorList3.first_child! // NODE_SELECTOR wrapper + let selector3 = selectorList3.first_child! // SELECTOR wrapper let typeSelector3 = selector3.first_child! let pseudo3 = typeSelector3.next_sibling! expect(pseudo3.name).toBe('after') @@ -1235,7 +1235,7 @@ describe('Parser', () => { let rule = root.first_child! let selectorList = rule.first_child! - let selector = selectorList.first_child! // NODE_SELECTOR wrapper + let selector = selectorList.first_child! // SELECTOR wrapper // Navigate through compound selector: input (type) -> -webkit-autofill (pseudo) -> :focus (pseudo) let typeSelector = selector.first_child! let webkitPseudo = typeSelector.next_sibling! @@ -1282,7 +1282,7 @@ describe('Parser', () => { let root = parser.parse() let card = root.first_child! - expect(card.type).toBe(NODE_STYLE_RULE) + expect(card.type).toBe(STYLE_RULE) // Card has selector + block expect(card.children.length).toBe(2) }) @@ -1307,8 +1307,8 @@ describe('Parser', () => { let root = parser.parse() let [layer1, layer2] = root.children - expect(layer1.type).toBe(NODE_AT_RULE) - expect(layer2.type).toBe(NODE_AT_RULE) + expect(layer1.type).toBe(AT_RULE) + expect(layer2.type).toBe(AT_RULE) }) test('should parse vendor prefixed properties', () => { @@ -1378,7 +1378,7 @@ describe('Parser', () => { // Parser may have issues with -- custom property names, check what we got expect(root.children.length).toBeGreaterThan(0) let first_rule = root.first_child! - expect(first_rule.type).toBe(NODE_STYLE_RULE) + expect(first_rule.type).toBe(STYLE_RULE) }) test('should parse attribute selectors with operators', () => { @@ -1399,8 +1399,8 @@ describe('Parser', () => { let root = parser.parse() let [rule1, rule2] = root.children - expect(rule1.type).toBe(NODE_STYLE_RULE) - expect(rule2.type).toBe(NODE_STYLE_RULE) + expect(rule1.type).toBe(STYLE_RULE) + expect(rule2.type).toBe(STYLE_RULE) }) test('should parse multiple !important declarations', () => { @@ -1412,7 +1412,7 @@ describe('Parser', () => { let block = rule.block! expect(block.children.length).toBeGreaterThan(1) // Check at least first declaration has important flag - let declarations = block.children.filter((c) => c.type === NODE_DECLARATION) + let declarations = block.children.filter((c) => c.type === DECLARATION) expect(declarations.length).toBeGreaterThan(0) expect(declarations[0].is_important).toBe(true) }) @@ -1427,7 +1427,7 @@ describe('Parser', () => { // Comments are skipped, only rule remains expect(root.children.length).toBe(1) let rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) }) test('should skip comments in declaration block', () => { @@ -1437,7 +1437,7 @@ describe('Parser', () => { let rule = root.first_child! // Comments don't break parsing - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) // Rule has selector + block expect(rule.children.length).toBe(2) }) @@ -1448,7 +1448,7 @@ describe('Parser', () => { let root = parser.parse() let rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) }) test('should handle comment between property and colon', () => { @@ -1482,7 +1482,7 @@ describe('Parser', () => { let root = parser.parse() let rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) }) test('should handle tabs and newlines', () => { @@ -1491,7 +1491,7 @@ describe('Parser', () => { let root = parser.parse() let rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) }) test('should handle no whitespace', () => { @@ -1514,7 +1514,7 @@ describe('Parser', () => { let root = parser.parse() let [charset, _body] = root.children - expect(charset.type).toBe(NODE_AT_RULE) + expect(charset.type).toBe(AT_RULE) expect(charset.name).toBe('charset') }) @@ -1524,7 +1524,7 @@ describe('Parser', () => { let root = parser.parse() let import_rule = root.first_child! - expect(import_rule.type).toBe(NODE_AT_RULE) + expect(import_rule.type).toBe(AT_RULE) expect(import_rule.name).toBe('import') }) @@ -1793,7 +1793,7 @@ describe('Parser', () => { let root = parser.parse() let atrule = root.first_child! - expect(atrule.type).toBe(NODE_AT_RULE) + expect(atrule.type).toBe(AT_RULE) expect(atrule.name).toBe('media') expect(atrule.prelude).toBe('(min-width: 768px)') }) @@ -1916,14 +1916,14 @@ describe('Parser', () => { let rule = atrule?.block?.first_child test('atrule should have block', () => { - expect(sheet.type).toBe(NODE_STYLESHEET) - expect(atrule!.type).toBe(NODE_AT_RULE) - expect(atrule?.block?.type).toBe(NODE_BLOCK) + expect(sheet.type).toBe(STYLESHEET) + expect(atrule!.type).toBe(AT_RULE) + expect(atrule?.block?.type).toBe(BLOCK) }) test('block children should be stylerule', () => { expect(atrule!.block).not.toBeNull() - expect(rule!.type).toBe(NODE_STYLE_RULE) + expect(rule!.type).toBe(STYLE_RULE) expect(rule!.text).toBe('a {}') }) @@ -1931,14 +1931,14 @@ describe('Parser', () => { expect(rule!.block).not.toBeNull() expect(rule?.has_block).toBeTruthy() expect(rule?.has_declarations).toBeFalsy() - expect(rule?.first_child!.type).toBe(NODE_SELECTOR_LIST) + expect(rule?.first_child!.type).toBe(SELECTOR_LIST) }) test('has correct nested selectors', () => { let list = rule?.first_child - expect(list!.type).toBe(NODE_SELECTOR_LIST) + expect(list!.type).toBe(SELECTOR_LIST) expect(list!.children).toHaveLength(1) - expect(list?.first_child?.type).toEqual(NODE_SELECTOR) + expect(list?.first_child?.type).toEqual(SELECTOR) expect(list?.first_child?.text).toEqual('a') }) }) @@ -2035,12 +2035,12 @@ describe('Parser', () => { let ast = parse(css) const container = ast.first_child! - expect(container.type).toBe(NODE_AT_RULE) + expect(container.type).toBe(AT_RULE) expect(container.name).toBe('container') const containerBlock = container.block! const rule = containerBlock.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) }) test('@container should parse rules with :has() selector', () => { @@ -2050,7 +2050,7 @@ describe('Parser', () => { const container = ast.first_child! const containerBlock = container.block! const rule = containerBlock.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) }) test('modern CSS example by Vadim Makeev', () => { @@ -2069,64 +2069,64 @@ describe('Parser', () => { let ast = parse(css) // Root should be stylesheet - expect(ast.type).toBe(NODE_STYLESHEET) + expect(ast.type).toBe(STYLESHEET) expect(ast.has_children).toBe(true) // First child: @layer what const layer = ast.first_child! - expect(layer.type).toBe(NODE_AT_RULE) + expect(layer.type).toBe(AT_RULE) expect(layer.name).toBe('layer') expect(layer.prelude).toBe('what') expect(layer.has_block).toBe(true) // Inside @layer: @container (width > 0) const container = layer.block!.first_child! - expect(container.type).toBe(NODE_AT_RULE) + expect(container.type).toBe(AT_RULE) expect(container.name).toBe('container') expect(container.prelude).toBe('(width > 0)') expect(container.has_block).toBe(true) // Inside @container: ul:has(:nth-child(1 of li)) const ulRule = container.block!.first_child! - expect(ulRule.type).toBe(NODE_STYLE_RULE) + expect(ulRule.type).toBe(STYLE_RULE) expect(ulRule.has_block).toBe(true) // Verify selector contains ul and :has(:nth-child(1 of li)) const selectorList = ulRule.first_child! - expect(selectorList.type).toBe(NODE_SELECTOR_LIST) + expect(selectorList.type).toBe(SELECTOR_LIST) const selector = selectorList.first_child! - expect(selector.type).toBe(NODE_SELECTOR) + expect(selector.type).toBe(SELECTOR) // The selector should have ul type selector and :has() pseudo-class const selectorParts = selector.children expect(selectorParts.length).toBeGreaterThan(0) - expect(selectorParts[0].type).toBe(NODE_SELECTOR_TYPE) + expect(selectorParts[0].type).toBe(TYPE_SELECTOR) expect(selectorParts[0].text).toBe('ul') // Inside ul rule: @media (height > 0) const media = ulRule.block!.first_child! - expect(media.type).toBe(NODE_AT_RULE) + expect(media.type).toBe(AT_RULE) expect(media.name).toBe('media') expect(media.prelude).toBe('(height > 0)') expect(media.has_block).toBe(true) // Inside @media: &:hover const nestingRule = media.block!.first_child! - expect(nestingRule.type).toBe(NODE_STYLE_RULE) + expect(nestingRule.type).toBe(STYLE_RULE) expect(nestingRule.has_block).toBe(true) // Verify nesting selector &:hover const nestingSelectorList = nestingRule.first_child! - expect(nestingSelectorList.type).toBe(NODE_SELECTOR_LIST) + expect(nestingSelectorList.type).toBe(SELECTOR_LIST) const nestingSelector = nestingSelectorList.first_child! - expect(nestingSelector.type).toBe(NODE_SELECTOR) + expect(nestingSelector.type).toBe(SELECTOR) const nestingParts = nestingSelector.children expect(nestingParts.length).toBeGreaterThan(0) - expect(nestingParts[0].type).toBe(NODE_SELECTOR_NESTING) + expect(nestingParts[0].type).toBe(NESTING_SELECTOR) expect(nestingParts[0].text).toBe('&') // Inside &:hover: --is: this declaration const declaration = nestingRule.block!.first_child! - expect(declaration.type).toBe(NODE_DECLARATION) + expect(declaration.type).toBe(DECLARATION) expect(declaration.property).toBe('--is') expect(declaration.value).toBe('this') }) diff --git a/src/parse.ts b/src/parse.ts index bc2a8c6..f85fccd 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -2,12 +2,12 @@ import { Lexer } from './lexer' import { CSSDataArena, - NODE_STYLESHEET, - NODE_STYLE_RULE, - NODE_SELECTOR_LIST, - NODE_DECLARATION, - NODE_AT_RULE, - NODE_BLOCK, + STYLESHEET, + STYLE_RULE, + SELECTOR_LIST, + DECLARATION, + AT_RULE, + BLOCK, FLAG_IMPORTANT, FLAG_HAS_BLOCK, FLAG_VENDOR_PREFIXED, @@ -106,7 +106,7 @@ export class Parser { // Create the root stylesheet node let stylesheet = this.arena.create_node() - this.arena.set_type(stylesheet, NODE_STYLESHEET) + this.arena.set_type(stylesheet, STYLESHEET) this.arena.set_start_offset(stylesheet, 0) this.arena.set_length(stylesheet, this.source.length) this.arena.set_start_line(stylesheet, 1) @@ -152,7 +152,7 @@ export class Parser { // Create the style rule node let style_rule = this.arena.create_node() - this.arena.set_type(style_rule, NODE_STYLE_RULE) + this.arena.set_type(style_rule, STYLE_RULE) this.arena.set_start_line(style_rule, rule_line) this.arena.set_start_column(style_rule, rule_column) @@ -176,7 +176,7 @@ export class Parser { let block_line = this.lexer.token_line let block_column = this.lexer.token_column let block_node = this.arena.create_node() - this.arena.set_type(block_node, NODE_BLOCK) + this.arena.set_type(block_node, BLOCK) this.arena.set_start_offset(block_node, block_start) this.arena.set_start_line(block_node, block_line) this.arena.set_start_column(block_node, block_column) @@ -260,7 +260,7 @@ export class Parser { // Otherwise create a simple selector list node with just text offsets let selector = this.arena.create_node() - this.arena.set_type(selector, NODE_SELECTOR_LIST) + this.arena.set_type(selector, SELECTOR_LIST) this.arena.set_start_line(selector, selector_line) this.arena.set_start_column(selector, selector_column) this.arena.set_start_offset(selector, selector_start) @@ -296,7 +296,7 @@ export class Parser { // Create declaration node let declaration = this.arena.create_node() - this.arena.set_type(declaration, NODE_DECLARATION) + this.arena.set_type(declaration, DECLARATION) this.arena.set_start_line(declaration, decl_line) this.arena.set_start_column(declaration, decl_column) this.arena.set_start_offset(declaration, prop_start) @@ -408,7 +408,7 @@ export class Parser { // Create at-rule node let at_rule = this.arena.create_node() - this.arena.set_type(at_rule, NODE_AT_RULE) + this.arena.set_type(at_rule, AT_RULE) this.arena.set_start_line(at_rule, at_rule_line) this.arena.set_start_column(at_rule, at_rule_column) this.arena.set_start_offset(at_rule, at_rule_start) @@ -457,7 +457,7 @@ export class Parser { let block_line = this.lexer.token_line let block_column = this.lexer.token_column let block_node = this.arena.create_node() - this.arena.set_type(block_node, NODE_BLOCK) + this.arena.set_type(block_node, BLOCK) this.arena.set_start_offset(block_node, block_start) this.arena.set_start_line(block_node, block_line) this.arena.set_start_column(block_node, block_column) diff --git a/src/stylerule-structure.test.ts b/src/stylerule-structure.test.ts index 63155c4..71a0eef 100644 --- a/src/stylerule-structure.test.ts +++ b/src/stylerule-structure.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from 'vitest' import { Parser } from './parse' -import { NODE_STYLE_RULE, NODE_SELECTOR_LIST, NODE_DECLARATION, NODE_AT_RULE } from './arena' +import { STYLE_RULE, SELECTOR_LIST, DECLARATION, AT_RULE } from './arena' describe('StyleRule Structure', () => { test('should have selector list as first child, followed by declarations', () => { @@ -8,11 +8,11 @@ describe('StyleRule Structure', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) + expect(rule.type).toBe(STYLE_RULE) // First child must be selector list const firstChild = rule.first_child! - expect(firstChild.type).toBe(NODE_SELECTOR_LIST) + expect(firstChild.type).toBe(SELECTOR_LIST) // Second child should be block containing declarations const block = firstChild.next_sibling! @@ -20,12 +20,12 @@ describe('StyleRule Structure', () => { // Declarations should be inside the block const secondChild = block.first_child! - expect(secondChild.type).toBe(NODE_DECLARATION) + expect(secondChild.type).toBe(DECLARATION) // Second declaration const thirdChild = secondChild.next_sibling! expect(thirdChild).not.toBeNull() - expect(thirdChild.type).toBe(NODE_DECLARATION) + expect(thirdChild.type).toBe(DECLARATION) // No more children expect(thirdChild.next_sibling).toBeNull() @@ -37,7 +37,7 @@ describe('StyleRule Structure', () => { const rule = root.first_child! const selectorList = rule.first_child! - expect(selectorList.type).toBe(NODE_SELECTOR_LIST) + expect(selectorList.type).toBe(SELECTOR_LIST) // Get all children of the selector list const children = [] @@ -68,7 +68,7 @@ describe('StyleRule Structure', () => { const rule = root.first_child! const selectorList = rule.first_child! - expect(selectorList.type).toBe(NODE_SELECTOR_LIST) + expect(selectorList.type).toBe(SELECTOR_LIST) // Collect all NODE_SELECTOR wrappers (direct children of selector list) const selectors = [] @@ -114,12 +114,12 @@ describe('StyleRule Structure', () => { const rule = root.first_child! // First child must be selector list - expect(rule.first_child!.type).toBe(NODE_SELECTOR_LIST) + expect(rule.first_child!.type).toBe(SELECTOR_LIST) // Walk through all children and verify no other selector lists let child = rule.first_child!.next_sibling while (child) { - expect(child.type).not.toBe(NODE_SELECTOR_LIST) + expect(child.type).not.toBe(SELECTOR_LIST) child = child.next_sibling } }) @@ -131,20 +131,20 @@ describe('StyleRule Structure', () => { const outerRule = root.first_child! // Outer rule structure - expect(outerRule.type).toBe(NODE_STYLE_RULE) - expect(outerRule.first_child!.type).toBe(NODE_SELECTOR_LIST) + expect(outerRule.type).toBe(STYLE_RULE) + expect(outerRule.first_child!.type).toBe(SELECTOR_LIST) // Find the nested rule (inside the block) const block = outerRule.first_child!.next_sibling! const nestedRule = block.first_child! - expect(nestedRule.type).toBe(NODE_STYLE_RULE) + expect(nestedRule.type).toBe(STYLE_RULE) // Nested rule should also have selector list as first child - expect(nestedRule.first_child!.type).toBe(NODE_SELECTOR_LIST) + expect(nestedRule.first_child!.type).toBe(SELECTOR_LIST) // Declaration comes after selector list in nested rule's block const nestedBlock = nestedRule.first_child!.next_sibling! - expect(nestedBlock.first_child!.type).toBe(NODE_DECLARATION) + expect(nestedBlock.first_child!.type).toBe(DECLARATION) }) test('& span should be parsed as ONE selector with 3 components', () => { @@ -155,11 +155,11 @@ describe('StyleRule Structure', () => { // Find the nested rule (& span) const block = outerRule.first_child!.next_sibling! const nestedRule = block.first_child! - expect(nestedRule.type).toBe(NODE_STYLE_RULE) + expect(nestedRule.type).toBe(STYLE_RULE) // Get selector list const selectorList = nestedRule.first_child! - expect(selectorList.type).toBe(NODE_SELECTOR_LIST) + expect(selectorList.type).toBe(SELECTOR_LIST) // Count how many selectors in the list (should be 1, not 2) const selectors = [] @@ -225,8 +225,8 @@ describe('StyleRule Structure', () => { const root = parser.parse() const rule = root.first_child! - expect(rule.type).toBe(NODE_STYLE_RULE) - expect(rule.first_child!.type).toBe(NODE_SELECTOR_LIST) + expect(rule.type).toBe(STYLE_RULE) + expect(rule.first_child!.type).toBe(SELECTOR_LIST) // Rule should have selector list + empty block const block = rule.first_child!.next_sibling @@ -256,7 +256,7 @@ describe('StyleRule Structure', () => { // Verify each child is a declaration for (let i = 0; i < children.length; i++) { - expect(children[i].type).toBe(NODE_DECLARATION) + expect(children[i].type).toBe(DECLARATION) } // Verify next_sibling chain @@ -298,11 +298,11 @@ describe('StyleRule Structure', () => { expect(children.length).toBe(5) // Verify types in order - expect(children[0].type).toBe(NODE_DECLARATION) // color: red - expect(children[1].type).toBe(NODE_STYLE_RULE) // .nested { margin: 0; } - expect(children[2].type).toBe(NODE_DECLARATION) // padding: 10px - expect(children[3].type).toBe(NODE_AT_RULE) // @media print { display: none; } - expect(children[4].type).toBe(NODE_DECLARATION) // font-size: 16px + expect(children[0].type).toBe(DECLARATION) // color: red + expect(children[1].type).toBe(STYLE_RULE) // .nested { margin: 0; } + expect(children[2].type).toBe(DECLARATION) // padding: 10px + expect(children[3].type).toBe(AT_RULE) // @media print { display: none; } + expect(children[4].type).toBe(DECLARATION) // font-size: 16px // Verify next_sibling chain for (let i = 0; i < children.length - 1; i++) { @@ -343,7 +343,7 @@ describe('StyleRule Structure', () => { // Verify each is a style rule for (const child of children) { - expect(child.type).toBe(NODE_STYLE_RULE) + expect(child.type).toBe(STYLE_RULE) } // Verify next_sibling chain @@ -384,7 +384,7 @@ describe('StyleRule Structure', () => { // Verify each is an at-rule for (const child of children) { - expect(child.type).toBe(NODE_AT_RULE) + expect(child.type).toBe(AT_RULE) } // Verify next_sibling chain diff --git a/src/walk.test.ts b/src/walk.test.ts index 15492b9..6a4ef87 100644 --- a/src/walk.test.ts +++ b/src/walk.test.ts @@ -1,16 +1,6 @@ import { describe, it, expect } from 'vitest' import { Parser } from './parse' -import { - NODE_STYLESHEET, - NODE_STYLE_RULE, - NODE_SELECTOR_LIST, - NODE_DECLARATION, - NODE_AT_RULE, - NODE_BLOCK, - NODE_VALUE_KEYWORD, - NODE_VALUE_NUMBER, - NODE_VALUE_DIMENSION, -} from './constants' +import { STYLESHEET, STYLE_RULE, SELECTOR_LIST, DECLARATION, AT_RULE, BLOCK, IDENTIFIER, NUMBER, DIMENSION } from './constants' import { walk, traverse } from './walk' describe('walk', () => { @@ -23,7 +13,7 @@ describe('walk', () => { visited.push(node.type) }) - expect(visited).toEqual([NODE_STYLESHEET]) + expect(visited).toEqual([STYLESHEET]) }) it('should visit all nodes in simple rule', () => { @@ -36,12 +26,12 @@ describe('walk', () => { }) expect(visited).toEqual([ - NODE_STYLESHEET, - NODE_STYLE_RULE, - NODE_SELECTOR_LIST, - NODE_BLOCK, - NODE_DECLARATION, - NODE_VALUE_KEYWORD, // red + STYLESHEET, + STYLE_RULE, + SELECTOR_LIST, + BLOCK, + DECLARATION, + IDENTIFIER, // red ]) }) @@ -55,19 +45,19 @@ describe('walk', () => { }) expect(visited).toEqual([ - NODE_STYLESHEET, - NODE_STYLE_RULE, // body rule - NODE_SELECTOR_LIST, // body selector - NODE_BLOCK, // body block - NODE_DECLARATION, // color: red - NODE_VALUE_KEYWORD, // red - NODE_DECLARATION, // margin: 0 - NODE_VALUE_NUMBER, // 0 - NODE_STYLE_RULE, // div rule - NODE_SELECTOR_LIST, // div selector - NODE_BLOCK, // div block - NODE_DECLARATION, // padding: 1rem - NODE_VALUE_DIMENSION, // 1rem + STYLESHEET, + STYLE_RULE, // body rule + SELECTOR_LIST, // body selector + BLOCK, // body block + DECLARATION, // color: red + IDENTIFIER, // red + DECLARATION, // margin: 0 + NUMBER, // 0 + STYLE_RULE, // div rule + SELECTOR_LIST, // div selector + BLOCK, // div block + DECLARATION, // padding: 1rem + DIMENSION, // 1rem ]) }) @@ -81,15 +71,15 @@ describe('walk', () => { }) expect(visited).toEqual([ - NODE_STYLESHEET, - NODE_STYLE_RULE, // .parent - NODE_SELECTOR_LIST, // .parent selector - NODE_BLOCK, // .parent block - NODE_DECLARATION, // color: red - NODE_STYLE_RULE, // .child (nested inside parent's block) - NODE_SELECTOR_LIST, // .child selector - NODE_BLOCK, // .child block - NODE_DECLARATION, // color: blue + STYLESHEET, + STYLE_RULE, // .parent + SELECTOR_LIST, // .parent selector + BLOCK, // .parent block + DECLARATION, // color: red + STYLE_RULE, // .child (nested inside parent's block) + SELECTOR_LIST, // .child selector + BLOCK, // .child block + DECLARATION, // color: blue ]) }) @@ -107,13 +97,13 @@ describe('walk', () => { }) expect(visited).toEqual([ - NODE_STYLESHEET, - NODE_AT_RULE, // @media - NODE_BLOCK, // @media block - NODE_STYLE_RULE, // body - NODE_SELECTOR_LIST, // body selector - NODE_BLOCK, // body block - NODE_DECLARATION, // color: red + STYLESHEET, + AT_RULE, // @media + BLOCK, // @media block + STYLE_RULE, // body + SELECTOR_LIST, // body selector + BLOCK, // body block + DECLARATION, // color: red ]) }) @@ -123,7 +113,7 @@ describe('walk', () => { const selectors: string[] = [] walk(root, (node) => { - if (node.type === NODE_SELECTOR_LIST) { + if (node.type === SELECTOR_LIST) { selectors.push(node.text) } }) @@ -137,7 +127,7 @@ describe('walk', () => { const properties: string[] = [] walk(root, (node) => { - if (node.type === NODE_DECLARATION) { + if (node.type === DECLARATION) { const name = node.name if (name) properties.push(name) } @@ -161,11 +151,11 @@ describe('walk', () => { counts[node.type] = (counts[node.type] || 0) + 1 }) - expect.soft(counts[NODE_STYLESHEET]).toBe(1) - expect.soft(counts[NODE_STYLE_RULE]).toBe(3) - expect.soft(counts[NODE_SELECTOR_LIST]).toBe(3) - expect.soft(counts[NODE_DECLARATION]).toBe(3) - expect.soft(counts[NODE_AT_RULE]).toBe(1) + expect.soft(counts[STYLESHEET]).toBe(1) + expect.soft(counts[STYLE_RULE]).toBe(3) + expect.soft(counts[SELECTOR_LIST]).toBe(3) + expect.soft(counts[DECLARATION]).toBe(3) + expect.soft(counts[AT_RULE]).toBe(1) }) it('should work with deeply nested structures', () => { @@ -174,7 +164,7 @@ describe('walk', () => { const rules: number[] = [] walk(root, (node) => { - if (node.type === NODE_STYLE_RULE) { + if (node.type === STYLE_RULE) { rules.push(node.type) } }) @@ -191,7 +181,7 @@ describe('walk', () => { depths.push(depth) }) - // NODE_STYLESHEET (0), NODE_STYLE_RULE (1), NODE_SELECTOR_LIST (2), NODE_BLOCK (2), NODE_DECLARATION (3), NODE_VALUE_KEYWORD (4) + // STYLESHEET (0), STYLE_RULE (1), SELECTOR_LIST (2), BLOCK (2), DECLARATION (3), IDENTIFIER (4) expect(depths).toEqual([0, 1, 2, 2, 3, 4]) }) @@ -201,7 +191,7 @@ describe('walk', () => { const ruleDepths: number[] = [] walk(root, (node, depth) => { - if (node.type === NODE_STYLE_RULE) { + if (node.type === STYLE_RULE) { ruleDepths.push(depth) } }) @@ -223,13 +213,13 @@ describe('walk', () => { }) expect(typeAndDepth).toEqual([ - { type: NODE_STYLESHEET, depth: 0 }, - { type: NODE_AT_RULE, depth: 1 }, // @media - { type: NODE_BLOCK, depth: 2 }, // @media block - { type: NODE_STYLE_RULE, depth: 3 }, // body - { type: NODE_SELECTOR_LIST, depth: 4 }, // body selector - { type: NODE_BLOCK, depth: 4 }, // body block - { type: NODE_DECLARATION, depth: 5 }, // color: red + { type: STYLESHEET, depth: 0 }, + { type: AT_RULE, depth: 1 }, // @media + { type: BLOCK, depth: 2 }, // @media block + { type: STYLE_RULE, depth: 3 }, // body + { type: SELECTOR_LIST, depth: 4 }, // body selector + { type: BLOCK, depth: 4 }, // body block + { type: DECLARATION, depth: 5 }, // color: red ]) }) @@ -247,19 +237,19 @@ describe('walk', () => { }) expect(typeAndDepth).toEqual([ - { type: NODE_STYLESHEET, depth: 0 }, - { type: NODE_AT_RULE, depth: 1 }, // @media - { type: NODE_BLOCK, depth: 2 }, // @media block - { type: NODE_STYLE_RULE, depth: 3 }, // body - { type: NODE_SELECTOR_LIST, depth: 4 }, // body selector - { type: NODE_BLOCK, depth: 4 }, // body block - { type: NODE_DECLARATION, depth: 5 }, // color: red - { type: NODE_AT_RULE, depth: 1 }, // @layer - { type: NODE_BLOCK, depth: 2 }, // @layer block - { type: NODE_STYLE_RULE, depth: 3 }, - { type: NODE_SELECTOR_LIST, depth: 4 }, - { type: NODE_BLOCK, depth: 4 }, - { type: NODE_DECLARATION, depth: 5 }, + { type: STYLESHEET, depth: 0 }, + { type: AT_RULE, depth: 1 }, // @media + { type: BLOCK, depth: 2 }, // @media block + { type: STYLE_RULE, depth: 3 }, // body + { type: SELECTOR_LIST, depth: 4 }, // body selector + { type: BLOCK, depth: 4 }, // body block + { type: DECLARATION, depth: 5 }, // color: red + { type: AT_RULE, depth: 1 }, // @layer + { type: BLOCK, depth: 2 }, // @layer block + { type: STYLE_RULE, depth: 3 }, + { type: SELECTOR_LIST, depth: 4 }, + { type: BLOCK, depth: 4 }, + { type: DECLARATION, depth: 5 }, ]) }) @@ -267,7 +257,7 @@ describe('walk', () => { let ast = new Parser('a{}').parse() walk(ast, (node, _depth) => { expectTypeOf(node.type).toBeNumber() - if (node.type === NODE_SELECTOR_LIST) { + if (node.type === SELECTOR_LIST) { expect(node.text).toEqual('a') } }) @@ -295,8 +285,8 @@ describe('walk enter/leave', () => { }, }) - expect(enter).toEqual([NODE_STYLESHEET, NODE_AT_RULE, NODE_BLOCK, NODE_STYLE_RULE, NODE_SELECTOR_LIST, NODE_BLOCK, NODE_DECLARATION]) - expect(leave).toEqual([NODE_SELECTOR_LIST, NODE_DECLARATION, NODE_BLOCK, NODE_STYLE_RULE, NODE_BLOCK, NODE_AT_RULE, NODE_STYLESHEET]) + expect(enter).toEqual([STYLESHEET, AT_RULE, BLOCK, STYLE_RULE, SELECTOR_LIST, BLOCK, DECLARATION]) + expect(leave).toEqual([SELECTOR_LIST, DECLARATION, BLOCK, STYLE_RULE, BLOCK, AT_RULE, STYLESHEET]) }) test('only enter', () => { @@ -308,7 +298,7 @@ describe('walk enter/leave', () => { }, }) - expect(enter).toEqual([NODE_STYLESHEET, NODE_AT_RULE, NODE_BLOCK, NODE_STYLE_RULE, NODE_SELECTOR_LIST, NODE_BLOCK, NODE_DECLARATION]) + expect(enter).toEqual([STYLESHEET, AT_RULE, BLOCK, STYLE_RULE, SELECTOR_LIST, BLOCK, DECLARATION]) }) test('only leave', () => { @@ -320,7 +310,7 @@ describe('walk enter/leave', () => { }, }) - expect(leave).toEqual([NODE_SELECTOR_LIST, NODE_DECLARATION, NODE_BLOCK, NODE_STYLE_RULE, NODE_BLOCK, NODE_AT_RULE, NODE_STYLESHEET]) + expect(leave).toEqual([SELECTOR_LIST, DECLARATION, BLOCK, STYLE_RULE, BLOCK, AT_RULE, STYLESHEET]) }) test('neither', () => {