diff --git a/src/api.test.ts b/src/api.test.ts
index c017e68..8d342e5 100644
--- a/src/api.test.ts
+++ b/src/api.test.ts
@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest'
-import { Parser, parse } from './parse'
+import { parse } from './parse'
import { parse_selector } from './parse-selector'
import {
DECLARATION,
@@ -18,8 +18,7 @@ describe('CSSNode', () => {
describe('iteration', () => {
test('should be iterable with for-of', () => {
const source = 'body { color: red; margin: 0; padding: 10px; }'
- const parser = new Parser(source, { parse_selectors: false, parse_values: false })
- const root = parser.parse()
+ const root = parse(source, { parse_selectors: false, parse_values: false })
const rule = root.first_child!
const block = rule.block!
@@ -34,8 +33,7 @@ describe('CSSNode', () => {
test('should work with spread operator', () => {
const source = 'body { color: red; } div { margin: 0; }'
- const parser = new Parser(source, { parse_selectors: false, parse_values: false })
- const root = parser.parse()
+ const root = parse(source, { parse_selectors: false, parse_values: false })
const rules = [...root]
expect(rules).toHaveLength(2)
@@ -45,8 +43,7 @@ describe('CSSNode', () => {
test('should work with Array.from', () => {
const source = '@media print { body { color: black; } }'
- const parser = new Parser(source, { parse_selectors: false, parse_values: false, parse_atrule_preludes: false })
- const root = parser.parse()
+ const root = parse(source, { parse_selectors: false, parse_values: false, parse_atrule_preludes: false })
const media = root.first_child!
const block = media.block!
@@ -58,12 +55,11 @@ describe('CSSNode', () => {
test('should iterate over empty children', () => {
const source = '@import url("style.css");'
- const parser = new Parser(source, {
+ const root = parse(source, {
parse_selectors: false,
parse_values: false,
parse_atrule_preludes: false,
})
- const root = parser.parse()
const importRule = root.first_child!
const children = [...importRule]
@@ -75,8 +71,7 @@ describe('CSSNode', () => {
describe('has_prelude', () => {
test('should return true for @media with prelude', () => {
const source = '@media (min-width: 768px) { body { color: red; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const media = root.first_child!
expect(media.type).toBe(AT_RULE)
@@ -86,8 +81,7 @@ describe('CSSNode', () => {
test('should return true for @supports with prelude', () => {
const source = '@supports (display: grid) { .grid { display: grid; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const supports = root.first_child!
expect(supports.type).toBe(AT_RULE)
@@ -97,8 +91,7 @@ describe('CSSNode', () => {
test('should return true for @layer with name', () => {
const source = '@layer utilities { .btn { padding: 1rem; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const layer = root.first_child!
expect(layer.type).toBe(AT_RULE)
@@ -108,8 +101,7 @@ describe('CSSNode', () => {
test('should return false for @layer without name', () => {
const source = '@layer { .btn { padding: 1rem; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const layer = root.first_child!
expect(layer.type).toBe(AT_RULE)
@@ -119,8 +111,7 @@ describe('CSSNode', () => {
test('should return true for @keyframes with name', () => {
const source = '@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const keyframes = root.first_child!
expect(keyframes.type).toBe(AT_RULE)
@@ -130,8 +121,7 @@ describe('CSSNode', () => {
test('should return false for @font-face without prelude', () => {
const source = '@font-face { font-family: "Custom"; src: url("font.woff2"); }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const fontFace = root.first_child!
expect(fontFace.type).toBe(AT_RULE)
@@ -141,8 +131,7 @@ describe('CSSNode', () => {
test('should return false for @page without prelude', () => {
const source = '@page { margin: 1in; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const page = root.first_child!
expect(page.type).toBe(AT_RULE)
@@ -152,8 +141,7 @@ describe('CSSNode', () => {
test('should return true for @import with options', () => {
const source = '@import url("styles.css") layer(base) supports(display: flex);'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const importRule = root.first_child!
expect(importRule.type).toBe(AT_RULE)
@@ -163,8 +151,7 @@ describe('CSSNode', () => {
test('should work efficiently without creating strings', () => {
const source = '@media (min-width: 768px) { body { color: red; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const media = root.first_child!
// has_prelude should be faster than prelude !== null
@@ -175,8 +162,7 @@ describe('CSSNode', () => {
test('should work for other node types that use value field', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selector = rule.first_child!
const block = selector.next_sibling!
@@ -196,8 +182,7 @@ describe('CSSNode', () => {
describe('has_block', () => {
test('should return true for style rules with blocks', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -206,8 +191,7 @@ describe('CSSNode', () => {
test('should return true for empty style rule blocks', () => {
const source = 'body { }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -216,8 +200,7 @@ describe('CSSNode', () => {
test('should return true for @media with block', () => {
const source = '@media (min-width: 768px) { body { color: red; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const media = root.first_child!
expect(media.type).toBe(AT_RULE)
@@ -226,8 +209,7 @@ describe('CSSNode', () => {
test('should return true for @supports with block', () => {
const source = '@supports (display: grid) { .grid { display: grid; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const supports = root.first_child!
expect(supports.type).toBe(AT_RULE)
@@ -236,8 +218,7 @@ describe('CSSNode', () => {
test('should return true for @layer with block', () => {
const source = '@layer utilities { .btn { padding: 1rem; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const layer = root.first_child!
expect(layer.type).toBe(AT_RULE)
@@ -246,8 +227,7 @@ describe('CSSNode', () => {
test('should return true for anonymous @layer with block', () => {
const source = '@layer { .btn { padding: 1rem; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const layer = root.first_child!
expect(layer.type).toBe(AT_RULE)
@@ -256,8 +236,7 @@ describe('CSSNode', () => {
test('should return true for @font-face with block', () => {
const source = '@font-face { font-family: "Custom"; src: url("font.woff2"); }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const fontFace = root.first_child!
expect(fontFace.type).toBe(AT_RULE)
@@ -266,8 +245,7 @@ describe('CSSNode', () => {
test('should return true for @keyframes with block', () => {
const source = '@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const keyframes = root.first_child!
expect(keyframes.type).toBe(AT_RULE)
@@ -276,8 +254,7 @@ describe('CSSNode', () => {
test('should return false for @import without block', () => {
const source = '@import url("styles.css");'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const importRule = root.first_child!
expect(importRule.type).toBe(AT_RULE)
@@ -286,8 +263,7 @@ describe('CSSNode', () => {
test('should return false for @import with preludes but no block', () => {
const source = '@import url("styles.css") layer(base) supports(display: flex);'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const importRule = root.first_child!
expect(importRule.type).toBe(AT_RULE)
@@ -301,8 +277,7 @@ describe('CSSNode', () => {
@import url("file.css") layer(base);
@layer utilities { .btn { padding: 1rem; } }
`
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const importRule = root.first_child!
const layerRule = importRule.next_sibling!
@@ -317,8 +292,7 @@ describe('CSSNode', () => {
test('should return false for non-rule nodes', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selector = rule.first_child!
const declaration = selector.next_sibling!
@@ -337,8 +311,7 @@ describe('CSSNode', () => {
@font-face { font-family: "Custom"; }
@keyframes fadeIn { from { opacity: 0; } }
`
- const parser = new Parser(css)
- const root = parser.parse()
+ const root = parse(css)
const nodes = [...root]
const [media, importRule, supports, layer, fontFace, keyframes] = nodes
@@ -355,8 +328,7 @@ describe('CSSNode', () => {
describe('has_declarations', () => {
test('should return true for style rules with declarations', () => {
const source = 'body { color: red; margin: 0; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -365,8 +337,7 @@ describe('CSSNode', () => {
test('should return false for empty style rules', () => {
const source = 'body { }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -375,8 +346,7 @@ describe('CSSNode', () => {
test('should return false for style rules with only nested rules', () => {
const source = 'body { .nested { color: red; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -385,8 +355,7 @@ describe('CSSNode', () => {
test('should return true for style rules with both declarations and nested rules', () => {
const source = 'body { color: blue; .nested { margin: 0; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -395,8 +364,7 @@ describe('CSSNode', () => {
test('should return false for at-rules', () => {
const source = '@media screen { body { color: red; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const media = root.first_child!
expect(media.type).toBe(AT_RULE)
@@ -407,16 +375,14 @@ describe('CSSNode', () => {
describe('type_name property', () => {
test('should return stylesheet for root node', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
expect(root.type_name).toBe('StyleSheet')
})
test('should return style_rule for style rules', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
expect(rule.type_name).toBe('Rule')
@@ -424,8 +390,7 @@ describe('CSSNode', () => {
test('should return declaration for declarations', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const block = rule.block!
const decl = block.first_child!
@@ -435,8 +400,7 @@ describe('CSSNode', () => {
test('should return at_rule for at-rules', () => {
const source = '@media screen { body { color: red; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const media = root.first_child!
expect(media.type_name).toBe('Atrule')
@@ -444,8 +408,7 @@ describe('CSSNode', () => {
test('should return selector_list for selector lists', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
@@ -454,8 +417,7 @@ describe('CSSNode', () => {
test('should return selector_type for type selectors', () => {
const source = 'div { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -466,8 +428,7 @@ describe('CSSNode', () => {
test('should return selector_class for class selectors', () => {
const source = '.foo { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -478,8 +439,7 @@ describe('CSSNode', () => {
test('should return selector_id for ID selectors', () => {
const source = '#bar { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -490,8 +450,7 @@ describe('CSSNode', () => {
test('should return selector_universal for universal selectors', () => {
const source = '* { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -502,8 +461,7 @@ describe('CSSNode', () => {
test('should return selector_attribute for attribute selectors', () => {
const source = '[href] { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -514,8 +472,7 @@ describe('CSSNode', () => {
test('should return selector_pseudo_class for pseudo-class selectors', () => {
const source = ':hover { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -526,8 +483,7 @@ describe('CSSNode', () => {
test('should return selector_pseudo_element for pseudo-element selectors', () => {
const source = '::before { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -538,8 +494,7 @@ describe('CSSNode', () => {
test('should return selector_combinator for combinators', () => {
const source = 'div > span { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const selectorList = rule.first_child!
const selector = selectorList.first_child!
@@ -550,8 +505,7 @@ describe('CSSNode', () => {
test('should return value_keyword for keyword values', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const block = rule.block!
const decl = block.first_child!
@@ -562,8 +516,7 @@ describe('CSSNode', () => {
test('should return value_number for numeric values', () => {
const source = 'body { opacity: 0.5; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const block = rule.block!
const decl = block.first_child!
@@ -574,8 +527,7 @@ describe('CSSNode', () => {
test('should return value_dimension for dimension values', () => {
const source = 'body { width: 100px; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const block = rule.block!
const decl = block.first_child!
@@ -586,8 +538,7 @@ describe('CSSNode', () => {
test('should return value_string for string values', () => {
const source = 'body { content: "hello"; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const block = rule.block!
const decl = block.first_child!
@@ -598,8 +549,7 @@ describe('CSSNode', () => {
test('should return value_color for color values', () => {
const source = 'body { color: #ff0000; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const block = rule.block!
const decl = block.first_child!
@@ -610,8 +560,7 @@ describe('CSSNode', () => {
test('should return value_function for function values', () => {
const source = 'body { width: calc(100% - 20px); }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const rule = root.first_child!
const block = rule.block!
const decl = block.first_child!
@@ -622,8 +571,7 @@ describe('CSSNode', () => {
test('should return prelude_media_query for media query preludes', () => {
const source = '@media screen and (min-width: 768px) { body { color: red; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
const media = root.first_child!
const prelude = media.first_child!
diff --git a/src/arena.ts b/src/arena.ts
index b1695cc..4232dfd 100644
--- a/src/arena.ts
+++ b/src/arena.ts
@@ -89,6 +89,7 @@ export const ATTR_FLAG_NONE = 0 // No flag
export const ATTR_FLAG_CASE_INSENSITIVE = 1 // [attr=value i]
export const ATTR_FLAG_CASE_SENSITIVE = 2 // [attr=value s]
+/** @internal */
export class CSSDataArena {
private buffer: ArrayBuffer
private view: DataView
diff --git a/src/lexer.ts b/src/lexer.ts
index b966e44..8080d97 100644
--- a/src/lexer.ts
+++ b/src/lexer.ts
@@ -68,6 +68,7 @@ export interface LexerPosition {
token_column: number
}
+/** @internal */
export class Lexer {
source: string
pos: number
diff --git a/src/parse-anplusb.ts b/src/parse-anplusb.ts
index ac3db97..2879f30 100644
--- a/src/parse-anplusb.ts
+++ b/src/parse-anplusb.ts
@@ -11,6 +11,7 @@ import { CHAR_MINUS_HYPHEN, CHAR_PLUS } from './string-utils'
import { skip_whitespace_forward } from './parse-utils'
import { CSSNode } from './css-node'
+/** @internal */
export class ANplusBParser {
private lexer: Lexer
private arena: CSSDataArena
diff --git a/src/parse-atrule-prelude.ts b/src/parse-atrule-prelude.ts
index 26441a0..bcdb691 100644
--- a/src/parse-atrule-prelude.ts
+++ b/src/parse-atrule-prelude.ts
@@ -28,6 +28,7 @@ import { str_equals } from './string-utils'
import { trim_boundaries, skip_whitespace_forward } from './parse-utils'
import { CSSNode } from './css-node'
+/** @internal */
export class AtRulePreludeParser {
private lexer: Lexer
private arena: CSSDataArena
diff --git a/src/parse-options.test.ts b/src/parse-options.test.ts
index 85a1bd7..c3b9ac9 100644
--- a/src/parse-options.test.ts
+++ b/src/parse-options.test.ts
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'
-import { Parser } from './parse'
+import { parse } from './parse'
import { SELECTOR_LIST, DECLARATION, IDENTIFIER } from './arena'
describe('Parser Options', () => {
@@ -7,8 +7,7 @@ describe('Parser Options', () => {
describe('Default behavior (all parsing enabled)', () => {
it('should parse values and selectors by default', () => {
- const parser = new Parser(css)
- const root = parser.parse()
+ const root = parse(css)
const rule = root.first_child
// Check selector is parsed with detailed structure
@@ -27,8 +26,7 @@ describe('Parser Options', () => {
})
it('should parse values and selectors with explicit options', () => {
- const parser = new Parser(css, { parse_values: true, parse_selectors: true })
- const root = parser.parse()
+ const root = parse(css, { parse_values: true, parse_selectors: true })
const rule = root.first_child
// Check selector is parsed
@@ -46,8 +44,7 @@ describe('Parser Options', () => {
describe('parse_values disabled', () => {
it('should not parse value details when parse_values is false', () => {
- const parser = new Parser(css, { parse_values: false })
- const root = parser.parse()
+ const root = parse(css, { parse_values: false })
const rule = root.first_child
// Selector should still be parsed
@@ -67,8 +64,7 @@ describe('Parser Options', () => {
})
it('should handle complex values without parsing', () => {
- const parser = new Parser('div { margin: 10px 20px; }', { parse_values: false })
- const root = parser.parse()
+ const root = parse('div { margin: 10px 20px; }', { parse_values: false })
const rule = root.first_child
const selector = rule?.first_child
const block = selector?.next_sibling
@@ -80,8 +76,7 @@ describe('Parser Options', () => {
})
it('should handle function values without parsing', () => {
- const parser = new Parser('div { color: rgb(255, 0, 0); }', { parse_values: false })
- const root = parser.parse()
+ const root = parse('div { color: rgb(255, 0, 0); }', { parse_values: false })
const rule = root.first_child
const selector = rule?.first_child
const block = selector?.next_sibling
@@ -95,8 +90,7 @@ describe('Parser Options', () => {
describe('parseSelectors disabled', () => {
it('should not parse selector details when parseSelectors is false', () => {
- const parser = new Parser(css, { parse_selectors: false })
- const root = parser.parse()
+ const root = parse(css, { parse_selectors: false })
const rule = root.first_child
// Selector should exist but be simple (just NODE_SELECTOR_LIST, no detailed structure)
@@ -114,8 +108,7 @@ describe('Parser Options', () => {
})
it('should handle complex selectors without parsing', () => {
- const parser = new Parser('div.container#app { color: red; }', { parse_selectors: false })
- const root = parser.parse()
+ const root = parse('div.container#app { color: red; }', { parse_selectors: false })
const rule = root.first_child
const selector = rule?.first_child
@@ -125,8 +118,7 @@ describe('Parser Options', () => {
})
it('should handle selector lists without parsing', () => {
- const parser = new Parser('div, p, span { color: red; }', { parse_selectors: false })
- const root = parser.parse()
+ const root = parse('div, p, span { color: red; }', { parse_selectors: false })
const rule = root.first_child
const selector = rule?.first_child
@@ -138,8 +130,7 @@ describe('Parser Options', () => {
describe('Both parse_values and parseSelectors disabled', () => {
it('should not parse details for values or selectors', () => {
- const parser = new Parser(css, { parse_values: false, parse_selectors: false })
- const root = parser.parse()
+ const root = parse(css, { parse_values: false, parse_selectors: false })
const rule = root.first_child
// Selector should be simple
@@ -164,8 +155,7 @@ describe('Parser Options', () => {
color: rgb(255, 0, 0);
}
`
- const parser = new Parser(css, { parse_values: false, parse_selectors: false })
- const root = parser.parse()
+ const root = parse(css, { parse_values: false, parse_selectors: false })
const rule = root.first_child
const selector = rule?.first_child
@@ -194,8 +184,7 @@ describe('Parser Options', () => {
margin: 10px 20px 30px 40px;
}
`
- const parser = new Parser(css, { parse_values: false })
- const root = parser.parse()
+ const root = parse(css, { parse_values: false })
const rule = root.first_child
const selector = rule?.first_child
@@ -219,8 +208,7 @@ describe('Parser Options', () => {
.another-complex[data-attr~="value"] { margin: 0; }
#very-specific-id:not(.excluded) { padding: 10px; }
`
- const parser = new Parser(css, { parse_selectors: false })
- const root = parser.parse()
+ const root = parse(css, { parse_selectors: false })
// Can quickly count rules without parsing complex selectors
let count = 0
@@ -236,8 +224,7 @@ describe('Parser Options', () => {
describe('Options validation', () => {
it('should accept empty options object', () => {
- const parser = new Parser(css, {})
- const root = parser.parse()
+ const root = parse(css, {})
const rule = root.first_child
const selector = rule?.first_child
const block = selector?.next_sibling
@@ -249,8 +236,7 @@ describe('Parser Options', () => {
})
it('should accept partial options', () => {
- const parser = new Parser(css, { parse_values: false })
- const root = parser.parse()
+ const root = parse(css, { parse_values: false })
const rule = root.first_child
const selector = rule?.first_child
const block = selector?.next_sibling
@@ -263,11 +249,10 @@ describe('Parser Options', () => {
})
it('should accept skip_comments with parsing options', () => {
- const parser = new Parser('/* test */ body { color: red; }', {
+ const root = parse('/* test */ body { color: red; }', {
skip_comments: true,
parse_values: false,
})
- const root = parser.parse()
const rule = root.first_child
// Comment should be skipped
diff --git a/src/parse-selector.ts b/src/parse-selector.ts
index 0276a43..557bc6d 100644
--- a/src/parse-selector.ts
+++ b/src/parse-selector.ts
@@ -69,6 +69,7 @@ import {
import { ANplusBParser } from './parse-anplusb'
import { CSSNode } from './css-node'
+/** @internal */
export class SelectorParser {
private lexer: Lexer
private arena: CSSDataArena
diff --git a/src/parse-utils.ts b/src/parse-utils.ts
index b8a6b2a..305219a 100644
--- a/src/parse-utils.ts
+++ b/src/parse-utils.ts
@@ -64,6 +64,7 @@ export function parse_dimension(text: string): { value: number; unit: string } {
* @param pos - Starting position
* @param end - End boundary (exclusive)
* @returns New position after skipping whitespace
+ * @internal
*/
export function skip_whitespace_forward(source: string, pos: number, end: number): number {
while (pos < end && is_whitespace(source.charCodeAt(pos))) {
@@ -79,6 +80,7 @@ export function skip_whitespace_forward(source: string, pos: number, end: number
* @param pos - Starting position
* @param end - End boundary (exclusive)
* @returns New position after skipping whitespace/comments
+ * @internal
*/
export function skip_whitespace_and_comments_forward(source: string, pos: number, end: number): number {
while (pos < end) {
@@ -115,6 +117,7 @@ export function skip_whitespace_and_comments_forward(source: string, pos: number
* @param pos - Starting position (exclusive, scanning backward from pos-1)
* @param start - Start boundary (inclusive, won't go before this)
* @returns New position after skipping whitespace/comments backward
+ * @internal
*/
export function skip_whitespace_and_comments_backward(source: string, pos: number, start: number): number {
while (pos > start) {
@@ -151,6 +154,7 @@ export function skip_whitespace_and_comments_backward(source: string, pos: numbe
* @param start - Start offset in source
* @param end - End offset in source
* @returns [trimmed_start, trimmed_end] or null if all whitespace/comments
+ * @internal
*
* Skips whitespace (space, tab, newline, CR, FF) and CSS comments from both ends
* of the specified range. Returns the trimmed boundaries or null if the range
diff --git a/src/parse-value.test.ts b/src/parse-value.test.ts
index 867edf7..02d2f6f 100644
--- a/src/parse-value.test.ts
+++ b/src/parse-value.test.ts
@@ -1,12 +1,11 @@
import { describe, it, expect } from 'vitest'
-import { Parser } from './parse'
+import { parse } from './parse'
import { IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL } from './arena'
describe('Value Node Types', () => {
// Helper to get first value node from a declaration
const getValue = (css: string) => {
- const parser = new Parser(css)
- const root = parser.parse()
+ const root = parse(css)
const rule = root.first_child
const decl = rule?.first_child?.next_sibling?.first_child // selector → block → declaration
return decl?.values[0]
@@ -68,8 +67,7 @@ describe('Value Node Types', () => {
describe('OPERATOR', () => {
it('should have correct offset and length', () => {
- const parser = new Parser('div { font-family: Arial, sans-serif; }')
- const root = parser.parse()
+ const root = parse('div { font-family: Arial, sans-serif; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const comma = decl?.values[1]
expect(comma?.offset).toBe(24)
@@ -80,8 +78,7 @@ describe('Value Node Types', () => {
describe('PARENTHESIS', () => {
it('should have correct offset and length', () => {
- const parser = new Parser('div { width: calc((100% - 50px) / 2); }')
- const root = parser.parse()
+ const root = parse('div { width: calc((100% - 50px) / 2); }')
const func = root.first_child?.first_child?.next_sibling?.first_child?.values[0]
const paren = func?.children[0]
expect(paren?.offset).toBe(18)
@@ -131,15 +128,13 @@ describe('Value Node Types', () => {
})
it('OPERATOR type constant', () => {
- const parser = new Parser('div { font-family: Arial, sans-serif; }')
- const root = parser.parse()
+ const root = parse('div { font-family: Arial, sans-serif; }')
const comma = root.first_child?.first_child?.next_sibling?.first_child?.values[1]
expect(comma?.type).toBe(OPERATOR)
})
it('PARENTHESIS type constant', () => {
- const parser = new Parser('div { width: calc((100% - 50px) / 2); }')
- const root = parser.parse()
+ const root = parse('div { width: calc((100% - 50px) / 2); }')
const func = root.first_child?.first_child?.next_sibling?.first_child?.values[0]
const paren = func?.children[0]
expect(paren?.type).toBe(PARENTHESIS)
@@ -183,15 +178,13 @@ describe('Value Node Types', () => {
})
it('OPERATOR type_name', () => {
- const parser = new Parser('div { font-family: Arial, sans-serif; }')
- const root = parser.parse()
+ const root = parse('div { font-family: Arial, sans-serif; }')
const comma = root.first_child?.first_child?.next_sibling?.first_child?.values[1]
expect(comma?.type_name).toBe('Operator')
})
it('PARENTHESIS type_name', () => {
- const parser = new Parser('div { width: calc((100% - 50px) / 2); }')
- const root = parser.parse()
+ const root = parse('div { width: calc((100% - 50px) / 2); }')
const func = root.first_child?.first_child?.next_sibling?.first_child?.values[0]
const paren = func?.children[0]
expect(paren?.type_name).toBe('Parentheses')
@@ -206,8 +199,7 @@ describe('Value Node Types', () => {
describe('Value Properties', () => {
describe('IDENTIFIER', () => {
it('should parse keyword values', () => {
- const parser = new Parser('body { color: red; }')
- const root = parser.parse()
+ const root = parse('body { color: red; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('red')
@@ -216,8 +208,7 @@ describe('Value Node Types', () => {
})
it('should parse multiple keywords', () => {
- const parser = new Parser('body { font-family: Arial, sans-serif; }')
- const root = parser.parse()
+ const root = parse('body { font-family: Arial, sans-serif; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(3)
@@ -230,8 +221,7 @@ describe('Value Node Types', () => {
describe('NUMBER', () => {
it('should parse number values', () => {
- const parser = new Parser('body { opacity: 0.5; }')
- const root = parser.parse()
+ const root = parse('body { opacity: 0.5; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('0.5')
@@ -240,8 +230,7 @@ describe('Value Node Types', () => {
})
it('should handle negative numbers', () => {
- const parser = new Parser('body { margin: -10px; }')
- const root = parser.parse()
+ const root = parse('body { margin: -10px; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(1)
@@ -250,8 +239,7 @@ describe('Value Node Types', () => {
})
it('should handle zero without unit', () => {
- const parser = new Parser('body { margin: 0; }')
- const root = parser.parse()
+ const root = parse('body { margin: 0; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(1)
@@ -262,8 +250,7 @@ describe('Value Node Types', () => {
describe('DIMENSION', () => {
it('should parse px dimension values', () => {
- const parser = new Parser('body { width: 100px; }')
- const root = parser.parse()
+ const root = parse('body { width: 100px; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('100px')
@@ -274,8 +261,7 @@ describe('Value Node Types', () => {
})
it('should parse em dimension values', () => {
- const parser = new Parser('body { font-size: 3em; }')
- const root = parser.parse()
+ const root = parse('body { font-size: 3em; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('3em')
@@ -286,8 +272,7 @@ describe('Value Node Types', () => {
})
it('should parse percentage values', () => {
- const parser = new Parser('body { width: 50%; }')
- const root = parser.parse()
+ const root = parse('body { width: 50%; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('50%')
@@ -296,8 +281,7 @@ describe('Value Node Types', () => {
})
it('should handle zero with unit', () => {
- const parser = new Parser('body { margin: 0px; }')
- const root = parser.parse()
+ const root = parse('body { margin: 0px; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(1)
@@ -306,8 +290,7 @@ describe('Value Node Types', () => {
})
it('should parse margin shorthand', () => {
- const parser = new Parser('body { margin: 10px 20px 30px 40px; }')
- const root = parser.parse()
+ const root = parse('body { margin: 10px 20px 30px 40px; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(4)
@@ -324,8 +307,7 @@ describe('Value Node Types', () => {
describe('STRING', () => {
it('should parse string values', () => {
- const parser = new Parser('body { content: "hello"; }')
- const root = parser.parse()
+ const root = parse('body { content: "hello"; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('"hello"')
@@ -336,8 +318,7 @@ describe('Value Node Types', () => {
describe('HASH', () => {
it('should parse color values', () => {
- const parser = new Parser('body { color: #ff0000; }')
- const root = parser.parse()
+ const root = parse('body { color: #ff0000; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('#ff0000')
@@ -348,8 +329,7 @@ describe('Value Node Types', () => {
describe('FUNCTION', () => {
it('should parse simple function', () => {
- const parser = new Parser('body { color: rgb(255, 0, 0); }')
- const root = parser.parse()
+ const root = parse('body { color: rgb(255, 0, 0); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(1)
@@ -359,8 +339,7 @@ describe('Value Node Types', () => {
})
it('should parse function arguments', () => {
- const parser = new Parser('body { color: rgb(255, 0, 0); }')
- const root = parser.parse()
+ const root = parse('body { color: rgb(255, 0, 0); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -378,8 +357,7 @@ describe('Value Node Types', () => {
})
it('should parse nested functions', () => {
- const parser = new Parser('body { width: calc(100% - 20px); }')
- const root = parser.parse()
+ const root = parse('body { width: calc(100% - 20px); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(1)
@@ -395,8 +373,7 @@ describe('Value Node Types', () => {
})
it('should parse var() function', () => {
- const parser = new Parser('body { color: var(--primary-color); }')
- const root = parser.parse()
+ const root = parse('body { color: var(--primary-color); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(1)
@@ -408,8 +385,7 @@ describe('Value Node Types', () => {
})
it('should provide node.value for calc()', () => {
- const parser = new Parser('body { width: calc(100% - 20px); }')
- const root = parser.parse()
+ const root = parse('body { width: calc(100% - 20px); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -421,8 +397,7 @@ describe('Value Node Types', () => {
})
it('should provide node.value for var() function', () => {
- const parser = new Parser('body { color: var(--primary-color); }')
- const root = parser.parse()
+ const root = parse('body { color: var(--primary-color); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -434,8 +409,7 @@ describe('Value Node Types', () => {
})
it('should parse transform value', () => {
- const parser = new Parser('body { transform: translateX(10px) rotate(45deg); }')
- const root = parser.parse()
+ const root = parse('body { transform: translateX(10px) rotate(45deg); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(2)
@@ -446,8 +420,7 @@ describe('Value Node Types', () => {
})
it('should parse filter value', () => {
- const parser = new Parser('body { filter: blur(5px) brightness(1.2); }')
- const root = parser.parse()
+ const root = parse('body { filter: blur(5px) brightness(1.2); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(2)
@@ -462,8 +435,7 @@ describe('Value Node Types', () => {
describe('OPERATOR', () => {
it('should parse comma operator', () => {
- const parser = new Parser('body { font-family: Arial, sans-serif; }')
- const root = parser.parse()
+ const root = parse('body { font-family: Arial, sans-serif; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values[1].type).toBe(OPERATOR)
@@ -471,8 +443,7 @@ describe('Value Node Types', () => {
})
it('should parse calc operators', () => {
- const parser = new Parser('body { width: calc(100% - 20px); }')
- const root = parser.parse()
+ const root = parse('body { width: calc(100% - 20px); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -481,8 +452,7 @@ describe('Value Node Types', () => {
})
it('should parse all calc operators', () => {
- const parser = new Parser('body { width: calc(1px + 2px * 3px / 4px - 5px); }')
- const root = parser.parse()
+ const root = parse('body { width: calc(1px + 2px * 3px / 4px - 5px); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -497,8 +467,7 @@ describe('Value Node Types', () => {
describe('PARENTHESIS', () => {
it('should parse parenthesized expressions in calc()', () => {
- const parser = new Parser('body { width: calc((100% - 50px) / 2); }')
- const root = parser.parse()
+ const root = parse('body { width: calc((100% - 50px) / 2); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -530,8 +499,7 @@ describe('Value Node Types', () => {
})
it('should parse complex nested parentheses', () => {
- const parser = new Parser('body { width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); }')
- const root = parser.parse()
+ const root = parse('body { width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -570,8 +538,7 @@ describe('Value Node Types', () => {
describe('URL', () => {
it('should parse url() function with quoted string', () => {
- const parser = new Parser('body { background: url("image.png"); }')
- const root = parser.parse()
+ const root = parse('body { background: url("image.png"); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(1)
@@ -583,8 +550,7 @@ describe('Value Node Types', () => {
})
it('should parse url() function with unquoted URL containing dots', () => {
- const parser = new Parser('body { cursor: url(mycursor.cur); }')
- const root = parser.parse()
+ const root = parse('body { cursor: url(mycursor.cur); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -598,8 +564,7 @@ describe('Value Node Types', () => {
})
it('should parse src() function with unquoted URL', () => {
- const parser = new Parser('body { content: src(myfont.woff2); }')
- const root = parser.parse()
+ const root = parse('body { content: src(myfont.woff2); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -611,8 +576,7 @@ describe('Value Node Types', () => {
})
it('should parse url() with base64 data URL', () => {
- const parser = new Parser('body { background: url(data:image/png;base64,iVBORw0KGg); }')
- const root = parser.parse()
+ const root = parse('body { background: url(data:image/png;base64,iVBORw0KGg); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -623,8 +587,7 @@ describe('Value Node Types', () => {
})
it('should parse url() with inline SVG', () => {
- const parser = new Parser('body { background: url(data:image/svg+xml,); }')
- const root = parser.parse()
+ const root = parse('body { background: url(data:image/svg+xml,); }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
const func = decl?.values[0]
@@ -635,8 +598,7 @@ describe('Value Node Types', () => {
})
it('should parse complex background value with url()', () => {
- const parser = new Parser('body { background: url("bg.png") no-repeat center center / cover; }')
- const root = parser.parse()
+ const root = parse('body { background: url("bg.png") no-repeat center center / cover; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values.length).toBeGreaterThan(1)
@@ -649,8 +611,7 @@ describe('Value Node Types', () => {
describe('Mixed values', () => {
it('should parse mixed value types', () => {
- const parser = new Parser('body { border: 1px solid red; }')
- const root = parser.parse()
+ const root = parse('body { border: 1px solid red; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.values).toHaveLength(3)
@@ -663,8 +624,7 @@ describe('Value Node Types', () => {
})
it('should handle empty value', () => {
- const parser = new Parser('body { color: ; }')
- const root = parser.parse()
+ const root = parse('body { color: ; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBeNull()
@@ -672,8 +632,7 @@ describe('Value Node Types', () => {
})
it('should handle value with !important', () => {
- const parser = new Parser('body { color: red !important; }')
- const root = parser.parse()
+ const root = parse('body { color: red !important; }')
const decl = root.first_child?.first_child?.next_sibling?.first_child
expect(decl?.value).toBe('red')
diff --git a/src/parse-value.ts b/src/parse-value.ts
index 998a714..5b9ed40 100644
--- a/src/parse-value.ts
+++ b/src/parse-value.ts
@@ -18,6 +18,7 @@ import {
import { is_whitespace, CHAR_MINUS_HYPHEN, CHAR_PLUS, CHAR_ASTERISK, CHAR_FORWARD_SLASH } from './string-utils'
import { CSSNode } from './css-node'
+/** @internal */
export class ValueParser {
private lexer: Lexer
private arena: CSSDataArena
diff --git a/src/parse.test.ts b/src/parse.test.ts
index afa5028..4795cbd 100644
--- a/src/parse.test.ts
+++ b/src/parse.test.ts
@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest'
-import { Parser, parse } from './parse'
+import { parse } from './parse'
import {
STYLESHEET,
STYLE_RULE,
@@ -277,26 +277,19 @@ describe('Core Nodes', () => {
describe('Node Properties', () => {
describe('STYLESHEET', () => {
test('empty stylesheet has no children', () => {
- const parser = new Parser('')
- const root = parser.parse()
+ const root = parse('')
+
expect(root.type).toBe(STYLESHEET)
expect(root.has_children).toBe(false)
})
test('stylesheet with only whitespace has no children', () => {
- const parser = new Parser(' \n\n ')
- const root = parser.parse()
+ const root = parse(' \n\n ')
+
expect(root.type).toBe(STYLESHEET)
expect(root.has_children).toBe(false)
})
- test('parser creates arena sized for source', () => {
- const source = 'body { color: red; }'
- const parser = new Parser(source)
- const arena = parser.get_arena()
- expect(arena.get_capacity()).toBeGreaterThan(0)
- expect(arena.get_count()).toBe(1) // Count starts at 1 (0 is reserved for "no node")
- })
})
describe('STYLE_RULE', () => {
@@ -642,8 +635,8 @@ describe('Core Nodes', () => {
describe('Selector parsing', () => {
test('should parse simple selector', () => {
const source = 'body { }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
expect(rule.has_children).toBe(true)
@@ -657,8 +650,8 @@ describe('Core Nodes', () => {
test('should parse complex selector', () => {
const source = 'div.class > p#id { }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const selectorlist = rule.first_child!
@@ -708,8 +701,8 @@ describe('Core Nodes', () => {
describe('Multiple rules', () => {
test('should parse multiple style rules', () => {
- const parser = new Parser('body { } div { }')
- const root = parser.parse()
+ const root = parse('body { } div { }')
+
const [rule1, rule2] = root.children
expect(rule1.type).toBe(STYLE_RULE)
@@ -723,8 +716,8 @@ describe('Core Nodes', () => {
describe('Statement at-rules (no block)', () => {
test('@import', () => {
const source = '@import url("style.css");'
- const parser = new Parser(source, { parse_atrule_preludes: false })
- const root = parser.parse()
+ const root = parse(source, { parse_atrule_preludes: false })
+
const atRule = root.first_child!
expect(atRule.type).toBe(AT_RULE)
@@ -734,8 +727,8 @@ describe('Core Nodes', () => {
test('@namespace', () => {
const source = '@namespace url(http://www.w3.org/1999/xhtml);'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const atRule = root.first_child!
expect(atRule.type).toBe(AT_RULE)
@@ -747,8 +740,8 @@ describe('Core Nodes', () => {
describe('Case-insensitive at-rule names', () => {
test('should parse @MEDIA (uppercase)', () => {
const source = '@MEDIA (min-width: 768px) { body { color: red; } }'
- const parser = new Parser(source, { parse_atrule_preludes: false })
- const root = parser.parse()
+ const root = parse(source, { parse_atrule_preludes: false })
+
const media = root.first_child!
expect(media.type).toBe(AT_RULE)
@@ -761,8 +754,8 @@ describe('Core Nodes', () => {
test('should parse @Font-Face (mixed case)', () => {
const source = '@Font-Face { font-family: "MyFont"; src: url("font.woff"); }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const fontFace = root.first_child!
expect(fontFace.type).toBe(AT_RULE)
@@ -776,8 +769,8 @@ describe('Core Nodes', () => {
test('should parse @SUPPORTS (uppercase)', () => {
const source = '@SUPPORTS (display: grid) { .grid { display: grid; } }'
- const parser = new Parser(source, { parse_atrule_preludes: false })
- const root = parser.parse()
+ const root = parse(source, { parse_atrule_preludes: false })
+
const supports = root.first_child!
expect(supports.type).toBe(AT_RULE)
@@ -789,8 +782,8 @@ describe('Core Nodes', () => {
describe('Block at-rules with nested rules', () => {
test('@media with nested rule', () => {
const source = '@media (min-width: 768px) { body { color: red; } }'
- const parser = new Parser(source, { parse_atrule_preludes: false })
- const root = parser.parse()
+ const root = parse(source, { parse_atrule_preludes: false })
+
const media = root.first_child!
expect(media.type).toBe(AT_RULE)
@@ -806,8 +799,8 @@ describe('Core Nodes', () => {
test('@layer with name', () => {
const source = '@layer utilities { .text-center { text-align: center; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const layer = root.first_child!
expect(layer.type).toBe(AT_RULE)
@@ -817,8 +810,8 @@ describe('Core Nodes', () => {
test('anonymous @layer', () => {
const source = '@layer { body { margin: 0; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const layer = root.first_child!
expect(layer.type).toBe(AT_RULE)
@@ -828,8 +821,8 @@ describe('Core Nodes', () => {
test('@supports', () => {
const source = '@supports (display: grid) { .grid { display: grid; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const supports = root.first_child!
expect(supports.type).toBe(AT_RULE)
@@ -839,8 +832,8 @@ describe('Core Nodes', () => {
test('@container', () => {
const source = '@container (min-width: 400px) { .card { padding: 2rem; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const container = root.first_child!
expect(container.type).toBe(AT_RULE)
@@ -852,8 +845,8 @@ describe('Core Nodes', () => {
describe('Descriptor at-rules (with declarations)', () => {
test('@font-face', () => {
const source = '@font-face { font-family: "Open Sans"; src: url(font.woff2); }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const fontFace = root.first_child!
expect(fontFace.type).toBe(AT_RULE)
@@ -868,8 +861,8 @@ describe('Core Nodes', () => {
test('@page', () => {
const source = '@page { margin: 1in; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const page = root.first_child!
expect(page.type).toBe(AT_RULE)
@@ -882,8 +875,8 @@ describe('Core Nodes', () => {
test('@counter-style', () => {
const source = '@counter-style thumbs { system: cyclic; symbols: "👍"; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const counterStyle = root.first_child!
expect(counterStyle.type).toBe(AT_RULE)
@@ -898,8 +891,8 @@ describe('Core Nodes', () => {
describe('Nested at-rules', () => {
test('@media inside @supports', () => {
const source = '@supports (display: grid) { @media (min-width: 768px) { body { color: red; } } }'
- const parser = new Parser(source, { parse_atrule_preludes: false })
- const root = parser.parse()
+ const root = parse(source, { parse_atrule_preludes: false })
+
const supports = root.first_child!
expect(supports.name).toBe('supports')
@@ -922,8 +915,8 @@ describe('Core Nodes', () => {
describe('Multiple at-rules', () => {
test('multiple at-rules at top level', () => {
const source = '@import url("a.css"); @layer base { body { margin: 0; } } @media print { body { color: black; } }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const [import1, layer, media] = root.children
expect(import1.name).toBe('import')
@@ -938,8 +931,8 @@ describe('Core Nodes', () => {
describe('Special at-rules', () => {
test('@charset', () => {
let source = '@charset "UTF-8"; body { color: red; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let [charset, _body] = root.children
expect(charset.type).toBe(AT_RULE)
@@ -948,8 +941,8 @@ describe('Core Nodes', () => {
test('@import with media query', () => {
let source = '@import url("print.css") print;'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let import_rule = root.first_child!
expect(import_rule.type).toBe(AT_RULE)
@@ -967,8 +960,8 @@ describe('Core Nodes', () => {
font-display: swap;
}
`
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let font_face = root.first_child!
expect(font_face.name).toBe('font-face')
@@ -978,8 +971,8 @@ describe('Core Nodes', () => {
test('@counter-style', () => {
let source = '@counter-style custom { system: cyclic; symbols: "⚫" "⚪"; suffix: " "; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let counter = root.first_child!
expect(counter.name).toBe('counter-style')
@@ -989,8 +982,8 @@ describe('Core Nodes', () => {
test('@property', () => {
let source = '@property --my-color { syntax: ""; inherits: false; initial-value: #c0ffee; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let property = root.first_child!
expect(property.name).toBe('property')
@@ -1000,8 +993,8 @@ describe('Core Nodes', () => {
describe('At-rule preludes', () => {
test('media query prelude', () => {
let source = '@media (min-width: 768px) { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.type).toBe(AT_RULE)
@@ -1011,8 +1004,8 @@ describe('Core Nodes', () => {
test('complex media query prelude', () => {
let source = '@media screen and (min-width: 768px) and (max-width: 1024px) { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('media')
@@ -1021,8 +1014,8 @@ describe('Core Nodes', () => {
test('container query prelude', () => {
let source = '@container (width >= 200px) { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('container')
@@ -1031,8 +1024,8 @@ describe('Core Nodes', () => {
test('supports query prelude', () => {
let source = '@supports (display: grid) { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('supports')
@@ -1041,8 +1034,8 @@ describe('Core Nodes', () => {
test('import prelude', () => {
let source = '@import url("styles.css");'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('import')
@@ -1051,8 +1044,8 @@ describe('Core Nodes', () => {
test('at-rule without prelude', () => {
let source = '@font-face { font-family: MyFont; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('font-face')
@@ -1061,8 +1054,8 @@ describe('Core Nodes', () => {
test('layer prelude', () => {
let source = '@layer utilities { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('layer')
@@ -1071,8 +1064,8 @@ describe('Core Nodes', () => {
test('keyframes prelude', () => {
let source = '@keyframes slide-in { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('keyframes')
@@ -1081,8 +1074,8 @@ describe('Core Nodes', () => {
test('prelude with extra whitespace', () => {
let source = '@media (min-width: 768px) { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('media')
@@ -1091,8 +1084,8 @@ describe('Core Nodes', () => {
test('charset prelude', () => {
let source = '@charset "UTF-8";'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('charset')
@@ -1101,8 +1094,8 @@ describe('Core Nodes', () => {
test('namespace prelude', () => {
let source = '@namespace svg url(http://www.w3.org/2000/svg);'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.name).toBe('namespace')
@@ -1111,8 +1104,8 @@ describe('Core Nodes', () => {
test('value and prelude should be aliases for at-rules', () => {
let source = '@media (min-width: 768px) { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let atrule = root.first_child!
expect(atrule.value).toBe(atrule.prelude)
@@ -1121,8 +1114,8 @@ describe('Core Nodes', () => {
test('at-rule prelude line tracking', () => {
let source = 'body { color: red; }\n\n@media screen { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let [_rule1, atRule] = root.children
expect(atRule.line).toBe(3)
@@ -1173,8 +1166,8 @@ describe('Core Nodes', () => {
describe('Basic declaration properties', () => {
test('should parse property name', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1185,8 +1178,8 @@ describe('Core Nodes', () => {
test('simple declaration without !important', () => {
const source = 'body { color: red; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1198,8 +1191,8 @@ describe('Core Nodes', () => {
test('declaration with !important', () => {
const source = 'body { color: red !important; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1211,8 +1204,8 @@ describe('Core Nodes', () => {
test('declaration with !ie (historic !important)', () => {
const source = 'body { color: red !ie; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1224,8 +1217,8 @@ describe('Core Nodes', () => {
test('declaration with ! followed by any identifier', () => {
const source = 'body { color: red !foo; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1237,8 +1230,8 @@ describe('Core Nodes', () => {
test('declaration without semicolon at end of block', () => {
const source = 'body { color: red }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1249,8 +1242,8 @@ describe('Core Nodes', () => {
test('complex declaration value', () => {
const source = 'body { background: url(image.png) no-repeat center; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1264,8 +1257,8 @@ describe('Core Nodes', () => {
describe('Multiple declarations', () => {
test('should parse multiple declarations', () => {
const source = 'body { color: red; margin: 0; }'
- const parser = new Parser(source)
- const root = parser.parse()
+ const root = parse(source)
+
const rule = root.first_child!
const [_selector, block] = rule.children
@@ -1280,8 +1273,8 @@ describe('Core Nodes', () => {
describe('Declaration values', () => {
test('extract simple value', () => {
let source = 'a { color: blue; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1293,8 +1286,8 @@ describe('Core Nodes', () => {
test('extract value with spaces', () => {
let source = 'a { padding: 1rem 2rem 3rem 4rem; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1306,8 +1299,8 @@ describe('Core Nodes', () => {
test('extract function value', () => {
let source = 'a { background: linear-gradient(to bottom, red, blue); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1319,8 +1312,8 @@ describe('Core Nodes', () => {
test('extract calc value', () => {
let source = 'a { width: calc(100% - 2rem); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1332,8 +1325,8 @@ describe('Core Nodes', () => {
test('exclude !important from value', () => {
let source = 'a { color: blue !important; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1346,8 +1339,8 @@ describe('Core Nodes', () => {
test('value with extra whitespace', () => {
let source = 'a { color: blue ; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1359,8 +1352,8 @@ describe('Core Nodes', () => {
test('CSS custom property value', () => {
let source = ':root { --brand-color: rgb(0% 10% 50% / 0.5); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1372,8 +1365,8 @@ describe('Core Nodes', () => {
test('var() reference value', () => {
let source = 'a { color: var(--primary-color); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1385,8 +1378,8 @@ describe('Core Nodes', () => {
test('nested function value', () => {
let source = 'a { transform: translate(calc(50% - 1rem), 0); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1398,8 +1391,8 @@ describe('Core Nodes', () => {
test('value without semicolon', () => {
let source = 'a { color: blue }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1411,8 +1404,8 @@ describe('Core Nodes', () => {
test('empty value', () => {
let source = 'a { color: ; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1424,8 +1417,8 @@ describe('Core Nodes', () => {
test('URL value', () => {
let source = 'a { background: url("image.png"); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1439,8 +1432,8 @@ describe('Core Nodes', () => {
describe('Vendor prefix detection', () => {
test('-webkit- vendor prefix', () => {
let source = '.box { -webkit-transform: scale(1); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1451,8 +1444,8 @@ describe('Core Nodes', () => {
test('-moz- vendor prefix', () => {
let source = '.box { -moz-transform: scale(1); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1463,8 +1456,8 @@ describe('Core Nodes', () => {
test('-ms- vendor prefix', () => {
let source = '.box { -ms-transform: scale(1); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1475,8 +1468,8 @@ describe('Core Nodes', () => {
test('-o- vendor prefix', () => {
let source = '.box { -o-transform: scale(1); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1487,8 +1480,8 @@ describe('Core Nodes', () => {
test('no vendor prefix for standard properties', () => {
let source = '.box { transform: scale(1); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1499,8 +1492,8 @@ describe('Core Nodes', () => {
test('no vendor prefix for properties with hyphens', () => {
let source = '.box { background-color: red; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1511,8 +1504,8 @@ describe('Core Nodes', () => {
test('no vendor prefix for custom properties', () => {
let source = ':root { --primary-color: blue; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1523,8 +1516,8 @@ describe('Core Nodes', () => {
test('multiple vendor-prefixed properties', () => {
let source = '.box { -webkit-transform: scale(1); -moz-transform: scale(1); transform: scale(1); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1542,8 +1535,8 @@ describe('Core Nodes', () => {
test('complex property names with vendor prefix', () => {
let source = '.box { -webkit-border-top-left-radius: 5px; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1554,8 +1547,8 @@ describe('Core Nodes', () => {
test('no vendor prefix for similar but non-vendor properties', () => {
let source = '.box { border-radius: 5px; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -1566,8 +1559,8 @@ describe('Core Nodes', () => {
test('false for nodes without names', () => {
let source = 'body { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selector = rule.first_child!
@@ -1578,8 +1571,8 @@ describe('Core Nodes', () => {
describe('Vendor prefix detection for selectors', () => {
test('-webkit- vendor prefix in pseudo-class', () => {
let source = 'input:-webkit-autofill { color: black; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1592,8 +1585,8 @@ describe('Core Nodes', () => {
test('-moz- vendor prefix in pseudo-class', () => {
let source = 'button:-moz-focusring { outline: 2px solid blue; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1606,8 +1599,8 @@ describe('Core Nodes', () => {
test('-ms- vendor prefix in pseudo-class', () => {
let source = 'input:-ms-input-placeholder { color: gray; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1620,8 +1613,8 @@ describe('Core Nodes', () => {
test('-webkit- vendor prefix in pseudo-element', () => {
let source = 'div::-webkit-scrollbar { width: 10px; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1634,8 +1627,8 @@ describe('Core Nodes', () => {
test('-moz- vendor prefix in pseudo-element', () => {
let source = 'div::-moz-selection { background: yellow; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1648,8 +1641,8 @@ describe('Core Nodes', () => {
test('-webkit- vendor prefix in pseudo-element with multiple parts', () => {
let source = 'input::-webkit-input-placeholder { color: gray; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1662,8 +1655,8 @@ describe('Core Nodes', () => {
test('-webkit- vendor prefix in pseudo-class function', () => {
let source = 'input:-webkit-any(input, button) { margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1676,8 +1669,8 @@ describe('Core Nodes', () => {
test('no vendor prefix for standard pseudo-classes', () => {
let source = 'a:hover { color: blue; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1690,8 +1683,8 @@ describe('Core Nodes', () => {
test('no vendor prefix for standard pseudo-elements', () => {
let source = 'div::before { content: ""; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1704,8 +1697,8 @@ describe('Core Nodes', () => {
test('multiple vendor-prefixed pseudo-elements', () => {
let source = 'div::-webkit-scrollbar { } div::-webkit-scrollbar-thumb { } div::after { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let [rule1, rule2, rule3] = root.children
@@ -1733,8 +1726,8 @@ describe('Core Nodes', () => {
test('vendor prefix in complex selector', () => {
let source = 'input:-webkit-autofill:focus { color: black; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selectorList = rule.first_child!
@@ -1753,8 +1746,8 @@ describe('Core Nodes', () => {
describe('BLOCK', () => {
test('block text excludes braces for empty at-rule block', () => {
- const parser = new Parser('@layer test {}')
- const root = parser.parse()
+ const root = parse('@layer test {}')
+
const atRule = root.first_child!
expect(atRule.has_block).toBe(true)
@@ -1763,8 +1756,8 @@ describe('Core Nodes', () => {
})
test('at-rule block with content excludes braces', () => {
- const parser = new Parser('@layer test { .foo { color: red; } }')
- const root = parser.parse()
+ const root = parse('@layer test { .foo { color: red; } }')
+
const atRule = root.first_child!
expect(atRule.has_block).toBe(true)
@@ -1773,8 +1766,8 @@ describe('Core Nodes', () => {
})
test('empty style rule block has empty text', () => {
- const parser = new Parser('body {}')
- const root = parser.parse()
+ const root = parse('body {}')
+
const styleRule = root.first_child!
expect(styleRule.has_block).toBe(true)
@@ -1783,8 +1776,8 @@ describe('Core Nodes', () => {
})
test('style rule block with declaration excludes braces', () => {
- const parser = new Parser('body { color: red; }')
- const root = parser.parse()
+ const root = parse('body { color: red; }')
+
const styleRule = root.first_child!
expect(styleRule.has_block).toBe(true)
@@ -1793,8 +1786,8 @@ describe('Core Nodes', () => {
})
test('nested style rule blocks exclude braces', () => {
- const parser = new Parser('.parent { .child { margin: 0; } }')
- const root = parser.parse()
+ const root = parse('.parent { .child { margin: 0; } }')
+
const parent = root.first_child!
const parentBlock = parent.block!
const child = parentBlock.first_child!
@@ -1805,32 +1798,32 @@ describe('Core Nodes', () => {
})
test('at-rule with multiple declarations excludes braces', () => {
- const parser = new Parser('@font-face { font-family: "Test"; src: url(test.woff); }')
- const root = parser.parse()
+ const root = parse('@font-face { font-family: "Test"; src: url(test.woff); }')
+
const atRule = root.first_child!
expect(atRule.block!.text).toBe(' font-family: "Test"; src: url(test.woff); ')
})
test('media query with nested rules excludes braces', () => {
- const parser = new Parser('@media screen { body { color: blue; } }')
- const root = parser.parse()
+ const root = parse('@media screen { body { color: blue; } }')
+
const mediaRule = root.first_child!
expect(mediaRule.block!.text).toBe(' body { color: blue; } ')
})
test('block with no whitespace is empty', () => {
- const parser = new Parser('div{}')
- const root = parser.parse()
+ const root = parse('div{}')
+
const styleRule = root.first_child!
expect(styleRule.block!.text).toBe('')
})
test('block with only whitespace preserves whitespace', () => {
- const parser = new Parser('div{ \n\t }')
- const root = parser.parse()
+ const root = parse('div{ \n\t }')
+
const styleRule = root.first_child!
expect(styleRule.block!.text).toBe(' \n\t ')
@@ -1840,8 +1833,8 @@ describe('Core Nodes', () => {
describe('CSS Nesting', () => {
test('nested rule with & selector', () => {
let source = '.parent { color: red; & .child { color: blue; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
expect(parent.type).toBe(STYLE_RULE)
@@ -1858,8 +1851,8 @@ describe('Core Nodes', () => {
test('nested rule without & selector', () => {
let source = '.parent { color: red; .child { color: blue; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -1872,8 +1865,8 @@ describe('Core Nodes', () => {
test('multiple nested rules', () => {
let source = '.parent { .child1 { } .child2 { } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -1885,8 +1878,8 @@ describe('Core Nodes', () => {
test('deeply nested rules', () => {
let source = '.a { .b { .c { color: red; } } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let a = root.first_child!
expect(a.length).toBe(32)
@@ -1908,8 +1901,8 @@ describe('Core Nodes', () => {
test('nested @media inside rule', () => {
let source = '.card { color: red; @media (min-width: 768px) { padding: 2rem; } }'
- let parser = new Parser(source, { parse_atrule_preludes: false })
- let root = parser.parse()
+ let root = parse(source, { parse_atrule_preludes: false })
+
let card = root.first_child!
let [_selector, block] = card.children
@@ -1927,8 +1920,8 @@ describe('Core Nodes', () => {
test(':is() pseudo-class', () => {
let source = ':is(.a, .b) { color: red; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selector = rule.first_child!
@@ -1937,8 +1930,8 @@ describe('Core Nodes', () => {
test(':where() pseudo-class', () => {
let source = ':where(h1, h2, h3) { margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selector = rule.first_child!
@@ -1947,8 +1940,8 @@ describe('Core Nodes', () => {
test(':has() pseudo-class', () => {
let source = 'div:has(> img) { display: flex; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selector = rule.first_child!
@@ -1962,8 +1955,8 @@ describe('Core Nodes', () => {
padding: 1rem;
.body { line-height: 1.5; }
}`
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let card = root.first_child!
let [_selector, block] = card.children
@@ -1983,8 +1976,8 @@ describe('Core Nodes', () => {
describe('Relaxed nesting (CSS Nesting Module Level 1)', () => {
test('nested rule with leading child combinator', () => {
let source = '.parent { > a { color: red; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
expect(parent.type).toBe(STYLE_RULE)
@@ -2000,8 +1993,8 @@ describe('Core Nodes', () => {
test('nested rule with leading next-sibling combinator', () => {
let source = '.parent { + span { color: blue; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -2015,8 +2008,8 @@ describe('Core Nodes', () => {
test('nested rule with leading subsequent-sibling combinator', () => {
let source = '.parent { ~ div { color: green; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -2030,8 +2023,8 @@ describe('Core Nodes', () => {
test('multiple nested rules with different leading combinators', () => {
let source = '.parent { > a { color: red; } ~ span { color: blue; } + div { color: green; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -2052,8 +2045,8 @@ describe('Core Nodes', () => {
test('complex selector after leading combinator', () => {
let source = '.parent { > a.link#nav[href]:hover { color: red; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -2066,8 +2059,8 @@ describe('Core Nodes', () => {
test('deeply nested rules with leading combinators', () => {
let source = '.a { > .b { > .c { color: red; } } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let a = root.first_child!
let [_selector_a, block_a] = a.children
@@ -2085,8 +2078,8 @@ describe('Core Nodes', () => {
test('mixed nested rules with and without leading combinators', () => {
let source = '.parent { .normal { } > .combinator { } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -2105,8 +2098,8 @@ describe('Core Nodes', () => {
describe('@keyframes parsing', () => {
test('@keyframes with from/to', () => {
let source = '@keyframes fade { from { opacity: 0; } to { opacity: 1; } }'
- let parser = new Parser(source, { parse_atrule_preludes: false })
- let root = parser.parse()
+ let root = parse(source, { parse_atrule_preludes: false })
+
let keyframes = root.first_child!
expect(keyframes.type).toBe(AT_RULE)
@@ -2126,8 +2119,8 @@ describe('Core Nodes', () => {
test('@keyframes with percentages', () => {
let source = '@keyframes slide { 0% { left: 0; } 50% { left: 50%; } 100% { left: 100%; } }'
- let parser = new Parser(source, { parse_atrule_preludes: false })
- let root = parser.parse()
+ let root = parse(source, { parse_atrule_preludes: false })
+
let keyframes = root.first_child!
let block = keyframes.block!
@@ -2143,8 +2136,8 @@ describe('Core Nodes', () => {
test('@keyframes with multiple selectors', () => {
let source = '@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }'
- let parser = new Parser(source, { parse_atrule_preludes: false })
- let root = parser.parse()
+ let root = parse(source, { parse_atrule_preludes: false })
+
let keyframes = root.first_child!
let block = keyframes.block!
@@ -2156,8 +2149,8 @@ describe('Core Nodes', () => {
test('@keyframes with mixed percentages and keywords', () => {
let source = '@keyframes slide { from { left: 0; } 25%, 75% { left: 50%; } to { left: 100%; } }'
- let parser = new Parser(source, { parse_atrule_preludes: false })
- let root = parser.parse()
+ let root = parse(source, { parse_atrule_preludes: false })
+
let keyframes = root.first_child!
let block = keyframes.block!
@@ -2168,8 +2161,8 @@ describe('Core Nodes', () => {
describe('@nest at-rule', () => {
test('@nest with & selector', () => {
let source = '.parent { @nest & .child { color: blue; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let parent = root.first_child!
let [_selector, block] = parent.children
@@ -2187,8 +2180,8 @@ describe('Core Nodes', () => {
test('@nest with complex selector', () => {
let source = '.a { @nest :not(&) { color: red; } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let a = root.first_child!
let [_selector, block] = a.children
@@ -2202,24 +2195,24 @@ describe('Core Nodes', () => {
describe('Error recovery and edge cases', () => {
test('malformed rule without opening brace', () => {
let source = 'body color: red; } div { margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
expect(root.children.length).toBeGreaterThan(0)
})
test('rule without closing brace', () => {
let source = 'body { color: red; div { margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
expect(root.has_children).toBe(true)
})
test('empty rule block', () => {
let source = '.empty { }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -2228,8 +2221,8 @@ describe('Core Nodes', () => {
test('declaration without value', () => {
let source = 'body { color: }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -2239,8 +2232,8 @@ describe('Core Nodes', () => {
test('multiple semicolons', () => {
let source = 'body { color: red;;; margin: 0;; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.children.length).toBe(2)
@@ -2248,8 +2241,8 @@ describe('Core Nodes', () => {
test('invalid tokens in declaration block', () => {
let source = 'body { color: red; @@@; margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.children.length).toBe(2)
@@ -2257,8 +2250,8 @@ describe('Core Nodes', () => {
test('declaration without colon', () => {
let source = 'body { color red; margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.children.length).toBe(2)
@@ -2266,16 +2259,16 @@ describe('Core Nodes', () => {
test('at-rule without name', () => {
let source = '@ { color: red; } body { margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
expect(root.children.length).toBeGreaterThan(0)
})
test('nested empty blocks', () => {
let source = '.a { .b { .c { } } }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let a = root.first_child!
expect(a.type).toBe(STYLE_RULE)
@@ -2283,8 +2276,8 @@ describe('Core Nodes', () => {
test('trailing comma in selector', () => {
let source = '.a, .b, { color: red; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -2294,8 +2287,8 @@ describe('Core Nodes', () => {
describe('Comment handling', () => {
test('skip comments at top level', () => {
let source = '/* comment */ body { color: red; } /* another comment */'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
expect(root.children.length).toBe(1)
let rule = root.first_child!
@@ -2304,8 +2297,8 @@ describe('Core Nodes', () => {
test('skip comments in declaration block', () => {
let source = 'body { color: red; /* comment */ margin: 0; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -2314,8 +2307,8 @@ describe('Core Nodes', () => {
test('skip comments in selector', () => {
let source = 'body /* comment */ , /* comment */ div { color: red; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -2323,8 +2316,8 @@ describe('Core Nodes', () => {
test('comment between property and colon', () => {
let source = 'body { color /* comment */ : red; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
expect(root.has_children).toBe(true)
})
@@ -2337,8 +2330,8 @@ describe('Core Nodes', () => {
*/
body { color: red; }
`
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
expect(root.children.length).toBe(1)
})
@@ -2347,8 +2340,8 @@ describe('Core Nodes', () => {
describe('Whitespace handling', () => {
test('excessive whitespace', () => {
let source = ' body { color : red ; } '
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -2356,8 +2349,8 @@ describe('Core Nodes', () => {
test('tabs and newlines', () => {
let source = 'body\t{\n\tcolor:\tred;\n}\n'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
expect(rule.type).toBe(STYLE_RULE)
@@ -2365,8 +2358,8 @@ describe('Core Nodes', () => {
test('no whitespace', () => {
let source = 'body{color:red;margin:0}'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -2405,8 +2398,8 @@ describe('Core Nodes', () => {
}
}
`
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let card = root.first_child!
expect(card.type).toBe(STYLE_RULE)
@@ -2429,8 +2422,8 @@ describe('Core Nodes', () => {
}
}
`
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let [layer1, layer2] = root.children
expect(layer1.type).toBe(AT_RULE)
@@ -2439,8 +2432,8 @@ describe('Core Nodes', () => {
test('vendor prefixed properties', () => {
let source = '.box { -webkit-transform: scale(1); -moz-transform: scale(1); transform: scale(1); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -2452,8 +2445,8 @@ describe('Core Nodes', () => {
test('complex selector list', () => {
let source = 'h1, h2, h3, h4, h5, h6, .heading, [role="heading"] { font-family: sans-serif; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selector = rule.first_child!
@@ -2471,8 +2464,8 @@ describe('Core Nodes', () => {
}
}
`
- let parser = new Parser(source, { parse_atrule_preludes: false })
- let root = parser.parse()
+ let root = parse(source, { parse_atrule_preludes: false })
+
let supports = root.first_child!
let supports_block = supports.block!
@@ -2486,8 +2479,8 @@ describe('Core Nodes', () => {
test('CSS with calc() and other functions', () => {
let source = '.box { width: calc(100% - 2rem); background: linear-gradient(to right, red, blue); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let [_selector, block] = rule.children
@@ -2498,8 +2491,8 @@ describe('Core Nodes', () => {
test('custom properties', () => {
let source = ':root { --primary-color: #007bff; --spacing: 1rem; } body { color: var(--primary-color); }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
expect(root.children.length).toBeGreaterThan(0)
let first_rule = root.first_child!
@@ -2508,8 +2501,8 @@ describe('Core Nodes', () => {
test('attribute selectors with operators', () => {
let source = '[href^="https"][href$=".pdf"][class*="doc"] { color: red; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let selector = rule.first_child!
@@ -2520,8 +2513,8 @@ describe('Core Nodes', () => {
test('pseudo-elements', () => {
let source = '.text::before { content: "→"; } .text::after { content: "←"; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let [rule1, rule2] = root.children
expect(rule1.type).toBe(STYLE_RULE)
@@ -2530,8 +2523,8 @@ describe('Core Nodes', () => {
test('multiple !important declarations', () => {
let source = '.override { color: red !important; margin: 0 !important; padding: 0 !ie; }'
- let parser = new Parser(source)
- let root = parser.parse()
+ let root = parse(source)
+
let rule = root.first_child!
let block = rule.block!
diff --git a/src/parse.ts b/src/parse.ts
index 35c38b1..f68966d 100644
--- a/src/parse.ts
+++ b/src/parse.ts
@@ -41,6 +41,7 @@ export interface ParserOptions {
let DECLARATION_AT_RULES = new Set(['font-face', 'font-feature-values', 'page', 'property', 'counter-style'])
let CONDITIONAL_AT_RULES = new Set(['media', 'supports', 'container', 'layer', 'nest'])
+/** @internal */
export class Parser {
private source: string
private lexer: Lexer
diff --git a/src/string-utils.ts b/src/string-utils.ts
index e156cc4..4d85ac1 100644
--- a/src/string-utils.ts
+++ b/src/string-utils.ts
@@ -24,15 +24,18 @@ export const CHAR_COLON = 0x3a // :
/**
* Check if a character code is whitespace (space, tab, newline, CR, or FF)
+ * @internal
*/
export function is_whitespace(ch: number): boolean {
return ch === CHAR_SPACE || ch === CHAR_TAB || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED
}
+/** @internal */
export function is_combinator(ch: number): boolean {
return ch === CHAR_GREATER_THAN || ch === CHAR_PLUS || ch === CHAR_TILDE
}
+/** @internal */
export function is_digit(ch: number): boolean {
return ch >= 0x30 && ch <= 0x39 // 0-9
}
diff --git a/src/walk.test.ts b/src/walk.test.ts
index 6a4ef87..f069294 100644
--- a/src/walk.test.ts
+++ b/src/walk.test.ts
@@ -1,12 +1,11 @@
import { describe, it, expect } from 'vitest'
-import { Parser } from './parse'
+import { parse } from './parse'
import { STYLESHEET, STYLE_RULE, SELECTOR_LIST, DECLARATION, AT_RULE, BLOCK, IDENTIFIER, NUMBER, DIMENSION } from './constants'
import { walk, traverse } from './walk'
describe('walk', () => {
it('should visit single node', () => {
- const parser = new Parser('', { parse_selectors: false, parse_values: false })
- const root = parser.parse()
+ const root = parse('', { parse_selectors: false, parse_values: false })
const visited: number[] = []
walk(root, (node) => {
@@ -17,8 +16,7 @@ describe('walk', () => {
})
it('should visit all nodes in simple rule', () => {
- const parser = new Parser('body { color: red; }', { parse_selectors: false, parse_values: true })
- const root = parser.parse()
+ const root = parse('body { color: red; }', { parse_selectors: false, parse_values: true })
const visited: number[] = []
walk(root, (node) => {
@@ -36,8 +34,7 @@ describe('walk', () => {
})
it('should visit nodes in depth-first order', () => {
- const parser = new Parser('body { color: red; margin: 0; } div { padding: 1rem; }', { parse_selectors: false, parse_values: true })
- const root = parser.parse()
+ const root = parse('body { color: red; margin: 0; } div { padding: 1rem; }', { parse_selectors: false, parse_values: true })
const visited: number[] = []
walk(root, (node) => {
@@ -62,8 +59,7 @@ describe('walk', () => {
})
it('should visit nested rules', () => {
- const parser = new Parser('.parent { color: red; .child { color: blue; } }', { parse_selectors: false, parse_values: false })
- const root = parser.parse()
+ const root = parse('.parent { color: red; .child { color: blue; } }', { parse_selectors: false, parse_values: false })
const visited: number[] = []
walk(root, (node) => {
@@ -84,12 +80,11 @@ describe('walk', () => {
})
it('should visit at-rule nodes', () => {
- const parser = new Parser('@media (min-width: 768px) { body { color: red; } }', {
+ const root = parse('@media (min-width: 768px) { body { color: red; } }', {
parse_selectors: false,
parse_values: false,
parse_atrule_preludes: false,
})
- const root = parser.parse()
const visited: number[] = []
walk(root, (node) => {
@@ -108,8 +103,7 @@ describe('walk', () => {
})
it('should allow collecting node data', () => {
- const parser = new Parser('body { color: red; } .btn { margin: 0; }', { parse_selectors: false, parse_values: false })
- const root = parser.parse()
+ const root = parse('body { color: red; } .btn { margin: 0; }', { parse_selectors: false, parse_values: false })
const selectors: string[] = []
walk(root, (node) => {
@@ -122,8 +116,7 @@ describe('walk', () => {
})
it('should allow collecting property names', () => {
- const parser = new Parser('body { color: red; margin: 0; padding: 1rem; }', { parse_selectors: false, parse_values: false })
- const root = parser.parse()
+ const root = parse('body { color: red; margin: 0; padding: 1rem; }', { parse_selectors: false, parse_values: false })
const properties: string[] = []
walk(root, (node) => {
@@ -137,14 +130,13 @@ describe('walk', () => {
})
it('should allow counting nodes by type', () => {
- const parser = new Parser(
+ const root = parse(
`
body { color: red; }
.card { padding: 1rem; }
@media screen { div { margin: 0; } }
`,
)
- const root = parser.parse()
const counts: Record = {}
walk(root, (node) => {
@@ -159,8 +151,7 @@ describe('walk', () => {
})
it('should work with deeply nested structures', () => {
- const parser = new Parser('.a { .b { .c { color: red; } } }')
- const root = parser.parse()
+ const root = parse('.a { .b { .c { color: red; } } }')
const rules: number[] = []
walk(root, (node) => {
@@ -173,8 +164,7 @@ describe('walk', () => {
})
it('should track depth correctly', () => {
- const parser = new Parser('body { color: red; }', { parse_selectors: false, parse_values: true })
- const root = parser.parse()
+ const root = parse('body { color: red; }', { parse_selectors: false, parse_values: true })
const depths: number[] = []
walk(root, (_node, depth) => {
@@ -186,8 +176,7 @@ describe('walk', () => {
})
it('should track depth in nested structures', () => {
- const parser = new Parser('.a { .b { .c { color: red; } } }', { parse_selectors: false, parse_values: true })
- const root = parser.parse()
+ const root = parse('.a { .b { .c { color: red; } } }', { parse_selectors: false, parse_values: true })
const ruleDepths: number[] = []
walk(root, (node, depth) => {
@@ -200,12 +189,11 @@ describe('walk', () => {
})
it('should track depth with at-rules', () => {
- const parser = new Parser('@media screen { body { color: red; } }', {
+ const root = parse('@media screen { body { color: red; } }', {
parse_selectors: false,
parse_values: false,
parse_atrule_preludes: false,
})
- const root = parser.parse()
const typeAndDepth: Array<{ type: number; depth: number }> = []
walk(root, (node, depth) => {
@@ -224,12 +212,11 @@ describe('walk', () => {
})
it('should track depth with consecutive at-rules', () => {
- const parser = new Parser('@media screen { body { color: red; } } @layer { a { color: red; } }', {
+ const root = parse('@media screen { body { color: red; } } @layer { a { color: red; } }', {
parse_selectors: false,
parse_values: false,
parse_atrule_preludes: false,
})
- const root = parser.parse()
const typeAndDepth: Array<{ type: number; depth: number }> = []
walk(root, (node, depth) => {
@@ -254,7 +241,7 @@ describe('walk', () => {
})
test('export types', () => {
- let ast = new Parser('a{}').parse()
+ let ast = parse('a{}')
walk(ast, (node, _depth) => {
expectTypeOf(node.type).toBeNumber()
if (node.type === SELECTOR_LIST) {
@@ -265,12 +252,11 @@ describe('walk', () => {
})
describe('walk enter/leave', () => {
- const parser = new Parser('@media screen { body { color: red; } }', {
+ const root = parse('@media screen { body { color: red; } }', {
parse_selectors: false,
parse_values: false,
parse_atrule_preludes: false,
})
- const root = parser.parse()
test('both enter + leave', () => {
const enter: number[] = []
diff --git a/tsconfig.build.json b/tsconfig.build.json
index b9aa892..9d25229 100644
--- a/tsconfig.build.json
+++ b/tsconfig.build.json
@@ -5,8 +5,15 @@
"emitDeclarationOnly": true,
"noEmit": false,
"outDir": "./dist",
- "rootDir": "./src"
+ "rootDir": "./src",
+ "stripInternal": true
},
"include": ["src/**/*.ts"],
- "exclude": ["node_modules", "dist", "**/*.test.ts", "benchmark"]
+ "exclude": [
+ "node_modules",
+ "dist",
+ "**/*.test.ts",
+ "benchmark",
+ "src/char-types.ts"
+ ]
}