@projectwallace/css-parser 0.13.4 → 0.13.6
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/css-node-2ejJUrIw.js +645 -0
- package/dist/css-node-DqyvMXBN.d.ts +313 -0
- package/dist/index.d.ts +150 -16
- package/dist/index.js +103 -13
- package/dist/parse-anplusb.d.ts +26 -2
- package/dist/parse-anplusb.js +191 -207
- package/dist/parse-atrule-prelude.d.ts +40 -2
- package/dist/parse-atrule-prelude.js +556 -652
- package/dist/parse-declaration.d.ts +16 -2
- package/dist/parse-declaration.js +140 -164
- package/dist/parse-dimension-CCn_XRDe.js +177 -0
- package/dist/parse-dimension.d.ts +6 -3
- package/dist/parse-dimension.js +1 -35
- package/dist/parse-selector.d.ts +37 -2
- package/dist/parse-selector.js +508 -635
- package/dist/parse-utils-DnsZRpfd.js +98 -0
- package/dist/parse-value.d.ts +23 -2
- package/dist/parse-value.js +176 -224
- package/dist/parse.d.ts +38 -8
- package/dist/parse.js +274 -350
- package/dist/tokenize-BQFB1jXg.js +540 -0
- package/dist/tokenize-odLrcjj2.d.ts +110 -0
- package/dist/tokenize.d.ts +2 -26
- package/dist/tokenize.js +1 -545
- package/package.json +20 -26
- package/dist/arena.d.ts +0 -59
- package/dist/arena.js +0 -290
- package/dist/char-types.d.ts +0 -14
- package/dist/char-types.js +0 -53
- package/dist/constants.d.ts +0 -43
- package/dist/constants.js +0 -50
- package/dist/css-node.d.ts +0 -202
- package/dist/css-node.js +0 -497
- package/dist/parse-utils.d.ts +0 -1
- package/dist/parse-utils.js +0 -60
- package/dist/string-utils.d.ts +0 -99
- package/dist/string-utils.js +0 -129
- package/dist/token-types.d.ts +0 -35
- package/dist/token-types.js +0 -29
- package/dist/walk.d.ts +0 -28
- package/dist/walk.js +0 -51
package/dist/css-node.js
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
import { DECLARATION, OPERATOR, SELECTOR, MEDIA_FEATURE, DIMENSION, NUMBER, URL, STRING, AT_RULE, AT_RULE_PRELUDE, STYLE_RULE, ATTRIBUTE_SELECTOR, FLAG_IMPORTANT, FLAG_BROWSERHACK, IDENTIFIER, FUNCTION, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, FLAG_HAS_ERROR, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, BLOCK, COMMENT, FLAG_HAS_PARENS, NTH_SELECTOR, NTH_OF_SELECTOR, SELECTOR_LIST, FEATURE_RANGE, PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_QUERY, LANG_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, COMBINATOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, VALUE, UNICODE_RANGE, PARENTHESIS, HASH, STYLESHEET, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_NONE } from './arena.js';
|
|
2
|
-
import { str_starts_with, is_vendor_prefixed, is_whitespace, CHAR_MINUS_HYPHEN, CHAR_PLUS } from './string-utils.js';
|
|
3
|
-
import { parse_dimension } from './parse-dimension.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
|
-
[UNICODE_RANGE]: "UnicodeRange",
|
|
23
|
-
[VALUE]: "Value",
|
|
24
|
-
[SELECTOR_LIST]: "SelectorList",
|
|
25
|
-
[TYPE_SELECTOR]: "TypeSelector",
|
|
26
|
-
[CLASS_SELECTOR]: "ClassSelector",
|
|
27
|
-
[ID_SELECTOR]: "IdSelector",
|
|
28
|
-
[ATTRIBUTE_SELECTOR]: "AttributeSelector",
|
|
29
|
-
[PSEUDO_CLASS_SELECTOR]: "PseudoClassSelector",
|
|
30
|
-
[PSEUDO_ELEMENT_SELECTOR]: "PseudoElementSelector",
|
|
31
|
-
[COMBINATOR]: "Combinator",
|
|
32
|
-
[UNIVERSAL_SELECTOR]: "UniversalSelector",
|
|
33
|
-
[NESTING_SELECTOR]: "NestingSelector",
|
|
34
|
-
[NTH_SELECTOR]: "Nth",
|
|
35
|
-
[NTH_OF_SELECTOR]: "NthOf",
|
|
36
|
-
[LANG_SELECTOR]: "Lang",
|
|
37
|
-
[MEDIA_QUERY]: "MediaQuery",
|
|
38
|
-
[MEDIA_FEATURE]: "Feature",
|
|
39
|
-
[MEDIA_TYPE]: "MediaType",
|
|
40
|
-
[CONTAINER_QUERY]: "ContainerQuery",
|
|
41
|
-
[SUPPORTS_QUERY]: "SupportsQuery",
|
|
42
|
-
[LAYER_NAME]: "Layer",
|
|
43
|
-
[PRELUDE_OPERATOR]: "Operator",
|
|
44
|
-
[FEATURE_RANGE]: "MediaFeatureRange",
|
|
45
|
-
[AT_RULE_PRELUDE]: "AtrulePrelude"
|
|
46
|
-
};
|
|
47
|
-
const ATTR_OPERATOR_NAMES = {
|
|
48
|
-
[ATTR_OPERATOR_NONE]: null,
|
|
49
|
-
[ATTR_OPERATOR_EQUAL]: "=",
|
|
50
|
-
[ATTR_OPERATOR_TILDE_EQUAL]: "~=",
|
|
51
|
-
[ATTR_OPERATOR_PIPE_EQUAL]: "|=",
|
|
52
|
-
[ATTR_OPERATOR_CARET_EQUAL]: "^=",
|
|
53
|
-
[ATTR_OPERATOR_DOLLAR_EQUAL]: "$=",
|
|
54
|
-
[ATTR_OPERATOR_STAR_EQUAL]: "*="
|
|
55
|
-
};
|
|
56
|
-
const ATTR_FLAG_NAMES = {
|
|
57
|
-
[ATTR_FLAG_NONE]: null,
|
|
58
|
-
[ATTR_FLAG_CASE_INSENSITIVE]: "i",
|
|
59
|
-
[ATTR_FLAG_CASE_SENSITIVE]: "s"
|
|
60
|
-
};
|
|
61
|
-
class CSSNode {
|
|
62
|
-
arena;
|
|
63
|
-
source;
|
|
64
|
-
index;
|
|
65
|
-
constructor(arena, source, index) {
|
|
66
|
-
this.arena = arena;
|
|
67
|
-
this.source = source;
|
|
68
|
-
this.index = index;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* @internal
|
|
72
|
-
* Get the arena (for internal/advanced use only)
|
|
73
|
-
*/
|
|
74
|
-
__get_arena() {
|
|
75
|
-
return this.arena;
|
|
76
|
-
}
|
|
77
|
-
get_content() {
|
|
78
|
-
let start = this.arena.get_content_start(this.index);
|
|
79
|
-
let length = this.arena.get_content_length(this.index);
|
|
80
|
-
if (length === 0) return "";
|
|
81
|
-
return this.source.substring(start, start + length);
|
|
82
|
-
}
|
|
83
|
-
/** Get node type as number (for performance) */
|
|
84
|
-
get type() {
|
|
85
|
-
return this.arena.get_type(this.index);
|
|
86
|
-
}
|
|
87
|
-
/** Get node type as human-readable string */
|
|
88
|
-
get type_name() {
|
|
89
|
-
return TYPE_NAMES[this.type] || "unknown";
|
|
90
|
-
}
|
|
91
|
-
/** Get the full text of this node from source */
|
|
92
|
-
get text() {
|
|
93
|
-
let start = this.arena.get_start_offset(this.index);
|
|
94
|
-
let length = this.arena.get_length(this.index);
|
|
95
|
-
return this.source.substring(start, start + length);
|
|
96
|
-
}
|
|
97
|
-
/** Get the "content" text (at-rule name for at-rules, layer name for import layers) */
|
|
98
|
-
get name() {
|
|
99
|
-
let { type } = this;
|
|
100
|
-
if (type === DECLARATION || type === OPERATOR || type === SELECTOR) return;
|
|
101
|
-
return this.get_content();
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Alias for name (for declarations: "color" in "color: blue")
|
|
105
|
-
* More semantic than `name` for declaration nodes
|
|
106
|
-
*/
|
|
107
|
-
get property() {
|
|
108
|
-
let { type } = this;
|
|
109
|
-
if (type !== DECLARATION && type !== MEDIA_FEATURE) return;
|
|
110
|
-
return this.get_content();
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Get the value text (for declarations: "blue" in "color: blue")
|
|
114
|
-
* For dimension/number nodes: returns the numeric value as a number
|
|
115
|
-
* For string nodes: returns the string content without quotes
|
|
116
|
-
* For URL nodes with quoted string: returns the string with quotes (consistent with STRING node)
|
|
117
|
-
* For URL nodes with unquoted URL: returns the URL content without quotes
|
|
118
|
-
*/
|
|
119
|
-
get value() {
|
|
120
|
-
let { type, text, first_child } = this;
|
|
121
|
-
if (type === DECLARATION && first_child) {
|
|
122
|
-
return first_child;
|
|
123
|
-
}
|
|
124
|
-
if (type === DIMENSION) {
|
|
125
|
-
return parse_dimension(text).value;
|
|
126
|
-
}
|
|
127
|
-
if (type === NUMBER) {
|
|
128
|
-
return Number.parseFloat(text);
|
|
129
|
-
}
|
|
130
|
-
if (type === URL) {
|
|
131
|
-
if (first_child?.type === STRING) {
|
|
132
|
-
return first_child.text;
|
|
133
|
-
}
|
|
134
|
-
if (str_starts_with(text, "url(")) {
|
|
135
|
-
let open_paren = text.indexOf("(");
|
|
136
|
-
let close_paren = text.lastIndexOf(")");
|
|
137
|
-
if (open_paren !== -1 && close_paren !== -1 && close_paren > open_paren) {
|
|
138
|
-
let content = text.substring(open_paren + 1, close_paren).trim();
|
|
139
|
-
return content;
|
|
140
|
-
}
|
|
141
|
-
} else if (text.startsWith('"') || text.startsWith("'")) {
|
|
142
|
-
return text;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (type === OPERATOR) {
|
|
146
|
-
return this.get_content();
|
|
147
|
-
}
|
|
148
|
-
let start = this.arena.get_value_start(this.index);
|
|
149
|
-
let length = this.arena.get_value_length(this.index);
|
|
150
|
-
if (length === 0) return null;
|
|
151
|
-
return this.source.substring(start, start + length);
|
|
152
|
-
}
|
|
153
|
-
/** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
|
|
154
|
-
get value_as_number() {
|
|
155
|
-
let { text, type } = this;
|
|
156
|
-
if (type === NUMBER) {
|
|
157
|
-
return Number.parseFloat(text);
|
|
158
|
-
}
|
|
159
|
-
if (type === DIMENSION) {
|
|
160
|
-
return parse_dimension(text).value;
|
|
161
|
-
}
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Get the prelude node:
|
|
166
|
-
* - For at-rules: AT_RULE_PRELUDE wrapper containing structured prelude children (media queries, layer names, etc.)
|
|
167
|
-
* - For style rules: SELECTOR_LIST or SELECTOR node
|
|
168
|
-
* Returns null if no prelude exists
|
|
169
|
-
*/
|
|
170
|
-
get prelude() {
|
|
171
|
-
if (this.type === AT_RULE) {
|
|
172
|
-
let first = this.first_child;
|
|
173
|
-
if (first && first.type === AT_RULE_PRELUDE) {
|
|
174
|
-
return first;
|
|
175
|
-
}
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
if (this.type === STYLE_RULE) {
|
|
179
|
-
return this.first_child;
|
|
180
|
-
}
|
|
181
|
-
return void 0;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
|
|
185
|
-
* Returns one of the ATTR_OPERATOR_* constants
|
|
186
|
-
*/
|
|
187
|
-
get attr_operator() {
|
|
188
|
-
if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
|
|
189
|
-
return this.arena.get_attr_operator(this.index);
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Get the attribute flags (for attribute selectors: i, s)
|
|
193
|
-
* Returns one of the ATTR_FLAG_* constants
|
|
194
|
-
*/
|
|
195
|
-
get attr_flags() {
|
|
196
|
-
if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
|
|
197
|
-
return this.arena.get_attr_flags(this.index);
|
|
198
|
-
}
|
|
199
|
-
/** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
|
|
200
|
-
get unit() {
|
|
201
|
-
if (this.type !== DIMENSION) return void 0;
|
|
202
|
-
return parse_dimension(this.text).unit;
|
|
203
|
-
}
|
|
204
|
-
/** Check if this declaration has !important */
|
|
205
|
-
get is_important() {
|
|
206
|
-
if (this.type !== DECLARATION) return void 0;
|
|
207
|
-
return this.arena.has_flag(this.index, FLAG_IMPORTANT);
|
|
208
|
-
}
|
|
209
|
-
/** Check if this declaration has a browser hack prefix */
|
|
210
|
-
get is_browserhack() {
|
|
211
|
-
if (this.type !== DECLARATION) return void 0;
|
|
212
|
-
return this.arena.has_flag(this.index, FLAG_BROWSERHACK);
|
|
213
|
-
}
|
|
214
|
-
/** Check if this has a vendor prefix (computed on-demand) */
|
|
215
|
-
get is_vendor_prefixed() {
|
|
216
|
-
switch (this.type) {
|
|
217
|
-
case DECLARATION:
|
|
218
|
-
return is_vendor_prefixed(this.get_content());
|
|
219
|
-
case PSEUDO_CLASS_SELECTOR:
|
|
220
|
-
case PSEUDO_ELEMENT_SELECTOR:
|
|
221
|
-
return is_vendor_prefixed(this.get_content());
|
|
222
|
-
case AT_RULE:
|
|
223
|
-
return is_vendor_prefixed(this.get_content());
|
|
224
|
-
case FUNCTION:
|
|
225
|
-
return is_vendor_prefixed(this.get_content());
|
|
226
|
-
case IDENTIFIER:
|
|
227
|
-
return is_vendor_prefixed(this.text);
|
|
228
|
-
default:
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
/** Check if this node has an error */
|
|
233
|
-
get has_error() {
|
|
234
|
-
return this.arena.has_flag(this.index, FLAG_HAS_ERROR);
|
|
235
|
-
}
|
|
236
|
-
/** Check if this node has a prelude (at-rules and style rules) */
|
|
237
|
-
get has_prelude() {
|
|
238
|
-
let { type } = this;
|
|
239
|
-
if (type === AT_RULE) {
|
|
240
|
-
return this.arena.get_value_length(this.index) > 0;
|
|
241
|
-
}
|
|
242
|
-
if (type === STYLE_RULE) {
|
|
243
|
-
return this.first_child !== null;
|
|
244
|
-
}
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
/** Check if this rule has a block { } */
|
|
248
|
-
get has_block() {
|
|
249
|
-
return this.arena.has_flag(this.index, FLAG_HAS_BLOCK);
|
|
250
|
-
}
|
|
251
|
-
/** Check if this style rule has declarations */
|
|
252
|
-
get has_declarations() {
|
|
253
|
-
return this.arena.has_flag(this.index, FLAG_HAS_DECLARATIONS);
|
|
254
|
-
}
|
|
255
|
-
/** Get the block node (for style rules and at-rules with blocks) */
|
|
256
|
-
get block() {
|
|
257
|
-
let { type } = this;
|
|
258
|
-
if (type === STYLE_RULE) {
|
|
259
|
-
let first = this.first_child;
|
|
260
|
-
if (!first) return null;
|
|
261
|
-
let block_node = first.next_sibling;
|
|
262
|
-
if (block_node?.type === BLOCK) {
|
|
263
|
-
return block_node;
|
|
264
|
-
}
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
if (type === AT_RULE) {
|
|
268
|
-
let child = this.first_child;
|
|
269
|
-
while (child) {
|
|
270
|
-
if (child.type === BLOCK && !child.next_sibling) {
|
|
271
|
-
return child;
|
|
272
|
-
}
|
|
273
|
-
child = child.next_sibling;
|
|
274
|
-
}
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
/** Check if this block is empty (no declarations or rules, only comments allowed) */
|
|
280
|
-
get is_empty() {
|
|
281
|
-
if (this.type !== BLOCK) return void 0;
|
|
282
|
-
let child = this.first_child;
|
|
283
|
-
while (child) {
|
|
284
|
-
if (child.type !== COMMENT) {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
child = child.next_sibling;
|
|
288
|
-
}
|
|
289
|
-
return true;
|
|
290
|
-
}
|
|
291
|
-
/** Get start line number */
|
|
292
|
-
get line() {
|
|
293
|
-
return this.arena.get_start_line(this.index);
|
|
294
|
-
}
|
|
295
|
-
/** Get start column number */
|
|
296
|
-
get column() {
|
|
297
|
-
return this.arena.get_start_column(this.index);
|
|
298
|
-
}
|
|
299
|
-
/** Get start offset in source */
|
|
300
|
-
get start() {
|
|
301
|
-
return this.arena.get_start_offset(this.index);
|
|
302
|
-
}
|
|
303
|
-
/** Get length in source */
|
|
304
|
-
get length() {
|
|
305
|
-
return this.arena.get_length(this.index);
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Get end offset in source
|
|
309
|
-
* End is not stored, must be calculated
|
|
310
|
-
*/
|
|
311
|
-
get end() {
|
|
312
|
-
return this.start + this.length;
|
|
313
|
-
}
|
|
314
|
-
// --- Tree Traversal ---
|
|
315
|
-
/** Get first child node */
|
|
316
|
-
get first_child() {
|
|
317
|
-
let child_index = this.arena.get_first_child(this.index);
|
|
318
|
-
if (child_index === 0) return null;
|
|
319
|
-
return new CSSNode(this.arena, this.source, child_index);
|
|
320
|
-
}
|
|
321
|
-
/** Get next sibling node */
|
|
322
|
-
get next_sibling() {
|
|
323
|
-
let sibling_index = this.arena.get_next_sibling(this.index);
|
|
324
|
-
if (sibling_index === 0) return null;
|
|
325
|
-
return new CSSNode(this.arena, this.source, sibling_index);
|
|
326
|
-
}
|
|
327
|
-
/** Check if this node has a next sibling */
|
|
328
|
-
get has_next() {
|
|
329
|
-
let sibling_index = this.arena.get_next_sibling(this.index);
|
|
330
|
-
return sibling_index !== 0;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Check if this node has children
|
|
334
|
-
* For pseudo-class/pseudo-element functions, returns true if FLAG_HAS_PARENS is set
|
|
335
|
-
* This allows formatters to distinguish :lang() from :hover
|
|
336
|
-
*/
|
|
337
|
-
get has_children() {
|
|
338
|
-
let { type } = this;
|
|
339
|
-
if (type === PSEUDO_CLASS_SELECTOR || type === PSEUDO_ELEMENT_SELECTOR) {
|
|
340
|
-
if (this.arena.has_flag(this.index, FLAG_HAS_PARENS)) {
|
|
341
|
-
return true;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return this.arena.has_children(this.index);
|
|
345
|
-
}
|
|
346
|
-
/** Get all children as an array */
|
|
347
|
-
get children() {
|
|
348
|
-
let result = [];
|
|
349
|
-
let child = this.first_child;
|
|
350
|
-
while (child) {
|
|
351
|
-
result.push(child);
|
|
352
|
-
child = child.next_sibling;
|
|
353
|
-
}
|
|
354
|
-
return result;
|
|
355
|
-
}
|
|
356
|
-
/** Make CSSNode iterable over its children */
|
|
357
|
-
*[Symbol.iterator]() {
|
|
358
|
-
let child = this.first_child;
|
|
359
|
-
while (child) {
|
|
360
|
-
yield child;
|
|
361
|
-
child = child.next_sibling;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
// --- An+B Expression Helpers (for NODE_SELECTOR_NTH) ---
|
|
365
|
-
/** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
|
|
366
|
-
get nth_a() {
|
|
367
|
-
let { type, arena, index } = this;
|
|
368
|
-
if (type !== NTH_SELECTOR && type !== NTH_OF_SELECTOR) return void 0;
|
|
369
|
-
let len = arena.get_content_length(index);
|
|
370
|
-
if (len === 0) return void 0;
|
|
371
|
-
let start = arena.get_content_start(index);
|
|
372
|
-
return this.source.substring(start, start + len);
|
|
373
|
-
}
|
|
374
|
-
/** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
|
|
375
|
-
get nth_b() {
|
|
376
|
-
let { type, arena, index, source } = this;
|
|
377
|
-
if (type !== NTH_SELECTOR && type !== NTH_OF_SELECTOR) return void 0;
|
|
378
|
-
let len = arena.get_value_length(index);
|
|
379
|
-
if (len === 0) return void 0;
|
|
380
|
-
let start = arena.get_value_start(index);
|
|
381
|
-
let value = source.substring(start, start + len);
|
|
382
|
-
let check_pos = start - 1;
|
|
383
|
-
while (check_pos >= 0) {
|
|
384
|
-
let ch = source.charCodeAt(check_pos);
|
|
385
|
-
if (is_whitespace(ch)) {
|
|
386
|
-
check_pos--;
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
if (ch === CHAR_MINUS_HYPHEN) {
|
|
390
|
-
value = "-" + value;
|
|
391
|
-
} else if (ch === CHAR_PLUS) {
|
|
392
|
-
value = "+" + value;
|
|
393
|
-
}
|
|
394
|
-
break;
|
|
395
|
-
}
|
|
396
|
-
return value;
|
|
397
|
-
}
|
|
398
|
-
// --- Pseudo-Class Nth-Of Helpers (for NODE_SELECTOR_NTH_OF) ---
|
|
399
|
-
/** Get the An+B formula node from :nth-child(2n+1 of .foo) */
|
|
400
|
-
get nth() {
|
|
401
|
-
if (this.type !== NTH_OF_SELECTOR) return void 0;
|
|
402
|
-
return this.first_child ?? void 0;
|
|
403
|
-
}
|
|
404
|
-
/** Get the selector list from :nth-child(2n+1 of .foo) */
|
|
405
|
-
get selector() {
|
|
406
|
-
if (this.type !== NTH_OF_SELECTOR) return void 0;
|
|
407
|
-
let first = this.first_child;
|
|
408
|
-
return first?.next_sibling ?? void 0;
|
|
409
|
-
}
|
|
410
|
-
// --- Pseudo-Class Selector List Helper ---
|
|
411
|
-
/**
|
|
412
|
-
* Get selector list from pseudo-class functions
|
|
413
|
-
* Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
|
|
414
|
-
*/
|
|
415
|
-
get selector_list() {
|
|
416
|
-
if (this.type !== PSEUDO_CLASS_SELECTOR) return void 0;
|
|
417
|
-
let child = this.first_child;
|
|
418
|
-
if (!child) return void 0;
|
|
419
|
-
if (child.type === SELECTOR_LIST) {
|
|
420
|
-
return child;
|
|
421
|
-
}
|
|
422
|
-
if (child.type === NTH_OF_SELECTOR) {
|
|
423
|
-
return child.selector;
|
|
424
|
-
}
|
|
425
|
-
return void 0;
|
|
426
|
-
}
|
|
427
|
-
// --- Node Cloning ---
|
|
428
|
-
/**
|
|
429
|
-
* Clone this node as a mutable plain JavaScript object with children as arrays.
|
|
430
|
-
* See API.md for examples.
|
|
431
|
-
*
|
|
432
|
-
* @param options - Cloning configuration
|
|
433
|
-
* @param options.deep - Recursively clone children (default: true)
|
|
434
|
-
* @param options.locations - Include line/column/start/length (default: false)
|
|
435
|
-
*/
|
|
436
|
-
clone(options = {}) {
|
|
437
|
-
const { deep = true, locations = false } = options;
|
|
438
|
-
let { type, name, property, value, unit } = this;
|
|
439
|
-
let plain = {
|
|
440
|
-
type,
|
|
441
|
-
type_name: this.type_name,
|
|
442
|
-
text: this.text
|
|
443
|
-
};
|
|
444
|
-
if (name) {
|
|
445
|
-
plain.name = name;
|
|
446
|
-
}
|
|
447
|
-
if (property) {
|
|
448
|
-
plain.property = property;
|
|
449
|
-
}
|
|
450
|
-
if (value) {
|
|
451
|
-
plain.value = value;
|
|
452
|
-
if (unit) {
|
|
453
|
-
plain.unit = unit;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
if (type === DECLARATION) {
|
|
457
|
-
let { is_important, is_browserhack } = this;
|
|
458
|
-
if (is_important) {
|
|
459
|
-
plain.is_important = true;
|
|
460
|
-
}
|
|
461
|
-
if (is_browserhack) {
|
|
462
|
-
plain.is_browserhack = true;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
let { is_vendor_prefixed: is_vendor_prefixed2, has_error } = this;
|
|
466
|
-
if (is_vendor_prefixed2) {
|
|
467
|
-
plain.is_vendor_prefixed = true;
|
|
468
|
-
}
|
|
469
|
-
if (has_error) {
|
|
470
|
-
plain.has_error = true;
|
|
471
|
-
}
|
|
472
|
-
if (type === ATTRIBUTE_SELECTOR) {
|
|
473
|
-
plain.attr_operator = ATTR_OPERATOR_NAMES[this.attr_operator];
|
|
474
|
-
plain.attr_flags = ATTR_FLAG_NAMES[this.attr_flags];
|
|
475
|
-
}
|
|
476
|
-
if (type === NTH_SELECTOR || type === NTH_OF_SELECTOR) {
|
|
477
|
-
plain.nth_a = this.nth_a;
|
|
478
|
-
plain.nth_b = this.nth_b;
|
|
479
|
-
}
|
|
480
|
-
if (locations) {
|
|
481
|
-
plain.line = this.line;
|
|
482
|
-
plain.column = this.column;
|
|
483
|
-
plain.start = this.start;
|
|
484
|
-
plain.length = this.length;
|
|
485
|
-
plain.end = this.end;
|
|
486
|
-
}
|
|
487
|
-
if (deep && type !== DECLARATION) {
|
|
488
|
-
plain.children = [];
|
|
489
|
-
for (let child of this.children) {
|
|
490
|
-
plain.children.push(child.clone({ deep: true, locations }));
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return plain;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
export { ATTR_FLAG_NAMES, ATTR_OPERATOR_NAMES, CSSNode, TYPE_NAMES };
|
package/dist/parse-utils.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/parse-utils.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { is_whitespace, CHAR_FORWARD_SLASH, CHAR_ASTERISK } from './string-utils.js';
|
|
2
|
-
|
|
3
|
-
function skip_whitespace_forward(source, pos, end) {
|
|
4
|
-
while (pos < end && is_whitespace(source.charCodeAt(pos))) {
|
|
5
|
-
pos++;
|
|
6
|
-
}
|
|
7
|
-
return pos;
|
|
8
|
-
}
|
|
9
|
-
function skip_whitespace_and_comments_forward(source, pos, end) {
|
|
10
|
-
while (pos < end) {
|
|
11
|
-
let ch = source.charCodeAt(pos);
|
|
12
|
-
if (is_whitespace(ch)) {
|
|
13
|
-
pos++;
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
if (ch === CHAR_FORWARD_SLASH && pos + 1 < end && source.charCodeAt(pos + 1) === CHAR_ASTERISK) {
|
|
17
|
-
pos += 2;
|
|
18
|
-
while (pos < end) {
|
|
19
|
-
if (source.charCodeAt(pos) === CHAR_ASTERISK && pos + 1 < end && source.charCodeAt(pos + 1) === CHAR_FORWARD_SLASH) {
|
|
20
|
-
pos += 2;
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
pos++;
|
|
24
|
-
}
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
return pos;
|
|
30
|
-
}
|
|
31
|
-
function skip_whitespace_and_comments_backward(source, pos, start) {
|
|
32
|
-
while (pos > start) {
|
|
33
|
-
let ch = source.charCodeAt(pos - 1);
|
|
34
|
-
if (is_whitespace(ch)) {
|
|
35
|
-
pos--;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (pos >= 2 && ch === CHAR_FORWARD_SLASH && source.charCodeAt(pos - 2) === CHAR_ASTERISK) {
|
|
39
|
-
pos -= 2;
|
|
40
|
-
while (pos > start) {
|
|
41
|
-
if (pos >= 2 && source.charCodeAt(pos - 2) === CHAR_FORWARD_SLASH && source.charCodeAt(pos - 1) === CHAR_ASTERISK) {
|
|
42
|
-
pos -= 2;
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
pos--;
|
|
46
|
-
}
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
return pos;
|
|
52
|
-
}
|
|
53
|
-
function trim_boundaries(source, start, end) {
|
|
54
|
-
start = skip_whitespace_and_comments_forward(source, start, end);
|
|
55
|
-
end = skip_whitespace_and_comments_backward(source, end, start);
|
|
56
|
-
if (start >= end) return null;
|
|
57
|
-
return [start, end];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export { skip_whitespace_and_comments_backward, skip_whitespace_and_comments_forward, skip_whitespace_forward, trim_boundaries };
|
package/dist/string-utils.d.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
export declare const CHAR_SPACE = 32;
|
|
2
|
-
export declare const CHAR_TAB = 9;
|
|
3
|
-
export declare const CHAR_NEWLINE = 10;
|
|
4
|
-
export declare const CHAR_CARRIAGE_RETURN = 13;
|
|
5
|
-
export declare const CHAR_FORM_FEED = 12;
|
|
6
|
-
export declare const CHAR_FORWARD_SLASH = 47;
|
|
7
|
-
export declare const CHAR_ASTERISK = 42;
|
|
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;
|
|
21
|
-
export declare const CHAR_LESS_THAN = 60;
|
|
22
|
-
/**
|
|
23
|
-
* @param a Base string, MUST be lowercase!
|
|
24
|
-
* @param b Compare string
|
|
25
|
-
*/
|
|
26
|
-
export declare function str_equals(a: string, b: string): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Case-insensitive ASCII prefix check without allocations
|
|
29
|
-
* Returns true if string `str` starts with prefix (case-insensitive)
|
|
30
|
-
*
|
|
31
|
-
* IMPORTANT: prefix MUST be lowercase for correct comparison
|
|
32
|
-
*
|
|
33
|
-
* @param str - The string to check
|
|
34
|
-
* @param prefix - The lowercase prefix to match against
|
|
35
|
-
*/
|
|
36
|
-
export declare function str_starts_with(str: string, prefix: string): boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Case-insensitive character/substring search without allocations
|
|
39
|
-
* Returns the index of the first occurrence of searchChar (case-insensitive)
|
|
40
|
-
*
|
|
41
|
-
* IMPORTANT: searchChar MUST be lowercase for correct comparison
|
|
42
|
-
*
|
|
43
|
-
* @param str - The string to search in
|
|
44
|
-
* @param searchChar - The lowercase character/substring to find
|
|
45
|
-
* @returns The index of the first match, or -1 if not found
|
|
46
|
-
*/
|
|
47
|
-
export declare function str_index_of(str: string, searchChar: string): number;
|
|
48
|
-
/**
|
|
49
|
-
* Check if a string range has a vendor prefix
|
|
50
|
-
*
|
|
51
|
-
* @param source - The source string
|
|
52
|
-
* @param start - Start offset in source
|
|
53
|
-
* @param end - End offset in source
|
|
54
|
-
* @returns true if the range starts with a vendor prefix (-webkit-, -moz-, -ms-, -o-)
|
|
55
|
-
*
|
|
56
|
-
* Detects vendor prefixes by checking:
|
|
57
|
-
* 1. Starts with a single hyphen (not --)
|
|
58
|
-
* 2. Contains at least 3 characters (shortest is -o-)
|
|
59
|
-
* 3. Has a second hyphen after the vendor name
|
|
60
|
-
*
|
|
61
|
-
* Examples:
|
|
62
|
-
* - `-webkit-transform` → true
|
|
63
|
-
* - `-moz-appearance` → true
|
|
64
|
-
* - `-ms-filter` → true
|
|
65
|
-
* - `-o-border-image` → true
|
|
66
|
-
* - `--custom-property` → false (CSS custom property)
|
|
67
|
-
* - `border-radius` → false (doesn't start with hyphen)
|
|
68
|
-
*/
|
|
69
|
-
export declare function is_vendor_prefixed(text: string): boolean;
|
|
70
|
-
export declare function is_vendor_prefixed(source: string, start: number, end: number): boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Check if a string is a CSS custom property (starts with --)
|
|
73
|
-
*
|
|
74
|
-
* @param str - The string to check
|
|
75
|
-
* @returns true if the string starts with -- (custom property)
|
|
76
|
-
*
|
|
77
|
-
* Examples:
|
|
78
|
-
* - `--primary-color` → true
|
|
79
|
-
* - `--my-var` → true
|
|
80
|
-
* - `-webkit-transform` → false (vendor prefix, not custom)
|
|
81
|
-
* - `border-radius` → false (standard property)
|
|
82
|
-
* - `color` → false
|
|
83
|
-
*/
|
|
84
|
-
export declare function is_custom(str: string): boolean;
|
|
85
|
-
/**
|
|
86
|
-
* Strip vendor prefix from a string
|
|
87
|
-
*
|
|
88
|
-
* @param str - The string to strip vendor prefix from
|
|
89
|
-
* @returns The string without vendor prefix, or original string if no prefix found
|
|
90
|
-
*
|
|
91
|
-
* Examples:
|
|
92
|
-
* - `-webkit-keyframes` → `keyframes`
|
|
93
|
-
* - `-moz-appearance` → `appearance`
|
|
94
|
-
* - `-ms-filter` → `filter`
|
|
95
|
-
* - `-o-border-image` → `border-image`
|
|
96
|
-
* - `keyframes` → `keyframes` (no change)
|
|
97
|
-
* - `--custom-property` → `--custom-property` (custom property, not vendor prefix)
|
|
98
|
-
*/
|
|
99
|
-
export declare function strip_vendor_prefix(str: string): string;
|