@projectwallace/css-parser 0.4.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.
@@ -1,6 +1,455 @@
1
- import { K as CSSDataArena, C as CSSNode } from './string-utils-tMt2O9RW.js';
2
- import { A as AtRulePreludeParser } from './at-rule-prelude-parser-Cj8ecgQp.js';
1
+ import { L as Lexer, q as TOKEN_COMMA, T as TOKEN_IDENT, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN, l as TOKEN_WHITESPACE, f as TOKEN_URL, a as TOKEN_FUNCTION, d as TOKEN_STRING, y as TOKEN_EOF } from './lexer-CtBKgfVv.js';
2
+ import { T as CSSDataArena, C as CSSNode, ah as str_equals, O as NODE_PRELUDE_OPERATOR, I as NODE_PRELUDE_MEDIA_TYPE, G as NODE_PRELUDE_MEDIA_QUERY, H as NODE_PRELUDE_MEDIA_FEATURE, Z as trim_boundaries, M as NODE_PRELUDE_IDENTIFIER, J as NODE_PRELUDE_CONTAINER_QUERY, K as NODE_PRELUDE_SUPPORTS_QUERY, L as NODE_PRELUDE_LAYER_NAME, P as NODE_PRELUDE_IMPORT_URL, Q as NODE_PRELUDE_IMPORT_LAYER, R as NODE_PRELUDE_IMPORT_SUPPORTS, ag as skip_whitespace_forward } from './css-node-BzCSxoLM.js';
3
3
 
4
+ class AtRulePreludeParser {
5
+ lexer;
6
+ arena;
7
+ source;
8
+ prelude_end;
9
+ constructor(arena, source) {
10
+ this.arena = arena;
11
+ this.source = source;
12
+ this.lexer = new Lexer(source, false);
13
+ this.prelude_end = 0;
14
+ }
15
+ // Parse an at-rule prelude into nodes based on the at-rule type
16
+ parse_prelude(at_rule_name, start, end, line = 1, column = 1) {
17
+ this.prelude_end = end;
18
+ this.lexer.pos = start;
19
+ this.lexer.line = line;
20
+ this.lexer.column = column;
21
+ if (str_equals("media", at_rule_name)) {
22
+ return this.parse_media_query_list();
23
+ } else if (str_equals("container", at_rule_name)) {
24
+ return this.parse_container_query();
25
+ } else if (str_equals("supports", at_rule_name)) {
26
+ return this.parse_supports_query();
27
+ } else if (str_equals("layer", at_rule_name)) {
28
+ return this.parse_layer_names();
29
+ } else if (str_equals("keyframes", at_rule_name)) {
30
+ return this.parse_identifier();
31
+ } else if (str_equals("property", at_rule_name)) {
32
+ return this.parse_identifier();
33
+ } else if (str_equals("import", at_rule_name)) {
34
+ return this.parse_import_prelude();
35
+ }
36
+ return [];
37
+ }
38
+ // Parse media query list: screen, (min-width: 768px), ...
39
+ parse_media_query_list() {
40
+ let nodes = [];
41
+ while (this.lexer.pos < this.prelude_end) {
42
+ this.skip_whitespace();
43
+ if (this.lexer.pos >= this.prelude_end) break;
44
+ let query = this.parse_single_media_query();
45
+ if (query !== null) {
46
+ nodes.push(query);
47
+ }
48
+ this.skip_whitespace();
49
+ if (this.peek_token_type() === TOKEN_COMMA) {
50
+ this.next_token();
51
+ }
52
+ }
53
+ return nodes;
54
+ }
55
+ is_and_or_not(str) {
56
+ if (str.length > 3 || str.length < 2) return false;
57
+ return str_equals("and", str) || str_equals("or", str) || str_equals("not", str);
58
+ }
59
+ // Parse a single media query: screen and (min-width: 768px)
60
+ parse_single_media_query() {
61
+ let query_start = this.lexer.pos;
62
+ let query_line = this.lexer.line;
63
+ this.skip_whitespace();
64
+ if (this.lexer.pos >= this.prelude_end) return null;
65
+ let token_start = this.lexer.pos;
66
+ this.next_token();
67
+ if (this.lexer.token_type === TOKEN_IDENT) {
68
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
69
+ if (!str_equals("only", text) && !str_equals("not", text)) {
70
+ this.lexer.pos = token_start;
71
+ }
72
+ } else {
73
+ this.lexer.pos = token_start;
74
+ }
75
+ let components = [];
76
+ while (this.lexer.pos < this.prelude_end) {
77
+ this.skip_whitespace();
78
+ if (this.lexer.pos >= this.prelude_end) break;
79
+ if (this.peek_token_type() === TOKEN_COMMA) break;
80
+ this.next_token();
81
+ let token_type = this.lexer.token_type;
82
+ if (token_type === TOKEN_LEFT_PAREN) {
83
+ let feature = this.parse_media_feature();
84
+ if (feature !== null) {
85
+ components.push(feature);
86
+ }
87
+ } else if (token_type === TOKEN_IDENT) {
88
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
89
+ if (this.is_and_or_not(text)) {
90
+ let op = this.arena.create_node();
91
+ this.arena.set_type(op, NODE_PRELUDE_OPERATOR);
92
+ this.arena.set_start_offset(op, this.lexer.token_start);
93
+ this.arena.set_length(op, this.lexer.token_end - this.lexer.token_start);
94
+ this.arena.set_start_line(op, this.lexer.token_line);
95
+ components.push(op);
96
+ } else {
97
+ let media_type = this.arena.create_node();
98
+ this.arena.set_type(media_type, NODE_PRELUDE_MEDIA_TYPE);
99
+ this.arena.set_start_offset(media_type, this.lexer.token_start);
100
+ this.arena.set_length(media_type, this.lexer.token_end - this.lexer.token_start);
101
+ this.arena.set_start_line(media_type, this.lexer.token_line);
102
+ this.arena.set_start_column(media_type, this.lexer.token_column);
103
+ components.push(media_type);
104
+ }
105
+ } else {
106
+ break;
107
+ }
108
+ }
109
+ if (components.length === 0) return null;
110
+ let query_node = this.arena.create_node();
111
+ this.arena.set_type(query_node, NODE_PRELUDE_MEDIA_QUERY);
112
+ this.arena.set_start_offset(query_node, query_start);
113
+ this.arena.set_length(query_node, this.lexer.pos - query_start);
114
+ this.arena.set_start_line(query_node, query_line);
115
+ for (let component of components) {
116
+ this.arena.append_child(query_node, component);
117
+ }
118
+ return query_node;
119
+ }
120
+ // Parse media feature: (min-width: 768px)
121
+ parse_media_feature() {
122
+ let feature_start = this.lexer.token_start;
123
+ let feature_line = this.lexer.token_line;
124
+ let depth = 1;
125
+ let content_start = this.lexer.pos;
126
+ while (this.lexer.pos < this.prelude_end && depth > 0) {
127
+ this.next_token();
128
+ let token_type = this.lexer.token_type;
129
+ if (token_type === TOKEN_LEFT_PAREN) {
130
+ depth++;
131
+ } else if (token_type === TOKEN_RIGHT_PAREN) {
132
+ depth--;
133
+ }
134
+ }
135
+ if (depth !== 0) return null;
136
+ let content_end = this.lexer.token_start;
137
+ let feature_end = this.lexer.token_end;
138
+ let feature = this.arena.create_node();
139
+ this.arena.set_type(feature, NODE_PRELUDE_MEDIA_FEATURE);
140
+ this.arena.set_start_offset(feature, feature_start);
141
+ this.arena.set_length(feature, feature_end - feature_start);
142
+ this.arena.set_start_line(feature, feature_line);
143
+ let trimmed = trim_boundaries(this.source, content_start, content_end);
144
+ if (trimmed) {
145
+ this.arena.set_value_start(feature, trimmed[0]);
146
+ this.arena.set_value_length(feature, trimmed[1] - trimmed[0]);
147
+ }
148
+ return feature;
149
+ }
150
+ // Parse container query: [name] and (min-width: 400px)
151
+ parse_container_query() {
152
+ let nodes = [];
153
+ let query_start = this.lexer.pos;
154
+ let query_line = this.lexer.line;
155
+ let components = [];
156
+ while (this.lexer.pos < this.prelude_end) {
157
+ this.skip_whitespace();
158
+ if (this.lexer.pos >= this.prelude_end) break;
159
+ this.next_token();
160
+ let token_type = this.lexer.token_type;
161
+ if (token_type === TOKEN_LEFT_PAREN) {
162
+ let feature = this.parse_media_feature();
163
+ if (feature !== null) {
164
+ components.push(feature);
165
+ }
166
+ } else if (token_type === TOKEN_IDENT) {
167
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
168
+ if (this.is_and_or_not(text)) {
169
+ let op = this.arena.create_node();
170
+ this.arena.set_type(op, NODE_PRELUDE_OPERATOR);
171
+ this.arena.set_start_offset(op, this.lexer.token_start);
172
+ this.arena.set_length(op, this.lexer.token_end - this.lexer.token_start);
173
+ this.arena.set_start_line(op, this.lexer.token_line);
174
+ components.push(op);
175
+ } else {
176
+ let name = this.arena.create_node();
177
+ this.arena.set_type(name, NODE_PRELUDE_IDENTIFIER);
178
+ this.arena.set_start_offset(name, this.lexer.token_start);
179
+ this.arena.set_length(name, this.lexer.token_end - this.lexer.token_start);
180
+ this.arena.set_start_line(name, this.lexer.token_line);
181
+ components.push(name);
182
+ }
183
+ }
184
+ }
185
+ if (components.length === 0) return [];
186
+ let query_node = this.arena.create_node();
187
+ this.arena.set_type(query_node, NODE_PRELUDE_CONTAINER_QUERY);
188
+ this.arena.set_start_offset(query_node, query_start);
189
+ this.arena.set_length(query_node, this.lexer.pos - query_start);
190
+ this.arena.set_start_line(query_node, query_line);
191
+ for (let component of components) {
192
+ this.arena.append_child(query_node, component);
193
+ }
194
+ nodes.push(query_node);
195
+ return nodes;
196
+ }
197
+ // Parse supports query: (display: flex) and (gap: 1rem)
198
+ parse_supports_query() {
199
+ let nodes = [];
200
+ while (this.lexer.pos < this.prelude_end) {
201
+ this.skip_whitespace();
202
+ if (this.lexer.pos >= this.prelude_end) break;
203
+ this.next_token();
204
+ let token_type = this.lexer.token_type;
205
+ if (token_type === TOKEN_LEFT_PAREN) {
206
+ let feature_start = this.lexer.token_start;
207
+ let feature_line = this.lexer.token_line;
208
+ let depth = 1;
209
+ let content_start = this.lexer.pos;
210
+ while (this.lexer.pos < this.prelude_end && depth > 0) {
211
+ this.next_token();
212
+ let inner_token_type = this.lexer.token_type;
213
+ if (inner_token_type === TOKEN_LEFT_PAREN) {
214
+ depth++;
215
+ } else if (inner_token_type === TOKEN_RIGHT_PAREN) {
216
+ depth--;
217
+ }
218
+ }
219
+ if (depth === 0) {
220
+ let content_end = this.lexer.token_start;
221
+ let feature_end = this.lexer.token_end;
222
+ let query = this.arena.create_node();
223
+ this.arena.set_type(query, NODE_PRELUDE_SUPPORTS_QUERY);
224
+ this.arena.set_start_offset(query, feature_start);
225
+ this.arena.set_length(query, feature_end - feature_start);
226
+ this.arena.set_start_line(query, feature_line);
227
+ let trimmed = trim_boundaries(this.source, content_start, content_end);
228
+ if (trimmed) {
229
+ this.arena.set_value_start(query, trimmed[0]);
230
+ this.arena.set_value_length(query, trimmed[1] - trimmed[0]);
231
+ }
232
+ nodes.push(query);
233
+ }
234
+ } else if (token_type === TOKEN_IDENT) {
235
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
236
+ if (this.is_and_or_not(text)) {
237
+ let op = this.arena.create_node();
238
+ this.arena.set_type(op, NODE_PRELUDE_OPERATOR);
239
+ this.arena.set_start_offset(op, this.lexer.token_start);
240
+ this.arena.set_length(op, this.lexer.token_end - this.lexer.token_start);
241
+ this.arena.set_start_line(op, this.lexer.token_line);
242
+ nodes.push(op);
243
+ }
244
+ }
245
+ }
246
+ return nodes;
247
+ }
248
+ // Parse layer names: base, components, utilities
249
+ parse_layer_names() {
250
+ let nodes = [];
251
+ while (this.lexer.pos < this.prelude_end) {
252
+ this.skip_whitespace();
253
+ if (this.lexer.pos >= this.prelude_end) break;
254
+ this.next_token();
255
+ let token_type = this.lexer.token_type;
256
+ if (token_type === TOKEN_IDENT) {
257
+ let layer = this.arena.create_node();
258
+ this.arena.set_type(layer, NODE_PRELUDE_LAYER_NAME);
259
+ this.arena.set_start_offset(layer, this.lexer.token_start);
260
+ this.arena.set_length(layer, this.lexer.token_end - this.lexer.token_start);
261
+ this.arena.set_start_line(layer, this.lexer.token_line);
262
+ nodes.push(layer);
263
+ } else if (token_type === TOKEN_COMMA) {
264
+ continue;
265
+ } else if (token_type === TOKEN_WHITESPACE) {
266
+ continue;
267
+ }
268
+ }
269
+ return nodes;
270
+ }
271
+ // Parse single identifier: keyframe name, property name
272
+ parse_identifier() {
273
+ this.skip_whitespace();
274
+ if (this.lexer.pos >= this.prelude_end) return [];
275
+ this.next_token();
276
+ if (this.lexer.token_type !== TOKEN_IDENT) return [];
277
+ let ident = this.arena.create_node();
278
+ this.arena.set_type(ident, NODE_PRELUDE_IDENTIFIER);
279
+ this.arena.set_start_offset(ident, this.lexer.token_start);
280
+ this.arena.set_length(ident, this.lexer.token_end - this.lexer.token_start);
281
+ this.arena.set_start_line(ident, this.lexer.token_line);
282
+ return [ident];
283
+ }
284
+ // Parse @import prelude: url() [layer] [supports()] [media-query-list]
285
+ // @import url("styles.css") layer(base) supports(display: grid) screen and (min-width: 768px);
286
+ parse_import_prelude() {
287
+ let nodes = [];
288
+ this.skip_whitespace();
289
+ if (this.lexer.pos >= this.prelude_end) return [];
290
+ let url_node = this.parse_import_url();
291
+ if (url_node !== null) {
292
+ nodes.push(url_node);
293
+ } else {
294
+ return [];
295
+ }
296
+ this.skip_whitespace();
297
+ if (this.lexer.pos >= this.prelude_end) return nodes;
298
+ let layer_node = this.parse_import_layer();
299
+ if (layer_node !== null) {
300
+ nodes.push(layer_node);
301
+ }
302
+ this.skip_whitespace();
303
+ if (this.lexer.pos >= this.prelude_end) return nodes;
304
+ let supports_node = this.parse_import_supports();
305
+ if (supports_node !== null) {
306
+ nodes.push(supports_node);
307
+ }
308
+ this.skip_whitespace();
309
+ if (this.lexer.pos >= this.prelude_end) return nodes;
310
+ let media_nodes = this.parse_media_query_list();
311
+ nodes.push(...media_nodes);
312
+ return nodes;
313
+ }
314
+ // Parse import URL: url("file.css") or "file.css"
315
+ parse_import_url() {
316
+ this.next_token();
317
+ if (this.lexer.token_type !== TOKEN_URL && this.lexer.token_type !== TOKEN_FUNCTION && this.lexer.token_type !== TOKEN_STRING) {
318
+ return null;
319
+ }
320
+ let url_start = this.lexer.token_start;
321
+ let url_end = this.lexer.token_end;
322
+ let url_line = this.lexer.token_line;
323
+ if (this.lexer.token_type === TOKEN_FUNCTION) {
324
+ let paren_depth = 1;
325
+ while (this.lexer.pos < this.prelude_end && paren_depth > 0) {
326
+ let tokenType = this.next_token();
327
+ if (tokenType === TOKEN_LEFT_PAREN || tokenType === TOKEN_FUNCTION) {
328
+ paren_depth++;
329
+ } else if (tokenType === TOKEN_RIGHT_PAREN) {
330
+ paren_depth--;
331
+ if (paren_depth === 0) {
332
+ url_end = this.lexer.token_end;
333
+ }
334
+ } else if (tokenType === TOKEN_EOF) {
335
+ break;
336
+ }
337
+ }
338
+ }
339
+ let url_node = this.arena.create_node();
340
+ this.arena.set_type(url_node, NODE_PRELUDE_IMPORT_URL);
341
+ this.arena.set_start_offset(url_node, url_start);
342
+ this.arena.set_length(url_node, url_end - url_start);
343
+ this.arena.set_start_line(url_node, url_line);
344
+ return url_node;
345
+ }
346
+ // Parse import layer: layer or layer(name)
347
+ parse_import_layer() {
348
+ const saved = this.lexer.save_position();
349
+ this.next_token();
350
+ if (this.lexer.token_type === TOKEN_IDENT || this.lexer.token_type === TOKEN_FUNCTION) {
351
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end);
352
+ if (this.lexer.token_type === TOKEN_FUNCTION && text.endsWith("(")) {
353
+ text = text.slice(0, -1);
354
+ }
355
+ if (str_equals("layer", text)) {
356
+ let layer_start = this.lexer.token_start;
357
+ let layer_end = this.lexer.token_end;
358
+ let layer_line = this.lexer.token_line;
359
+ let content_start = 0;
360
+ let content_length = 0;
361
+ if (this.lexer.token_type === TOKEN_FUNCTION) {
362
+ content_start = this.lexer.pos;
363
+ let paren_depth = 1;
364
+ while (this.lexer.pos < this.prelude_end && paren_depth > 0) {
365
+ let tokenType = this.next_token();
366
+ if (tokenType === TOKEN_LEFT_PAREN || tokenType === TOKEN_FUNCTION) {
367
+ paren_depth++;
368
+ } else if (tokenType === TOKEN_RIGHT_PAREN) {
369
+ paren_depth--;
370
+ if (paren_depth === 0) {
371
+ content_length = this.lexer.token_start - content_start;
372
+ layer_end = this.lexer.token_end;
373
+ }
374
+ } else if (tokenType === TOKEN_EOF) {
375
+ break;
376
+ }
377
+ }
378
+ }
379
+ let layer_node = this.arena.create_node();
380
+ this.arena.set_type(layer_node, NODE_PRELUDE_IMPORT_LAYER);
381
+ this.arena.set_start_offset(layer_node, layer_start);
382
+ this.arena.set_length(layer_node, layer_end - layer_start);
383
+ this.arena.set_start_line(layer_node, layer_line);
384
+ if (content_length > 0) {
385
+ let trimmed = trim_boundaries(this.source, content_start, content_start + content_length);
386
+ if (trimmed) {
387
+ this.arena.set_content_start(layer_node, trimmed[0]);
388
+ this.arena.set_content_length(layer_node, trimmed[1] - trimmed[0]);
389
+ }
390
+ }
391
+ return layer_node;
392
+ }
393
+ }
394
+ this.lexer.restore_position(saved);
395
+ return null;
396
+ }
397
+ // Parse import supports: supports(condition)
398
+ parse_import_supports() {
399
+ const saved = this.lexer.save_position();
400
+ this.next_token();
401
+ if (this.lexer.token_type === TOKEN_FUNCTION) {
402
+ let text = this.source.substring(this.lexer.token_start, this.lexer.token_end - 1);
403
+ if (str_equals("supports", text)) {
404
+ let supports_start = this.lexer.token_start;
405
+ let supports_line = this.lexer.token_line;
406
+ let paren_depth = 1;
407
+ let supports_end = this.lexer.token_end;
408
+ while (this.lexer.pos < this.prelude_end && paren_depth > 0) {
409
+ let tokenType = this.next_token();
410
+ if (tokenType === TOKEN_LEFT_PAREN || tokenType === TOKEN_FUNCTION) {
411
+ paren_depth++;
412
+ } else if (tokenType === TOKEN_RIGHT_PAREN) {
413
+ paren_depth--;
414
+ if (paren_depth === 0) {
415
+ supports_end = this.lexer.token_end;
416
+ }
417
+ } else if (tokenType === TOKEN_EOF) {
418
+ break;
419
+ }
420
+ }
421
+ let supports_node = this.arena.create_node();
422
+ this.arena.set_type(supports_node, NODE_PRELUDE_IMPORT_SUPPORTS);
423
+ this.arena.set_start_offset(supports_node, supports_start);
424
+ this.arena.set_length(supports_node, supports_end - supports_start);
425
+ this.arena.set_start_line(supports_node, supports_line);
426
+ return supports_node;
427
+ }
428
+ }
429
+ this.lexer.restore_position(saved);
430
+ return null;
431
+ }
432
+ // Helper: Skip whitespace
433
+ skip_whitespace() {
434
+ this.lexer.pos = skip_whitespace_forward(this.source, this.lexer.pos, this.prelude_end);
435
+ }
436
+ // Helper: Peek at next token type without consuming
437
+ peek_token_type() {
438
+ const saved = this.lexer.save_position();
439
+ this.next_token();
440
+ let type = this.lexer.token_type;
441
+ this.lexer.restore_position(saved);
442
+ return type;
443
+ }
444
+ // Helper: Get next token
445
+ next_token() {
446
+ if (this.lexer.pos >= this.prelude_end) {
447
+ this.lexer.token_type = TOKEN_EOF;
448
+ return TOKEN_EOF;
449
+ }
450
+ return this.lexer.next_token_fast(false);
451
+ }
452
+ }
4
453
  function parse_atrule_prelude(at_rule_name, prelude) {
5
454
  const arena = new CSSDataArena(CSSDataArena.capacity_for_source(prelude.length));
6
455
  const prelude_parser = new AtRulePreludeParser(arena, prelude);
@@ -8,4 +457,4 @@ function parse_atrule_prelude(at_rule_name, prelude) {
8
457
  return node_indices.map((index) => new CSSNode(arena, prelude, index));
9
458
  }
10
459
 
11
- export { parse_atrule_prelude };
460
+ export { AtRulePreludeParser, parse_atrule_prelude };
@@ -1,4 +1,34 @@
1
+ import { CSSDataArena } from './arena';
1
2
  import { CSSNode } from './css-node';
3
+ export declare class SelectorParser {
4
+ private lexer;
5
+ private arena;
6
+ private source;
7
+ private selector_end;
8
+ constructor(arena: CSSDataArena, source: string);
9
+ parse_selector(start: number, end: number, line?: number, column?: number, allow_relative?: boolean): number | null;
10
+ private parse_selector_list;
11
+ private parse_complex_selector;
12
+ private parse_compound_selector;
13
+ private parse_simple_selector;
14
+ private try_parse_combinator;
15
+ private parse_class_selector;
16
+ private parse_attribute_selector;
17
+ private parse_attribute_content;
18
+ private parse_pseudo;
19
+ private parse_pseudo_function;
20
+ private parse_pseudo_function_after_colon;
21
+ private is_nth_pseudo;
22
+ private parse_lang_identifiers;
23
+ private parse_nth_expression;
24
+ private find_of_keyword;
25
+ private create_type_selector;
26
+ private create_id_selector;
27
+ private create_universal_selector;
28
+ private create_nesting_selector;
29
+ private create_combinator;
30
+ private skip_whitespace;
31
+ }
2
32
  /**
3
33
  * Parse a CSS selector string and return an AST
4
34
  * @param source - The CSS selector to parse (e.g., "div.class > p#id")