@projectwallace/css-parser 0.6.7 → 0.7.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.
- package/dist/arena.d.ts +36 -91
- package/dist/arena.js +280 -0
- package/dist/char-types.js +55 -0
- package/dist/constants.d.ts +1 -0
- package/dist/css-node.d.ts +2 -2
- package/dist/css-node.js +455 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -24
- package/dist/lexer.d.ts +1 -37
- package/dist/{lexer-CtBKgfVv.js → lexer.js} +3 -81
- package/dist/parse-anplusb.d.ts +0 -20
- package/dist/parse-anplusb.js +28 -30
- package/dist/parse-atrule-prelude.d.ts +0 -25
- package/dist/parse-atrule-prelude.js +36 -42
- package/dist/parse-selector.d.ts +0 -30
- package/dist/parse-selector.js +64 -66
- package/dist/parse-utils.d.ts +0 -40
- package/dist/parse-utils.js +92 -0
- package/dist/parse-value.d.ts +0 -16
- package/dist/parse-value.js +50 -49
- package/dist/parse.d.ts +0 -27
- package/dist/parse.js +96 -73
- package/dist/string-utils.d.ts +0 -6
- package/dist/string-utils.js +63 -0
- package/dist/token-types.js +28 -0
- package/dist/tokenize.js +2 -1
- package/dist/walk.js +21 -0
- package/package.json +1 -1
- package/dist/css-node-GOEvp2OO.js +0 -876
package/dist/css-node.js
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { DIMENSION, NUMBER, DECLARATION, FLAG_IMPORTANT, FLAG_VENDOR_PREFIXED, FLAG_HAS_ERROR, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, STYLE_RULE, BLOCK, AT_RULE, COMMENT, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, FLAG_HAS_PARENS, NTH_SELECTOR, NTH_OF_SELECTOR, SELECTOR_LIST, SELECTOR, COMBINATOR, ATTRIBUTE_SELECTOR, PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_FEATURE, MEDIA_QUERY, LANG_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, URL, PARENTHESIS, OPERATOR, FUNCTION, HASH, STRING, IDENTIFIER, STYLESHEET } from './arena.js';
|
|
2
|
+
import { is_whitespace, CHAR_MINUS_HYPHEN, CHAR_PLUS } from './string-utils.js';
|
|
3
|
+
import { parse_dimension } from './parse-utils.js';
|
|
4
|
+
|
|
5
|
+
const TYPE_NAMES = {
|
|
6
|
+
[STYLESHEET]: "StyleSheet",
|
|
7
|
+
[STYLE_RULE]: "Rule",
|
|
8
|
+
[AT_RULE]: "Atrule",
|
|
9
|
+
[DECLARATION]: "Declaration",
|
|
10
|
+
[SELECTOR]: "Selector",
|
|
11
|
+
[COMMENT]: "Comment",
|
|
12
|
+
[BLOCK]: "Block",
|
|
13
|
+
[IDENTIFIER]: "Identifier",
|
|
14
|
+
[NUMBER]: "Number",
|
|
15
|
+
[DIMENSION]: "Dimension",
|
|
16
|
+
[STRING]: "String",
|
|
17
|
+
[HASH]: "Hash",
|
|
18
|
+
[FUNCTION]: "Function",
|
|
19
|
+
[OPERATOR]: "Operator",
|
|
20
|
+
[PARENTHESIS]: "Parentheses",
|
|
21
|
+
[URL]: "Url",
|
|
22
|
+
[SELECTOR_LIST]: "SelectorList",
|
|
23
|
+
[TYPE_SELECTOR]: "TypeSelector",
|
|
24
|
+
[CLASS_SELECTOR]: "ClassSelector",
|
|
25
|
+
[ID_SELECTOR]: "IdSelector",
|
|
26
|
+
[ATTRIBUTE_SELECTOR]: "AttributeSelector",
|
|
27
|
+
[PSEUDO_CLASS_SELECTOR]: "PseudoClassSelector",
|
|
28
|
+
[PSEUDO_ELEMENT_SELECTOR]: "PseudoElementSelector",
|
|
29
|
+
[COMBINATOR]: "Combinator",
|
|
30
|
+
[UNIVERSAL_SELECTOR]: "UniversalSelector",
|
|
31
|
+
[NESTING_SELECTOR]: "NestingSelector",
|
|
32
|
+
[NTH_SELECTOR]: "Nth",
|
|
33
|
+
[NTH_OF_SELECTOR]: "NthOf",
|
|
34
|
+
[LANG_SELECTOR]: "Lang",
|
|
35
|
+
[MEDIA_QUERY]: "MediaQuery",
|
|
36
|
+
[MEDIA_FEATURE]: "Feature",
|
|
37
|
+
[MEDIA_TYPE]: "MediaType",
|
|
38
|
+
[CONTAINER_QUERY]: "ContainerQuery",
|
|
39
|
+
[SUPPORTS_QUERY]: "SupportsQuery",
|
|
40
|
+
[LAYER_NAME]: "Layer",
|
|
41
|
+
[PRELUDE_OPERATOR]: "Operator"
|
|
42
|
+
};
|
|
43
|
+
class CSSNode {
|
|
44
|
+
arena;
|
|
45
|
+
source;
|
|
46
|
+
index;
|
|
47
|
+
constructor(arena, source, index) {
|
|
48
|
+
this.arena = arena;
|
|
49
|
+
this.source = source;
|
|
50
|
+
this.index = index;
|
|
51
|
+
}
|
|
52
|
+
// Get the node index (for internal use)
|
|
53
|
+
get_index() {
|
|
54
|
+
return this.index;
|
|
55
|
+
}
|
|
56
|
+
// Get node type as number (for performance)
|
|
57
|
+
get type() {
|
|
58
|
+
return this.arena.get_type(this.index);
|
|
59
|
+
}
|
|
60
|
+
// Get node type as human-readable string
|
|
61
|
+
get type_name() {
|
|
62
|
+
return TYPE_NAMES[this.type] || "unknown";
|
|
63
|
+
}
|
|
64
|
+
// Get the full text of this node from source
|
|
65
|
+
get text() {
|
|
66
|
+
let start = this.arena.get_start_offset(this.index);
|
|
67
|
+
let length = this.arena.get_length(this.index);
|
|
68
|
+
return this.source.substring(start, start + length);
|
|
69
|
+
}
|
|
70
|
+
// Get the "content" text (property name for declarations, at-rule name for at-rules, layer name for import layers)
|
|
71
|
+
get name() {
|
|
72
|
+
let start = this.arena.get_content_start(this.index);
|
|
73
|
+
let length = this.arena.get_content_length(this.index);
|
|
74
|
+
if (length === 0) return "";
|
|
75
|
+
return this.source.substring(start, start + length);
|
|
76
|
+
}
|
|
77
|
+
// Alias for name (for declarations: "color" in "color: blue")
|
|
78
|
+
// More semantic than `name` for declaration nodes
|
|
79
|
+
get property() {
|
|
80
|
+
return this.name;
|
|
81
|
+
}
|
|
82
|
+
// Get the value text (for declarations: "blue" in "color: blue")
|
|
83
|
+
// For dimension/number nodes: returns the numeric value as a number
|
|
84
|
+
// For string nodes: returns the string content without quotes
|
|
85
|
+
get value() {
|
|
86
|
+
if (this.type === DIMENSION || this.type === NUMBER) {
|
|
87
|
+
return parse_dimension(this.text).value;
|
|
88
|
+
}
|
|
89
|
+
let start = this.arena.get_value_start(this.index);
|
|
90
|
+
let length = this.arena.get_value_length(this.index);
|
|
91
|
+
if (length === 0) return null;
|
|
92
|
+
return this.source.substring(start, start + length);
|
|
93
|
+
}
|
|
94
|
+
// Get the prelude text (for at-rules: "(min-width: 768px)" in "@media (min-width: 768px)")
|
|
95
|
+
// This is an alias for `value` to make at-rule usage more semantic
|
|
96
|
+
get prelude() {
|
|
97
|
+
let val = this.value;
|
|
98
|
+
return typeof val === "string" ? val : null;
|
|
99
|
+
}
|
|
100
|
+
// Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
|
|
101
|
+
// Returns one of the ATTR_OPERATOR_* constants
|
|
102
|
+
get attr_operator() {
|
|
103
|
+
return this.arena.get_attr_operator(this.index);
|
|
104
|
+
}
|
|
105
|
+
// Get the attribute flags (for attribute selectors: i, s)
|
|
106
|
+
// Returns one of the ATTR_FLAG_* constants
|
|
107
|
+
get attr_flags() {
|
|
108
|
+
return this.arena.get_attr_flags(this.index);
|
|
109
|
+
}
|
|
110
|
+
// Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%")
|
|
111
|
+
get unit() {
|
|
112
|
+
if (this.type !== DIMENSION) return null;
|
|
113
|
+
return parse_dimension(this.text).unit;
|
|
114
|
+
}
|
|
115
|
+
// Check if this declaration has !important
|
|
116
|
+
get is_important() {
|
|
117
|
+
if (this.type !== DECLARATION) return null;
|
|
118
|
+
return this.arena.has_flag(this.index, FLAG_IMPORTANT);
|
|
119
|
+
}
|
|
120
|
+
// Check if this has a vendor prefix (flag-based for performance)
|
|
121
|
+
get is_vendor_prefixed() {
|
|
122
|
+
return this.arena.has_flag(this.index, FLAG_VENDOR_PREFIXED);
|
|
123
|
+
}
|
|
124
|
+
// Check if this node has an error
|
|
125
|
+
get has_error() {
|
|
126
|
+
return this.arena.has_flag(this.index, FLAG_HAS_ERROR);
|
|
127
|
+
}
|
|
128
|
+
// Check if this at-rule has a prelude
|
|
129
|
+
get has_prelude() {
|
|
130
|
+
return this.arena.get_value_length(this.index) > 0;
|
|
131
|
+
}
|
|
132
|
+
// Check if this rule has a block { }
|
|
133
|
+
get has_block() {
|
|
134
|
+
return this.arena.has_flag(this.index, FLAG_HAS_BLOCK);
|
|
135
|
+
}
|
|
136
|
+
// Check if this style rule has declarations
|
|
137
|
+
get has_declarations() {
|
|
138
|
+
return this.arena.has_flag(this.index, FLAG_HAS_DECLARATIONS);
|
|
139
|
+
}
|
|
140
|
+
// Get the block node (for style rules and at-rules with blocks)
|
|
141
|
+
get block() {
|
|
142
|
+
if (this.type === STYLE_RULE) {
|
|
143
|
+
let first = this.first_child;
|
|
144
|
+
if (!first) return null;
|
|
145
|
+
let blockNode = first.next_sibling;
|
|
146
|
+
if (blockNode && blockNode.type === BLOCK) {
|
|
147
|
+
return blockNode;
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
if (this.type === AT_RULE) {
|
|
152
|
+
let child = this.first_child;
|
|
153
|
+
while (child) {
|
|
154
|
+
if (child.type === BLOCK && !child.next_sibling) {
|
|
155
|
+
return child;
|
|
156
|
+
}
|
|
157
|
+
child = child.next_sibling;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
// Check if this block is empty (no declarations or rules, only comments allowed)
|
|
164
|
+
get is_empty() {
|
|
165
|
+
if (this.type !== BLOCK) return false;
|
|
166
|
+
let child = this.first_child;
|
|
167
|
+
while (child) {
|
|
168
|
+
if (child.type !== COMMENT) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
child = child.next_sibling;
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
// --- Value Node Access (for declarations) ---
|
|
176
|
+
// Get array of parsed value nodes (for declarations only)
|
|
177
|
+
get values() {
|
|
178
|
+
let result = [];
|
|
179
|
+
let child = this.first_child;
|
|
180
|
+
while (child) {
|
|
181
|
+
result.push(child);
|
|
182
|
+
child = child.next_sibling;
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
// Get count of value nodes
|
|
187
|
+
get value_count() {
|
|
188
|
+
let count = 0;
|
|
189
|
+
let child = this.first_child;
|
|
190
|
+
while (child) {
|
|
191
|
+
count++;
|
|
192
|
+
child = child.next_sibling;
|
|
193
|
+
}
|
|
194
|
+
return count;
|
|
195
|
+
}
|
|
196
|
+
// Get start line number
|
|
197
|
+
get line() {
|
|
198
|
+
return this.arena.get_start_line(this.index);
|
|
199
|
+
}
|
|
200
|
+
// Get start column number
|
|
201
|
+
get column() {
|
|
202
|
+
return this.arena.get_start_column(this.index);
|
|
203
|
+
}
|
|
204
|
+
// Get start offset in source
|
|
205
|
+
get offset() {
|
|
206
|
+
return this.arena.get_start_offset(this.index);
|
|
207
|
+
}
|
|
208
|
+
// Get length in source
|
|
209
|
+
get length() {
|
|
210
|
+
return this.arena.get_length(this.index);
|
|
211
|
+
}
|
|
212
|
+
// --- Tree Traversal ---
|
|
213
|
+
// Get first child node
|
|
214
|
+
get first_child() {
|
|
215
|
+
let child_index = this.arena.get_first_child(this.index);
|
|
216
|
+
if (child_index === 0) return null;
|
|
217
|
+
return new CSSNode(this.arena, this.source, child_index);
|
|
218
|
+
}
|
|
219
|
+
// Get next sibling node
|
|
220
|
+
get next_sibling() {
|
|
221
|
+
let sibling_index = this.arena.get_next_sibling(this.index);
|
|
222
|
+
if (sibling_index === 0) return null;
|
|
223
|
+
return new CSSNode(this.arena, this.source, sibling_index);
|
|
224
|
+
}
|
|
225
|
+
get has_next() {
|
|
226
|
+
let sibling_index = this.arena.get_next_sibling(this.index);
|
|
227
|
+
return sibling_index !== 0;
|
|
228
|
+
}
|
|
229
|
+
// Check if this node has children
|
|
230
|
+
// For pseudo-class/pseudo-element functions, returns true if FLAG_HAS_PARENS is set
|
|
231
|
+
// This allows formatters to distinguish :lang() from :hover
|
|
232
|
+
get has_children() {
|
|
233
|
+
if (this.type === PSEUDO_CLASS_SELECTOR || this.type === PSEUDO_ELEMENT_SELECTOR) {
|
|
234
|
+
if (this.arena.has_flag(this.index, FLAG_HAS_PARENS)) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return this.arena.has_children(this.index);
|
|
239
|
+
}
|
|
240
|
+
// Get all children as an array
|
|
241
|
+
get children() {
|
|
242
|
+
let result = [];
|
|
243
|
+
let child = this.first_child;
|
|
244
|
+
while (child) {
|
|
245
|
+
result.push(child);
|
|
246
|
+
child = child.next_sibling;
|
|
247
|
+
}
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
// Make CSSNode iterable over its children
|
|
251
|
+
*[Symbol.iterator]() {
|
|
252
|
+
let child = this.first_child;
|
|
253
|
+
while (child) {
|
|
254
|
+
yield child;
|
|
255
|
+
child = child.next_sibling;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// --- An+B Expression Helpers (for NODE_SELECTOR_NTH) ---
|
|
259
|
+
// Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd")
|
|
260
|
+
get nth_a() {
|
|
261
|
+
if (this.type !== NTH_SELECTOR) return null;
|
|
262
|
+
let len = this.arena.get_content_length(this.index);
|
|
263
|
+
if (len === 0) return null;
|
|
264
|
+
let start = this.arena.get_content_start(this.index);
|
|
265
|
+
return this.source.substring(start, start + len);
|
|
266
|
+
}
|
|
267
|
+
// Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1")
|
|
268
|
+
get nth_b() {
|
|
269
|
+
if (this.type !== NTH_SELECTOR) return null;
|
|
270
|
+
let len = this.arena.get_value_length(this.index);
|
|
271
|
+
if (len === 0) return null;
|
|
272
|
+
let start = this.arena.get_value_start(this.index);
|
|
273
|
+
let value = this.source.substring(start, start + len);
|
|
274
|
+
let check_pos = start - 1;
|
|
275
|
+
while (check_pos >= 0) {
|
|
276
|
+
let ch = this.source.charCodeAt(check_pos);
|
|
277
|
+
if (is_whitespace(ch)) {
|
|
278
|
+
check_pos--;
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (ch === CHAR_MINUS_HYPHEN) {
|
|
282
|
+
value = "-" + value;
|
|
283
|
+
} else if (ch === CHAR_PLUS) {
|
|
284
|
+
value = "+" + value;
|
|
285
|
+
}
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
// --- Pseudo-Class Nth-Of Helpers (for NODE_SELECTOR_NTH_OF) ---
|
|
291
|
+
// Get the An+B formula node from :nth-child(2n+1 of .foo)
|
|
292
|
+
get nth() {
|
|
293
|
+
if (this.type !== NTH_OF_SELECTOR) return null;
|
|
294
|
+
return this.first_child;
|
|
295
|
+
}
|
|
296
|
+
// Get the selector list from :nth-child(2n+1 of .foo)
|
|
297
|
+
get selector() {
|
|
298
|
+
if (this.type !== NTH_OF_SELECTOR) return null;
|
|
299
|
+
let first = this.first_child;
|
|
300
|
+
return first ? first.next_sibling : null;
|
|
301
|
+
}
|
|
302
|
+
// --- Pseudo-Class Selector List Helper ---
|
|
303
|
+
// Get selector list from pseudo-class functions
|
|
304
|
+
// Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
|
|
305
|
+
get selector_list() {
|
|
306
|
+
if (this.type !== PSEUDO_CLASS_SELECTOR) return null;
|
|
307
|
+
let child = this.first_child;
|
|
308
|
+
if (!child) return null;
|
|
309
|
+
if (child.type === SELECTOR_LIST) {
|
|
310
|
+
return child;
|
|
311
|
+
}
|
|
312
|
+
if (child.type === NTH_OF_SELECTOR) {
|
|
313
|
+
return child.selector;
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
// --- Compound Selector Helpers (for NODE_SELECTOR) ---
|
|
318
|
+
// Iterator over first compound selector parts (zero allocation)
|
|
319
|
+
// Yields parts before the first combinator
|
|
320
|
+
*compound_parts() {
|
|
321
|
+
if (this.type !== SELECTOR) return;
|
|
322
|
+
let child = this.first_child;
|
|
323
|
+
while (child) {
|
|
324
|
+
if (child.type === COMBINATOR) break;
|
|
325
|
+
yield child;
|
|
326
|
+
child = child.next_sibling;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Get first compound selector as array
|
|
330
|
+
// Returns array of parts before first combinator
|
|
331
|
+
get first_compound() {
|
|
332
|
+
if (this.type !== SELECTOR) return [];
|
|
333
|
+
let result = [];
|
|
334
|
+
let child = this.first_child;
|
|
335
|
+
while (child) {
|
|
336
|
+
if (child.type === COMBINATOR) break;
|
|
337
|
+
result.push(child);
|
|
338
|
+
child = child.next_sibling;
|
|
339
|
+
}
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
// Split selector into compound selectors
|
|
343
|
+
// Returns array of compound arrays split by combinators
|
|
344
|
+
get all_compounds() {
|
|
345
|
+
if (this.type !== SELECTOR) return [];
|
|
346
|
+
let compounds = [];
|
|
347
|
+
let current_compound = [];
|
|
348
|
+
let child = this.first_child;
|
|
349
|
+
while (child) {
|
|
350
|
+
if (child.type === COMBINATOR) {
|
|
351
|
+
if (current_compound.length > 0) {
|
|
352
|
+
compounds.push(current_compound);
|
|
353
|
+
current_compound = [];
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
current_compound.push(child);
|
|
357
|
+
}
|
|
358
|
+
child = child.next_sibling;
|
|
359
|
+
}
|
|
360
|
+
if (current_compound.length > 0) {
|
|
361
|
+
compounds.push(current_compound);
|
|
362
|
+
}
|
|
363
|
+
return compounds;
|
|
364
|
+
}
|
|
365
|
+
// Check if selector is compound (no combinators)
|
|
366
|
+
get is_compound() {
|
|
367
|
+
if (this.type !== SELECTOR) return false;
|
|
368
|
+
let child = this.first_child;
|
|
369
|
+
while (child) {
|
|
370
|
+
if (child.type === COMBINATOR) return false;
|
|
371
|
+
child = child.next_sibling;
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
// Get text of first compound selector (no node allocation)
|
|
376
|
+
get first_compound_text() {
|
|
377
|
+
if (this.type !== SELECTOR) return "";
|
|
378
|
+
let start = -1;
|
|
379
|
+
let end = -1;
|
|
380
|
+
let child = this.first_child;
|
|
381
|
+
while (child) {
|
|
382
|
+
if (child.type === COMBINATOR) break;
|
|
383
|
+
if (start === -1) start = child.offset;
|
|
384
|
+
end = child.offset + child.length;
|
|
385
|
+
child = child.next_sibling;
|
|
386
|
+
}
|
|
387
|
+
if (start === -1) return "";
|
|
388
|
+
return this.source.substring(start, end);
|
|
389
|
+
}
|
|
390
|
+
// --- Node Cloning ---
|
|
391
|
+
/**
|
|
392
|
+
* Clone this node as a mutable plain JavaScript object
|
|
393
|
+
*
|
|
394
|
+
* Extracts all properties from the arena into a plain object with children as an array.
|
|
395
|
+
* The resulting object can be freely modified.
|
|
396
|
+
*
|
|
397
|
+
* @param options - Cloning configuration
|
|
398
|
+
* @param options.deep - Recursively clone children (default: true)
|
|
399
|
+
* @param options.locations - Include line/column/offset/length (default: false)
|
|
400
|
+
* @returns Plain object with children as array
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* const ast = parse('div { color: red; }')
|
|
404
|
+
* const decl = ast.first_child.block.first_child
|
|
405
|
+
* const plain = decl.clone()
|
|
406
|
+
*
|
|
407
|
+
* // Access children as array
|
|
408
|
+
* plain.children.length
|
|
409
|
+
* plain.children[0]
|
|
410
|
+
* plain.children.push(newChild)
|
|
411
|
+
*/
|
|
412
|
+
clone(options = {}) {
|
|
413
|
+
const { deep = true, locations = false } = options;
|
|
414
|
+
let plain = {
|
|
415
|
+
type: this.type,
|
|
416
|
+
type_name: this.type_name,
|
|
417
|
+
text: this.text,
|
|
418
|
+
children: []
|
|
419
|
+
};
|
|
420
|
+
if (this.name) plain.name = this.name;
|
|
421
|
+
if (this.type === DECLARATION) plain.property = this.name;
|
|
422
|
+
if (this.value !== void 0 && this.value !== null) {
|
|
423
|
+
plain.value = this.value;
|
|
424
|
+
if (this.unit) plain.unit = this.unit;
|
|
425
|
+
}
|
|
426
|
+
if (this.type === AT_RULE && this.prelude) {
|
|
427
|
+
plain.prelude = this.prelude;
|
|
428
|
+
}
|
|
429
|
+
if (this.type === DECLARATION) plain.is_important = this.is_important;
|
|
430
|
+
plain.is_vendor_prefixed = this.is_vendor_prefixed;
|
|
431
|
+
plain.has_error = this.has_error;
|
|
432
|
+
if (this.type === ATTRIBUTE_SELECTOR) {
|
|
433
|
+
plain.attr_operator = this.attr_operator;
|
|
434
|
+
plain.attr_flags = this.attr_flags;
|
|
435
|
+
}
|
|
436
|
+
if (this.type === NTH_SELECTOR || this.type === NTH_OF_SELECTOR) {
|
|
437
|
+
plain.nth_a = this.nth_a;
|
|
438
|
+
plain.nth_b = this.nth_b;
|
|
439
|
+
}
|
|
440
|
+
if (locations) {
|
|
441
|
+
plain.line = this.line;
|
|
442
|
+
plain.column = this.column;
|
|
443
|
+
plain.offset = this.offset;
|
|
444
|
+
plain.length = this.length;
|
|
445
|
+
}
|
|
446
|
+
if (deep) {
|
|
447
|
+
for (let child of this.children) {
|
|
448
|
+
plain.children.push(child.clone({ deep: true, locations }));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return plain;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export { CSSNode, TYPE_NAMES };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export { type ParserOptions } from './parse';
|
|
|
8
8
|
export { CSSNode, type CSSNodeType, TYPE_NAMES, type CloneOptions, type PlainCSSNode } from './css-node';
|
|
9
9
|
export type { LexerPosition } from './lexer';
|
|
10
10
|
export { ATTR_OPERATOR_NONE, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_FLAG_NONE, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, } from './arena';
|
|
11
|
-
export
|
|
12
|
-
export
|
|
11
|
+
export * from './constants';
|
|
12
|
+
export * from './token-types';
|
package/dist/index.js
CHANGED
|
@@ -3,27 +3,7 @@ export { parse_selector } from './parse-selector.js';
|
|
|
3
3
|
export { parse_atrule_prelude } from './parse-atrule-prelude.js';
|
|
4
4
|
export { parse_value } from './parse-value.js';
|
|
5
5
|
export { tokenize } from './tokenize.js';
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
callback(node, depth);
|
|
11
|
-
let child = node.first_child;
|
|
12
|
-
while (child) {
|
|
13
|
-
walk(child, callback, depth + 1);
|
|
14
|
-
child = child.next_sibling;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
const NOOP = function() {
|
|
18
|
-
};
|
|
19
|
-
function traverse(node, { enter = NOOP, leave = NOOP } = {}) {
|
|
20
|
-
enter(node);
|
|
21
|
-
let child = node.first_child;
|
|
22
|
-
while (child) {
|
|
23
|
-
traverse(child, { enter, leave });
|
|
24
|
-
child = child.next_sibling;
|
|
25
|
-
}
|
|
26
|
-
leave(node);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export { traverse, walk };
|
|
6
|
+
export { traverse, walk } from './walk.js';
|
|
7
|
+
export { CSSNode, TYPE_NAMES } from './css-node.js';
|
|
8
|
+
export { ATTRIBUTE_SELECTOR, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_NONE, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, AT_RULE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FLAG_IMPORTANT, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PARENTHESIS, PRELUDE_OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, STRING, STYLESHEET, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, UNIVERSAL_SELECTOR, URL } from './arena.js';
|
|
9
|
+
export { TOKEN_AT_KEYWORD, TOKEN_BAD_STRING, TOKEN_BAD_URL, TOKEN_CDC, TOKEN_CDO, TOKEN_COLON, TOKEN_COMMA, TOKEN_COMMENT, TOKEN_DELIM, TOKEN_DIMENSION, TOKEN_EOF, TOKEN_FUNCTION, TOKEN_HASH, TOKEN_IDENT, TOKEN_LEFT_BRACE, TOKEN_LEFT_BRACKET, TOKEN_LEFT_PAREN, TOKEN_NUMBER, TOKEN_PERCENTAGE, TOKEN_RIGHT_BRACE, TOKEN_RIGHT_BRACKET, TOKEN_RIGHT_PAREN, TOKEN_SEMICOLON, TOKEN_STRING, TOKEN_URL, TOKEN_WHITESPACE } from './token-types.js';
|
package/dist/lexer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type TokenType } from './token-types';
|
|
2
2
|
export interface LexerPosition {
|
|
3
3
|
pos: number;
|
|
4
4
|
line: number;
|
|
@@ -9,39 +9,3 @@ export interface LexerPosition {
|
|
|
9
9
|
token_line: number;
|
|
10
10
|
token_column: number;
|
|
11
11
|
}
|
|
12
|
-
export declare class Lexer {
|
|
13
|
-
source: string;
|
|
14
|
-
pos: number;
|
|
15
|
-
line: number;
|
|
16
|
-
column: number;
|
|
17
|
-
skip_comments: boolean;
|
|
18
|
-
token_type: TokenType;
|
|
19
|
-
token_start: number;
|
|
20
|
-
token_end: number;
|
|
21
|
-
token_line: number;
|
|
22
|
-
token_column: number;
|
|
23
|
-
constructor(source: string, skip_comments?: boolean);
|
|
24
|
-
next_token_fast(skip_whitespace?: boolean): TokenType;
|
|
25
|
-
consume_whitespace(start_line: number, start_column: number): TokenType;
|
|
26
|
-
consume_comment(start_line: number, start_column: number): TokenType;
|
|
27
|
-
consume_string(quote: number, start_line: number, start_column: number): TokenType;
|
|
28
|
-
consume_hex_escape(): void;
|
|
29
|
-
consume_number(start_line: number, start_column: number): TokenType;
|
|
30
|
-
consume_ident_or_function(start_line: number, start_column: number): TokenType;
|
|
31
|
-
consume_at_keyword(start_line: number, start_column: number): TokenType;
|
|
32
|
-
consume_hash(start_line: number, start_column: number): TokenType;
|
|
33
|
-
advance(count?: number): void;
|
|
34
|
-
peek(offset?: number): number;
|
|
35
|
-
make_token(type: TokenType, start: number, end: number, line?: number, column?: number): TokenType;
|
|
36
|
-
next_token(skip_whitespace?: boolean): Token | null;
|
|
37
|
-
/**
|
|
38
|
-
* Save complete lexer state for backtracking
|
|
39
|
-
* @returns Object containing all lexer state
|
|
40
|
-
*/
|
|
41
|
-
save_position(): LexerPosition;
|
|
42
|
-
/**
|
|
43
|
-
* Restore lexer state from saved position
|
|
44
|
-
* @param saved The saved position to restore
|
|
45
|
-
*/
|
|
46
|
-
restore_position(saved: LexerPosition): void;
|
|
47
|
-
}
|
|
@@ -1,83 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
let CHAR_HEX = 1 << 2;
|
|
4
|
-
let CHAR_WHITESPACE = 1 << 3;
|
|
5
|
-
let CHAR_NEWLINE = 1 << 4;
|
|
6
|
-
let char_types = new Uint8Array(128);
|
|
7
|
-
for (let i = 48; i <= 57; i++) {
|
|
8
|
-
char_types[i] = CHAR_DIGIT;
|
|
9
|
-
}
|
|
10
|
-
for (let i = 48; i <= 57; i++) {
|
|
11
|
-
char_types[i] |= CHAR_HEX;
|
|
12
|
-
}
|
|
13
|
-
for (let i = 65; i <= 70; i++) {
|
|
14
|
-
char_types[i] = CHAR_HEX;
|
|
15
|
-
}
|
|
16
|
-
for (let i = 97; i <= 102; i++) {
|
|
17
|
-
char_types[i] = CHAR_HEX;
|
|
18
|
-
}
|
|
19
|
-
for (let i = 65; i <= 90; i++) {
|
|
20
|
-
char_types[i] |= CHAR_ALPHA;
|
|
21
|
-
}
|
|
22
|
-
for (let i = 97; i <= 122; i++) {
|
|
23
|
-
char_types[i] |= CHAR_ALPHA;
|
|
24
|
-
}
|
|
25
|
-
char_types[32] = CHAR_WHITESPACE;
|
|
26
|
-
char_types[9] = CHAR_WHITESPACE;
|
|
27
|
-
char_types[10] = CHAR_NEWLINE;
|
|
28
|
-
char_types[13] = CHAR_NEWLINE;
|
|
29
|
-
char_types[12] = CHAR_NEWLINE;
|
|
30
|
-
function is_digit(ch) {
|
|
31
|
-
return ch < 128 && (char_types[ch] & CHAR_DIGIT) !== 0;
|
|
32
|
-
}
|
|
33
|
-
function is_hex_digit(ch) {
|
|
34
|
-
return ch < 128 && (char_types[ch] & CHAR_HEX) !== 0;
|
|
35
|
-
}
|
|
36
|
-
function is_alpha(ch) {
|
|
37
|
-
return ch < 128 && (char_types[ch] & CHAR_ALPHA) !== 0;
|
|
38
|
-
}
|
|
39
|
-
function is_whitespace(ch) {
|
|
40
|
-
return ch < 128 && (char_types[ch] & CHAR_WHITESPACE) !== 0;
|
|
41
|
-
}
|
|
42
|
-
function is_newline(ch) {
|
|
43
|
-
return ch < 128 && (char_types[ch] & CHAR_NEWLINE) !== 0;
|
|
44
|
-
}
|
|
45
|
-
function is_ident_start(ch) {
|
|
46
|
-
if (ch >= 128) return true;
|
|
47
|
-
if (ch === 95) return true;
|
|
48
|
-
return is_alpha(ch);
|
|
49
|
-
}
|
|
50
|
-
function is_ident_char(ch) {
|
|
51
|
-
if (ch === 45) return true;
|
|
52
|
-
return is_ident_start(ch) || is_digit(ch);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const TOKEN_IDENT = 1;
|
|
56
|
-
const TOKEN_FUNCTION = 2;
|
|
57
|
-
const TOKEN_AT_KEYWORD = 3;
|
|
58
|
-
const TOKEN_HASH = 4;
|
|
59
|
-
const TOKEN_STRING = 5;
|
|
60
|
-
const TOKEN_BAD_STRING = 6;
|
|
61
|
-
const TOKEN_URL = 7;
|
|
62
|
-
const TOKEN_BAD_URL = 8;
|
|
63
|
-
const TOKEN_DELIM = 9;
|
|
64
|
-
const TOKEN_NUMBER = 10;
|
|
65
|
-
const TOKEN_PERCENTAGE = 11;
|
|
66
|
-
const TOKEN_DIMENSION = 12;
|
|
67
|
-
const TOKEN_WHITESPACE = 13;
|
|
68
|
-
const TOKEN_CDO = 14;
|
|
69
|
-
const TOKEN_CDC = 15;
|
|
70
|
-
const TOKEN_COLON = 16;
|
|
71
|
-
const TOKEN_SEMICOLON = 17;
|
|
72
|
-
const TOKEN_COMMA = 18;
|
|
73
|
-
const TOKEN_LEFT_BRACKET = 19;
|
|
74
|
-
const TOKEN_RIGHT_BRACKET = 20;
|
|
75
|
-
const TOKEN_LEFT_PAREN = 21;
|
|
76
|
-
const TOKEN_RIGHT_PAREN = 22;
|
|
77
|
-
const TOKEN_LEFT_BRACE = 23;
|
|
78
|
-
const TOKEN_RIGHT_BRACE = 24;
|
|
79
|
-
const TOKEN_COMMENT = 25;
|
|
80
|
-
const TOKEN_EOF = 26;
|
|
1
|
+
import { is_whitespace, is_newline, is_digit, is_ident_start, is_hex_digit, is_ident_char } from './char-types.js';
|
|
2
|
+
import { TOKEN_EOF, TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE, TOKEN_COLON, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_CDO, TOKEN_CDC, TOKEN_DELIM, TOKEN_WHITESPACE, TOKEN_COMMENT, TOKEN_STRING, TOKEN_BAD_STRING, TOKEN_PERCENTAGE, TOKEN_DIMENSION, TOKEN_NUMBER, TOKEN_FUNCTION, TOKEN_IDENT, TOKEN_AT_KEYWORD, TOKEN_HASH } from './token-types.js';
|
|
81
3
|
|
|
82
4
|
const CHAR_LEFT_BRACE = 123;
|
|
83
5
|
const CHAR_RIGHT_BRACE = 125;
|
|
@@ -506,4 +428,4 @@ class Lexer {
|
|
|
506
428
|
}
|
|
507
429
|
}
|
|
508
430
|
|
|
509
|
-
export { Lexer
|
|
431
|
+
export { Lexer };
|
package/dist/parse-anplusb.d.ts
CHANGED
|
@@ -1,22 +1,2 @@
|
|
|
1
|
-
import { CSSDataArena } from './arena';
|
|
2
1
|
import { CSSNode } from './css-node';
|
|
3
|
-
export declare class ANplusBParser {
|
|
4
|
-
private lexer;
|
|
5
|
-
private arena;
|
|
6
|
-
private source;
|
|
7
|
-
private expr_end;
|
|
8
|
-
constructor(arena: CSSDataArena, source: string);
|
|
9
|
-
/**
|
|
10
|
-
* Parse An+B expression
|
|
11
|
-
* Examples: odd, even, 3, n, -n, 2n, 2n+1, -3n-5
|
|
12
|
-
*/
|
|
13
|
-
parse_anplusb(start: number, end: number, line?: number): number | null;
|
|
14
|
-
/**
|
|
15
|
-
* Parse the b part after 'n'
|
|
16
|
-
* Handles: +5, -3, whitespace variations
|
|
17
|
-
*/
|
|
18
|
-
private parse_b_part;
|
|
19
|
-
private skip_whitespace;
|
|
20
|
-
private create_anplusb_node;
|
|
21
|
-
}
|
|
22
2
|
export declare function parse_anplusb(expr: string): CSSNode | null;
|