@projectwallace/css-parser 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ import { CSSDataArena } from './arena';
2
+ import { CSSNode } from './css-node';
3
+ export declare class ValueParser {
4
+ private lexer;
5
+ private arena;
6
+ private source;
7
+ private value_end;
8
+ constructor(arena: CSSDataArena, source: string);
9
+ parse_value(start: number, end: number): number[];
10
+ private is_whitespace_token;
11
+ private parse_value_node;
12
+ private create_node;
13
+ private create_operator_node;
14
+ private parse_operator_node;
15
+ private parse_function_node;
16
+ }
17
+ /**
18
+ * Parse a CSS declaration value string and return an array of value AST nodes
19
+ * @param value_string - The CSS value to parse (e.g., "1px solid red")
20
+ * @returns An array of CSSNode objects representing the parsed value
21
+ */
22
+ export declare function parse_value(value_string: string): CSSNode[];
@@ -0,0 +1,142 @@
1
+ import { L as Lexer, y as TOKEN_EOF, q as TOKEN_COMMA, h as TOKEN_DELIM, a as TOKEN_FUNCTION, c as TOKEN_HASH, d as TOKEN_STRING, k as TOKEN_DIMENSION, j as TOKEN_PERCENTAGE, i as TOKEN_NUMBER, T as TOKEN_IDENT, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN } from './lexer-CtBKgfVv.js';
2
+ import { T as CSSDataArena, C as CSSNode, a5 as is_whitespace, r as NODE_VALUE_OPERATOR, p as NODE_VALUE_COLOR, o as NODE_VALUE_STRING, n as NODE_VALUE_DIMENSION, m as NODE_VALUE_NUMBER, l as NODE_VALUE_KEYWORD, a0 as CHAR_PLUS, ai as CHAR_MINUS_HYPHEN, a3 as CHAR_ASTERISK, aj as CHAR_FORWARD_SLASH, q as NODE_VALUE_FUNCTION } from './css-node-BzCSxoLM.js';
3
+
4
+ class ValueParser {
5
+ lexer;
6
+ arena;
7
+ source;
8
+ value_end;
9
+ constructor(arena, source) {
10
+ this.arena = arena;
11
+ this.source = source;
12
+ this.lexer = new Lexer(source, false);
13
+ this.value_end = 0;
14
+ }
15
+ // Parse a declaration value range into value nodes
16
+ // Returns array of value node indices
17
+ parse_value(start, end) {
18
+ this.value_end = end;
19
+ this.lexer.pos = start;
20
+ this.lexer.line = 1;
21
+ let nodes = [];
22
+ while (this.lexer.pos < this.value_end) {
23
+ this.lexer.next_token_fast(false);
24
+ if (this.lexer.token_start >= this.value_end) break;
25
+ let token_type = this.lexer.token_type;
26
+ if (token_type === TOKEN_EOF) break;
27
+ if (this.is_whitespace_token()) {
28
+ continue;
29
+ }
30
+ let node = this.parse_value_node();
31
+ if (node !== null) {
32
+ nodes.push(node);
33
+ }
34
+ }
35
+ return nodes;
36
+ }
37
+ is_whitespace_token() {
38
+ let start = this.lexer.token_start;
39
+ let end = this.lexer.token_end;
40
+ if (start >= end) return false;
41
+ for (let i = start; i < end; i++) {
42
+ let ch = this.source.charCodeAt(i);
43
+ if (!is_whitespace(ch)) {
44
+ return false;
45
+ }
46
+ }
47
+ return true;
48
+ }
49
+ parse_value_node() {
50
+ let token_type = this.lexer.token_type;
51
+ let start = this.lexer.token_start;
52
+ let end = this.lexer.token_end;
53
+ switch (token_type) {
54
+ case TOKEN_IDENT:
55
+ return this.create_node(NODE_VALUE_KEYWORD, start, end);
56
+ case TOKEN_NUMBER:
57
+ return this.create_node(NODE_VALUE_NUMBER, start, end);
58
+ case TOKEN_PERCENTAGE:
59
+ case TOKEN_DIMENSION:
60
+ return this.create_node(NODE_VALUE_DIMENSION, start, end);
61
+ case TOKEN_STRING:
62
+ return this.create_node(NODE_VALUE_STRING, start, end);
63
+ case TOKEN_HASH:
64
+ return this.create_node(NODE_VALUE_COLOR, start, end);
65
+ case TOKEN_FUNCTION:
66
+ return this.parse_function_node(start, end);
67
+ case TOKEN_DELIM:
68
+ return this.parse_operator_node(start, end);
69
+ case TOKEN_COMMA:
70
+ return this.create_node(NODE_VALUE_OPERATOR, start, end);
71
+ default:
72
+ return null;
73
+ }
74
+ }
75
+ create_node(node_type, start, end) {
76
+ let node = this.arena.create_node();
77
+ this.arena.set_type(node, node_type);
78
+ this.arena.set_start_offset(node, start);
79
+ this.arena.set_length(node, end - start);
80
+ this.arena.set_content_start(node, start);
81
+ this.arena.set_content_length(node, end - start);
82
+ return node;
83
+ }
84
+ create_operator_node(start, end) {
85
+ return this.create_node(NODE_VALUE_OPERATOR, start, end);
86
+ }
87
+ parse_operator_node(start, end) {
88
+ let ch = this.source.charCodeAt(start);
89
+ if (ch === CHAR_PLUS || ch === CHAR_MINUS_HYPHEN || ch === CHAR_ASTERISK || ch === CHAR_FORWARD_SLASH) {
90
+ return this.create_operator_node(start, end);
91
+ }
92
+ return null;
93
+ }
94
+ parse_function_node(start, end) {
95
+ let node = this.arena.create_node();
96
+ this.arena.set_type(node, NODE_VALUE_FUNCTION);
97
+ this.arena.set_start_offset(node, start);
98
+ let name_end = end - 1;
99
+ this.arena.set_content_start(node, start);
100
+ this.arena.set_content_length(node, name_end - start);
101
+ let args = [];
102
+ let paren_depth = 1;
103
+ let func_end = end;
104
+ while (this.lexer.pos < this.value_end && paren_depth > 0) {
105
+ this.lexer.next_token_fast(false);
106
+ let token_type = this.lexer.token_type;
107
+ if (token_type === TOKEN_EOF) break;
108
+ if (this.lexer.token_start >= this.value_end) break;
109
+ if (token_type === TOKEN_LEFT_PAREN || token_type === TOKEN_FUNCTION) {
110
+ paren_depth++;
111
+ } else if (token_type === TOKEN_RIGHT_PAREN) {
112
+ paren_depth--;
113
+ if (paren_depth === 0) {
114
+ func_end = this.lexer.token_end;
115
+ break;
116
+ }
117
+ }
118
+ if (this.is_whitespace_token()) continue;
119
+ let arg_node = this.parse_value_node();
120
+ if (arg_node !== null) {
121
+ args.push(arg_node);
122
+ }
123
+ }
124
+ this.arena.set_length(node, func_end - start);
125
+ if (args.length > 0) {
126
+ this.arena.set_first_child(node, args[0]);
127
+ this.arena.set_last_child(node, args[args.length - 1]);
128
+ for (let i = 0; i < args.length - 1; i++) {
129
+ this.arena.set_next_sibling(args[i], args[i + 1]);
130
+ }
131
+ }
132
+ return node;
133
+ }
134
+ }
135
+ function parse_value(value_string) {
136
+ const arena = new CSSDataArena(CSSDataArena.capacity_for_source(value_string.length));
137
+ const value_parser = new ValueParser(arena, value_string);
138
+ const node_indices = value_parser.parse_value(0, value_string.length);
139
+ return node_indices.map((index) => new CSSNode(arena, value_string, index));
140
+ }
141
+
142
+ export { ValueParser, parse_value };
package/dist/parse.d.ts CHANGED
@@ -1,5 +1,36 @@
1
- import type { ParserOptions } from './parser';
2
- import type { CSSNode } from './css-node';
1
+ import { CSSDataArena } from './arena';
2
+ import { CSSNode } from './css-node';
3
+ export interface ParserOptions {
4
+ skip_comments?: boolean;
5
+ parse_values?: boolean;
6
+ parse_selectors?: boolean;
7
+ parse_atrule_preludes?: boolean;
8
+ }
9
+ export declare class Parser {
10
+ private source;
11
+ private lexer;
12
+ private arena;
13
+ private value_parser;
14
+ private selector_parser;
15
+ private prelude_parser;
16
+ private parse_values_enabled;
17
+ private parse_selectors_enabled;
18
+ private parse_atrule_preludes_enabled;
19
+ constructor(source: string, options?: ParserOptions);
20
+ get_arena(): CSSDataArena;
21
+ get_source(): string;
22
+ private next_token;
23
+ private peek_type;
24
+ private is_eof;
25
+ parse(): CSSNode;
26
+ private parse_rule;
27
+ private parse_style_rule;
28
+ private parse_selector;
29
+ private parse_declaration;
30
+ private parse_atrule;
31
+ private atrule_has_declarations;
32
+ private atrule_is_conditional;
33
+ }
3
34
  /**
4
35
  * Parse CSS and return an AST
5
36
  * @param source - The CSS source code to parse
@@ -7,3 +38,4 @@ import type { CSSNode } from './css-node';
7
38
  * @returns The root CSSNode of the AST
8
39
  */
9
40
  export declare function parse(source: string, options?: ParserOptions): CSSNode;
41
+ export { NODE_STYLESHEET, NODE_STYLE_RULE, NODE_AT_RULE, NODE_DECLARATION, NODE_SELECTOR, NODE_COMMENT, NODE_BLOCK, NODE_VALUE_KEYWORD, NODE_VALUE_NUMBER, NODE_VALUE_DIMENSION, NODE_VALUE_STRING, NODE_VALUE_COLOR, NODE_VALUE_FUNCTION, NODE_VALUE_OPERATOR, NODE_SELECTOR_LIST, NODE_SELECTOR_TYPE, NODE_SELECTOR_CLASS, NODE_SELECTOR_ID, NODE_SELECTOR_ATTRIBUTE, NODE_SELECTOR_PSEUDO_CLASS, NODE_SELECTOR_PSEUDO_ELEMENT, NODE_SELECTOR_COMBINATOR, NODE_SELECTOR_UNIVERSAL, NODE_SELECTOR_NESTING, NODE_SELECTOR_NTH, NODE_SELECTOR_NTH_OF, NODE_SELECTOR_LANG, NODE_PRELUDE_MEDIA_QUERY, NODE_PRELUDE_MEDIA_FEATURE, NODE_PRELUDE_MEDIA_TYPE, NODE_PRELUDE_CONTAINER_QUERY, NODE_PRELUDE_SUPPORTS_QUERY, NODE_PRELUDE_LAYER_NAME, NODE_PRELUDE_IDENTIFIER, NODE_PRELUDE_OPERATOR, NODE_PRELUDE_IMPORT_URL, NODE_PRELUDE_IMPORT_LAYER, NODE_PRELUDE_IMPORT_SUPPORTS, FLAG_IMPORTANT, } from './arena';
package/dist/parse.js CHANGED
@@ -1,185 +1,9 @@
1
- import { L as Lexer, y as TOKEN_EOF, q as TOKEN_COMMA, h as TOKEN_DELIM, a as TOKEN_FUNCTION, c as TOKEN_HASH, d as TOKEN_STRING, k as TOKEN_DIMENSION, j as TOKEN_PERCENTAGE, i as TOKEN_NUMBER, T as TOKEN_IDENT, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN, b as TOKEN_AT_KEYWORD, v as TOKEN_LEFT_BRACE, w as TOKEN_RIGHT_BRACE, o as TOKEN_COLON, p as TOKEN_SEMICOLON } from './lexer-DXablYMZ.js';
2
- import { J as is_whitespace, f as NODE_VALUE_KEYWORD, g as NODE_VALUE_NUMBER, h as NODE_VALUE_DIMENSION, i as NODE_VALUE_STRING, j as NODE_VALUE_COLOR, l as NODE_VALUE_OPERATOR, k as NODE_VALUE_FUNCTION, K as CSSDataArena, e as NODE_STYLESHEET, C as CSSNode, N as NODE_STYLE_RULE, L as FLAG_HAS_BLOCK, M as FLAG_HAS_DECLARATIONS, m as NODE_SELECTOR_LIST, c as NODE_DECLARATION, O as is_vendor_prefixed, P as FLAG_VENDOR_PREFIXED, Q as trim_boundaries, I as FLAG_IMPORTANT, a as NODE_AT_RULE } from './string-utils-tMt2O9RW.js';
3
- import { S as SelectorParser } from './selector-parser-C-u1epDB.js';
4
- import { A as AtRulePreludeParser } from './at-rule-prelude-parser-Cj8ecgQp.js';
5
-
6
- const CH_PLUS = 43;
7
- const CH_MINUS = 45;
8
- const CH_ASTERISK = 42;
9
- const CH_SLASH = 47;
10
- class ValueParser {
11
- lexer;
12
- arena;
13
- source;
14
- value_end;
15
- constructor(arena, source) {
16
- this.arena = arena;
17
- this.source = source;
18
- this.lexer = new Lexer(source, false);
19
- this.value_end = 0;
20
- }
21
- // Parse a declaration value range into value nodes
22
- // Returns array of value node indices
23
- parse_value(start, end) {
24
- this.value_end = end;
25
- this.lexer.pos = start;
26
- this.lexer.line = 1;
27
- let nodes = [];
28
- while (this.lexer.pos < this.value_end) {
29
- this.lexer.next_token_fast(false);
30
- if (this.lexer.token_start >= this.value_end) break;
31
- let token_type = this.lexer.token_type;
32
- if (token_type === TOKEN_EOF) break;
33
- if (this.is_whitespace_token()) {
34
- continue;
35
- }
36
- let node = this.parse_value_node();
37
- if (node !== null) {
38
- nodes.push(node);
39
- }
40
- }
41
- return nodes;
42
- }
43
- is_whitespace_token() {
44
- let start = this.lexer.token_start;
45
- let end = this.lexer.token_end;
46
- if (start >= end) return false;
47
- for (let i = start; i < end; i++) {
48
- let ch = this.source.charCodeAt(i);
49
- if (!is_whitespace(ch)) {
50
- return false;
51
- }
52
- }
53
- return true;
54
- }
55
- parse_value_node() {
56
- let token_type = this.lexer.token_type;
57
- let start = this.lexer.token_start;
58
- let end = this.lexer.token_end;
59
- switch (token_type) {
60
- case TOKEN_IDENT:
61
- return this.create_keyword_node(start, end);
62
- case TOKEN_NUMBER:
63
- return this.create_number_node(start, end);
64
- case TOKEN_PERCENTAGE:
65
- case TOKEN_DIMENSION:
66
- return this.create_dimension_node(start, end);
67
- case TOKEN_STRING:
68
- return this.create_string_node(start, end);
69
- case TOKEN_HASH:
70
- return this.create_color_node(start, end);
71
- case TOKEN_FUNCTION:
72
- return this.parse_function_node(start, end);
73
- case TOKEN_DELIM:
74
- return this.parse_operator_node(start, end);
75
- case TOKEN_COMMA:
76
- return this.create_operator_node(start, end);
77
- default:
78
- return null;
79
- }
80
- }
81
- create_keyword_node(start, end) {
82
- let node = this.arena.create_node();
83
- this.arena.set_type(node, NODE_VALUE_KEYWORD);
84
- this.arena.set_start_offset(node, start);
85
- this.arena.set_length(node, end - start);
86
- this.arena.set_content_start(node, start);
87
- this.arena.set_content_length(node, end - start);
88
- return node;
89
- }
90
- create_number_node(start, end) {
91
- let node = this.arena.create_node();
92
- this.arena.set_type(node, NODE_VALUE_NUMBER);
93
- this.arena.set_start_offset(node, start);
94
- this.arena.set_length(node, end - start);
95
- this.arena.set_content_start(node, start);
96
- this.arena.set_content_length(node, end - start);
97
- return node;
98
- }
99
- create_dimension_node(start, end) {
100
- let node = this.arena.create_node();
101
- this.arena.set_type(node, NODE_VALUE_DIMENSION);
102
- this.arena.set_start_offset(node, start);
103
- this.arena.set_length(node, end - start);
104
- this.arena.set_content_start(node, start);
105
- this.arena.set_content_length(node, end - start);
106
- return node;
107
- }
108
- create_string_node(start, end) {
109
- let node = this.arena.create_node();
110
- this.arena.set_type(node, NODE_VALUE_STRING);
111
- this.arena.set_start_offset(node, start);
112
- this.arena.set_length(node, end - start);
113
- this.arena.set_content_start(node, start);
114
- this.arena.set_content_length(node, end - start);
115
- return node;
116
- }
117
- create_color_node(start, end) {
118
- let node = this.arena.create_node();
119
- this.arena.set_type(node, NODE_VALUE_COLOR);
120
- this.arena.set_start_offset(node, start);
121
- this.arena.set_length(node, end - start);
122
- this.arena.set_content_start(node, start);
123
- this.arena.set_content_length(node, end - start);
124
- return node;
125
- }
126
- create_operator_node(start, end) {
127
- let node = this.arena.create_node();
128
- this.arena.set_type(node, NODE_VALUE_OPERATOR);
129
- this.arena.set_start_offset(node, start);
130
- this.arena.set_length(node, end - start);
131
- this.arena.set_content_start(node, start);
132
- this.arena.set_content_length(node, end - start);
133
- return node;
134
- }
135
- parse_operator_node(start, end) {
136
- let ch = this.source.charCodeAt(start);
137
- if (ch === CH_PLUS || ch === CH_MINUS || ch === CH_ASTERISK || ch === CH_SLASH) {
138
- return this.create_operator_node(start, end);
139
- }
140
- return null;
141
- }
142
- parse_function_node(start, end) {
143
- let node = this.arena.create_node();
144
- this.arena.set_type(node, NODE_VALUE_FUNCTION);
145
- this.arena.set_start_offset(node, start);
146
- let name_end = end - 1;
147
- this.arena.set_content_start(node, start);
148
- this.arena.set_content_length(node, name_end - start);
149
- let args = [];
150
- let paren_depth = 1;
151
- let func_end = end;
152
- while (this.lexer.pos < this.value_end && paren_depth > 0) {
153
- this.lexer.next_token_fast(false);
154
- let token_type = this.lexer.token_type;
155
- if (token_type === TOKEN_EOF) break;
156
- if (this.lexer.token_start >= this.value_end) break;
157
- if (token_type === TOKEN_LEFT_PAREN) {
158
- paren_depth++;
159
- } else if (token_type === TOKEN_RIGHT_PAREN) {
160
- paren_depth--;
161
- if (paren_depth === 0) {
162
- func_end = this.lexer.token_end;
163
- break;
164
- }
165
- }
166
- if (this.is_whitespace_token()) continue;
167
- let arg_node = this.parse_value_node();
168
- if (arg_node !== null) {
169
- args.push(arg_node);
170
- }
171
- }
172
- this.arena.set_length(node, func_end - start);
173
- if (args.length > 0) {
174
- this.arena.set_first_child(node, args[0]);
175
- this.arena.set_last_child(node, args[args.length - 1]);
176
- for (let i = 0; i < args.length - 1; i++) {
177
- this.arena.set_next_sibling(args[i], args[i + 1]);
178
- }
179
- }
180
- return node;
181
- }
182
- }
1
+ import { L as Lexer, y as TOKEN_EOF, b as TOKEN_AT_KEYWORD, v as TOKEN_LEFT_BRACE, w as TOKEN_RIGHT_BRACE, T as TOKEN_IDENT, o as TOKEN_COLON, p as TOKEN_SEMICOLON, h as TOKEN_DELIM } from './lexer-CtBKgfVv.js';
2
+ import { T as CSSDataArena, k as NODE_STYLESHEET, C as CSSNode, N as NODE_STYLE_RULE, U as FLAG_HAS_BLOCK, V as NODE_BLOCK, W as FLAG_HAS_DECLARATIONS, s as NODE_SELECTOR_LIST, i as NODE_DECLARATION, X as is_vendor_prefixed, Y as FLAG_VENDOR_PREFIXED, Z as trim_boundaries, S as FLAG_IMPORTANT, g as NODE_AT_RULE } from './css-node-BzCSxoLM.js';
3
+ export { h as NODE_COMMENT, J as NODE_PRELUDE_CONTAINER_QUERY, M as NODE_PRELUDE_IDENTIFIER, Q as NODE_PRELUDE_IMPORT_LAYER, R as NODE_PRELUDE_IMPORT_SUPPORTS, P as NODE_PRELUDE_IMPORT_URL, L as NODE_PRELUDE_LAYER_NAME, H as NODE_PRELUDE_MEDIA_FEATURE, G as NODE_PRELUDE_MEDIA_QUERY, I as NODE_PRELUDE_MEDIA_TYPE, O as NODE_PRELUDE_OPERATOR, K as NODE_PRELUDE_SUPPORTS_QUERY, j as NODE_SELECTOR, w as NODE_SELECTOR_ATTRIBUTE, u as NODE_SELECTOR_CLASS, z as NODE_SELECTOR_COMBINATOR, v as NODE_SELECTOR_ID, _ as NODE_SELECTOR_LANG, D as NODE_SELECTOR_NESTING, E as NODE_SELECTOR_NTH, F as NODE_SELECTOR_NTH_OF, x as NODE_SELECTOR_PSEUDO_CLASS, y as NODE_SELECTOR_PSEUDO_ELEMENT, t as NODE_SELECTOR_TYPE, B as NODE_SELECTOR_UNIVERSAL, p as NODE_VALUE_COLOR, n as NODE_VALUE_DIMENSION, q as NODE_VALUE_FUNCTION, l as NODE_VALUE_KEYWORD, m as NODE_VALUE_NUMBER, r as NODE_VALUE_OPERATOR, o as NODE_VALUE_STRING } from './css-node-BzCSxoLM.js';
4
+ import { ValueParser } from './parse-value.js';
5
+ import { SelectorParser } from './parse-selector.js';
6
+ import { AtRulePreludeParser } from './parse-atrule-prelude.js';
183
7
 
184
8
  let DECLARATION_AT_RULES = /* @__PURE__ */ new Set(["font-face", "font-feature-values", "page", "property", "counter-style"]);
185
9
  let CONDITIONAL_AT_RULES = /* @__PURE__ */ new Set(["media", "supports", "container", "layer", "nest"]);
@@ -273,15 +97,23 @@ class Parser {
273
97
  if (this.peek_type() !== TOKEN_LEFT_BRACE) {
274
98
  return null;
275
99
  }
100
+ let block_start = this.lexer.token_end;
276
101
  this.next_token();
277
102
  this.arena.set_flag(style_rule, FLAG_HAS_BLOCK);
103
+ let block_line = this.lexer.token_line;
104
+ let block_column = this.lexer.token_column;
105
+ let block_node = this.arena.create_node();
106
+ this.arena.set_type(block_node, NODE_BLOCK);
107
+ this.arena.set_start_offset(block_node, block_start);
108
+ this.arena.set_start_line(block_node, block_line);
109
+ this.arena.set_start_column(block_node, block_column);
278
110
  while (!this.is_eof()) {
279
111
  let token_type = this.peek_type();
280
112
  if (token_type === TOKEN_RIGHT_BRACE) break;
281
113
  if (token_type === TOKEN_AT_KEYWORD) {
282
114
  let nested_at_rule = this.parse_atrule();
283
115
  if (nested_at_rule !== null) {
284
- this.arena.append_child(style_rule, nested_at_rule);
116
+ this.arena.append_child(block_node, nested_at_rule);
285
117
  } else {
286
118
  this.next_token();
287
119
  }
@@ -290,21 +122,27 @@ class Parser {
290
122
  let declaration = this.parse_declaration();
291
123
  if (declaration !== null) {
292
124
  this.arena.set_flag(style_rule, FLAG_HAS_DECLARATIONS);
293
- this.arena.append_child(style_rule, declaration);
125
+ this.arena.append_child(block_node, declaration);
294
126
  continue;
295
127
  }
296
128
  let nested_rule = this.parse_style_rule();
297
129
  if (nested_rule !== null) {
298
- this.arena.append_child(style_rule, nested_rule);
130
+ this.arena.append_child(block_node, nested_rule);
299
131
  } else {
300
132
  this.next_token();
301
133
  }
302
134
  }
135
+ let block_end = this.lexer.token_start;
136
+ let rule_end = this.lexer.token_end;
303
137
  if (this.peek_type() === TOKEN_RIGHT_BRACE) {
138
+ block_end = this.lexer.token_start;
139
+ rule_end = this.lexer.token_end;
304
140
  this.next_token();
305
141
  }
142
+ this.arena.set_length(block_node, block_end - block_start);
143
+ this.arena.append_child(style_rule, block_node);
306
144
  this.arena.set_start_offset(style_rule, rule_start);
307
- this.arena.set_length(style_rule, this.lexer.token_end - rule_start);
145
+ this.arena.set_length(style_rule, rule_end - rule_start);
308
146
  return style_rule;
309
147
  }
310
148
  // Parse a selector (everything before '{')
@@ -341,8 +179,10 @@ class Parser {
341
179
  let prop_end = this.lexer.token_end;
342
180
  let decl_line = this.lexer.token_line;
343
181
  let decl_column = this.lexer.token_column;
182
+ const saved = this.lexer.save_position();
344
183
  this.next_token();
345
184
  if (this.peek_type() !== TOKEN_COLON) {
185
+ this.lexer.restore_position(saved);
346
186
  return null;
347
187
  }
348
188
  this.next_token();
@@ -363,12 +203,17 @@ class Parser {
363
203
  while (!this.is_eof()) {
364
204
  let token_type = this.peek_type();
365
205
  if (token_type === TOKEN_SEMICOLON || token_type === TOKEN_RIGHT_BRACE) break;
206
+ if (token_type === TOKEN_LEFT_BRACE) {
207
+ this.lexer.restore_position(saved);
208
+ return null;
209
+ }
366
210
  if (token_type === TOKEN_DELIM && this.source[this.lexer.token_start] === "!") {
367
211
  value_end = this.lexer.token_start;
368
212
  let next_type = this.lexer.next_token_fast();
369
213
  if (next_type === TOKEN_IDENT) {
370
214
  has_important = true;
371
215
  last_end = this.lexer.token_end;
216
+ this.next_token();
372
217
  break;
373
218
  }
374
219
  }
@@ -439,8 +284,16 @@ class Parser {
439
284
  }
440
285
  let last_end = this.lexer.token_end;
441
286
  if (this.peek_type() === TOKEN_LEFT_BRACE) {
287
+ let block_start = this.lexer.token_end;
442
288
  this.next_token();
443
289
  this.arena.set_flag(at_rule, FLAG_HAS_BLOCK);
290
+ let block_line = this.lexer.token_line;
291
+ let block_column = this.lexer.token_column;
292
+ let block_node = this.arena.create_node();
293
+ this.arena.set_type(block_node, NODE_BLOCK);
294
+ this.arena.set_start_offset(block_node, block_start);
295
+ this.arena.set_start_line(block_node, block_line);
296
+ this.arena.set_start_column(block_node, block_column);
444
297
  let has_declarations = this.atrule_has_declarations(at_rule_name);
445
298
  let is_conditional = this.atrule_is_conditional(at_rule_name);
446
299
  if (has_declarations) {
@@ -449,7 +302,7 @@ class Parser {
449
302
  if (token_type === TOKEN_RIGHT_BRACE) break;
450
303
  let declaration = this.parse_declaration();
451
304
  if (declaration !== null) {
452
- this.arena.append_child(at_rule, declaration);
305
+ this.arena.append_child(block_node, declaration);
453
306
  } else {
454
307
  this.next_token();
455
308
  }
@@ -461,7 +314,7 @@ class Parser {
461
314
  if (token_type === TOKEN_AT_KEYWORD) {
462
315
  let nested_at_rule = this.parse_atrule();
463
316
  if (nested_at_rule !== null) {
464
- this.arena.append_child(at_rule, nested_at_rule);
317
+ this.arena.append_child(block_node, nested_at_rule);
465
318
  } else {
466
319
  this.next_token();
467
320
  }
@@ -469,12 +322,12 @@ class Parser {
469
322
  }
470
323
  let declaration = this.parse_declaration();
471
324
  if (declaration !== null) {
472
- this.arena.append_child(at_rule, declaration);
325
+ this.arena.append_child(block_node, declaration);
473
326
  continue;
474
327
  }
475
328
  let nested_rule = this.parse_style_rule();
476
329
  if (nested_rule !== null) {
477
- this.arena.append_child(at_rule, nested_rule);
330
+ this.arena.append_child(block_node, nested_rule);
478
331
  } else {
479
332
  this.next_token();
480
333
  }
@@ -485,16 +338,21 @@ class Parser {
485
338
  if (token_type === TOKEN_RIGHT_BRACE) break;
486
339
  let rule = this.parse_rule();
487
340
  if (rule !== null) {
488
- this.arena.append_child(at_rule, rule);
341
+ this.arena.append_child(block_node, rule);
489
342
  } else {
490
343
  this.next_token();
491
344
  }
492
345
  }
493
346
  }
494
347
  if (this.peek_type() === TOKEN_RIGHT_BRACE) {
495
- last_end = this.lexer.token_end;
348
+ let block_end = this.lexer.token_start;
496
349
  this.next_token();
350
+ last_end = this.lexer.token_end;
351
+ this.arena.set_length(block_node, block_end - block_start);
352
+ } else {
353
+ this.arena.set_length(block_node, last_end - block_start);
497
354
  }
355
+ this.arena.append_child(at_rule, block_node);
498
356
  } else if (this.peek_type() === TOKEN_SEMICOLON) {
499
357
  last_end = this.lexer.token_end;
500
358
  this.next_token();
@@ -511,10 +369,9 @@ class Parser {
511
369
  return CONDITIONAL_AT_RULES.has(name.toLowerCase());
512
370
  }
513
371
  }
514
-
515
372
  function parse(source, options) {
516
373
  const parser = new Parser(source, options);
517
374
  return parser.parse();
518
375
  }
519
376
 
520
- export { parse };
377
+ export { FLAG_IMPORTANT, NODE_AT_RULE, NODE_BLOCK, NODE_DECLARATION, NODE_SELECTOR_LIST, NODE_STYLESHEET, NODE_STYLE_RULE, Parser, parse };
@@ -6,23 +6,24 @@ export declare const CHAR_FORM_FEED = 12;
6
6
  export declare const CHAR_FORWARD_SLASH = 47;
7
7
  export declare const CHAR_ASTERISK = 42;
8
8
  export declare const CHAR_MINUS_HYPHEN = 45;
9
+ export declare const CHAR_SINGLE_QUOTE = 39;
10
+ export declare const CHAR_DOUBLE_QUOTE = 34;
11
+ export declare const CHAR_PLUS = 43;
12
+ export declare const CHAR_PERIOD = 46;
13
+ export declare const CHAR_TILDE = 126;
14
+ export declare const CHAR_GREATER_THAN = 62;
15
+ export declare const CHAR_AMPERSAND = 38;
16
+ export declare const CHAR_EQUALS = 61;
17
+ export declare const CHAR_PIPE = 124;
18
+ export declare const CHAR_DOLLAR = 36;
19
+ export declare const CHAR_CARET = 94;
20
+ export declare const CHAR_COLON = 58;
9
21
  /**
10
22
  * Check if a character code is whitespace (space, tab, newline, CR, or FF)
11
23
  */
12
24
  export declare function is_whitespace(ch: number): boolean;
13
- /**
14
- * Trim whitespace and comments from both ends of a string range
15
- *
16
- * @param source - The source string
17
- * @param start - Start offset in source
18
- * @param end - End offset in source
19
- * @returns [trimmed_start, trimmed_end] or null if all whitespace/comments
20
- *
21
- * Skips whitespace (space, tab, newline, CR, FF) and CSS comments from both ends
22
- * of the specified range. Returns the trimmed boundaries or null if the range
23
- * contains only whitespace and comments.
24
- */
25
- export declare function trim_boundaries(source: string, start: number, end: number): [number, number] | null;
25
+ export declare function is_combinator(ch: number): boolean;
26
+ export declare function is_digit(ch: number): boolean;
26
27
  /**
27
28
  * @param a Base string, MUST be lowercase!
28
29
  * @param b Compare string
package/dist/tokenize.js CHANGED
@@ -1,4 +1,4 @@
1
- import { L as Lexer, y as TOKEN_EOF } from './lexer-DXablYMZ.js';
1
+ import { L as Lexer, y as TOKEN_EOF } from './lexer-CtBKgfVv.js';
2
2
 
3
3
  function* tokenize(source, skip_comments = true) {
4
4
  const lexer = new Lexer(source, skip_comments);
package/dist/walk.d.ts CHANGED
@@ -16,5 +16,5 @@ interface WalkEnterLeaveOptions {
16
16
  * @param node - The root node to start walking from
17
17
  * @param options - Object with optional enter and leave callback functions
18
18
  */
19
- export declare function walk_enter_leave(node: CSSNode, { enter, leave }?: WalkEnterLeaveOptions): void;
19
+ export declare function traverse(node: CSSNode, { enter, leave }?: WalkEnterLeaveOptions): void;
20
20
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-parser",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "High-performance CSS lexer and parser, optimized for CSS inspection and analysis",
5
5
  "author": "Bart Veneman <bat@projectwallace.com>",
6
6
  "license": "MIT",
@@ -34,6 +34,14 @@
34
34
  "./parse-atrule-prelude": {
35
35
  "types": "./dist/parse-atrule-prelude.d.ts",
36
36
  "import": "./dist/parse-atrule-prelude.js"
37
+ },
38
+ "./parse-value": {
39
+ "types": "./dist/parse-value.d.ts",
40
+ "import": "./dist/parse-value.js"
41
+ },
42
+ "./parse-anplusb": {
43
+ "types": "./dist/parse-anplusb.d.ts",
44
+ "import": "./dist/parse-anplusb.js"
37
45
  }
38
46
  },
39
47
  "files": [