@projectwallace/css-parser 0.7.3 → 0.8.1
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.js +31 -41
- package/dist/constants.d.ts +40 -1
- package/dist/constants.js +47 -0
- package/dist/css-node.d.ts +85 -6
- package/dist/css-node.js +125 -82
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/parse-selector.js +12 -20
- package/dist/parse.js +1 -5
- package/dist/string-utils.d.ts +1 -0
- package/dist/string-utils.js +4 -0
- package/dist/walk.d.ts +59 -5
- package/dist/walk.js +33 -9
- package/package.json +1 -1
package/dist/arena.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
let BYTES_PER_NODE =
|
|
1
|
+
let BYTES_PER_NODE = 32;
|
|
2
2
|
const STYLESHEET = 1;
|
|
3
3
|
const STYLE_RULE = 2;
|
|
4
4
|
const AT_RULE = 3;
|
|
@@ -38,7 +38,6 @@ const PRELUDE_OPERATOR = 38;
|
|
|
38
38
|
const FLAG_IMPORTANT = 1 << 0;
|
|
39
39
|
const FLAG_HAS_ERROR = 1 << 1;
|
|
40
40
|
const FLAG_HAS_BLOCK = 1 << 3;
|
|
41
|
-
const FLAG_VENDOR_PREFIXED = 1 << 4;
|
|
42
41
|
const FLAG_HAS_DECLARATIONS = 1 << 5;
|
|
43
42
|
const FLAG_HAS_PARENS = 1 << 6;
|
|
44
43
|
const ATTR_OPERATOR_NONE = 0;
|
|
@@ -99,59 +98,55 @@ class CSSDataArena {
|
|
|
99
98
|
}
|
|
100
99
|
// Read start offset in source
|
|
101
100
|
get_start_offset(node_index) {
|
|
102
|
-
return this.view.getUint32(this.node_offset(node_index) +
|
|
101
|
+
return this.view.getUint32(this.node_offset(node_index) + 12, true);
|
|
103
102
|
}
|
|
104
103
|
// Read length in source
|
|
105
104
|
get_length(node_index) {
|
|
106
|
-
return this.view.getUint16(this.node_offset(node_index) +
|
|
105
|
+
return this.view.getUint16(this.node_offset(node_index) + 2, true);
|
|
107
106
|
}
|
|
108
107
|
// Read content start offset (stored as delta from startOffset)
|
|
109
108
|
get_content_start(node_index) {
|
|
110
109
|
const startOffset = this.get_start_offset(node_index);
|
|
111
|
-
const delta = this.view.getUint16(this.node_offset(node_index) +
|
|
110
|
+
const delta = this.view.getUint16(this.node_offset(node_index) + 16, true);
|
|
112
111
|
return startOffset + delta;
|
|
113
112
|
}
|
|
114
113
|
// Read content length
|
|
115
114
|
get_content_length(node_index) {
|
|
116
|
-
return this.view.getUint16(this.node_offset(node_index) +
|
|
115
|
+
return this.view.getUint16(this.node_offset(node_index) + 20, true);
|
|
117
116
|
}
|
|
118
117
|
// Read attribute operator (for NODE_SELECTOR_ATTRIBUTE)
|
|
119
118
|
get_attr_operator(node_index) {
|
|
120
|
-
return this.view.getUint8(this.node_offset(node_index) +
|
|
119
|
+
return this.view.getUint8(this.node_offset(node_index) + 30);
|
|
121
120
|
}
|
|
122
121
|
// Read attribute flags (for NODE_SELECTOR_ATTRIBUTE)
|
|
123
122
|
get_attr_flags(node_index) {
|
|
124
|
-
return this.view.getUint8(this.node_offset(node_index) +
|
|
123
|
+
return this.view.getUint8(this.node_offset(node_index) + 31);
|
|
125
124
|
}
|
|
126
125
|
// Read first child index (0 = no children)
|
|
127
126
|
get_first_child(node_index) {
|
|
128
|
-
return this.view.getUint32(this.node_offset(node_index) +
|
|
129
|
-
}
|
|
130
|
-
// Read last child index (0 = no children)
|
|
131
|
-
get_last_child(node_index) {
|
|
132
|
-
return this.view.getUint32(this.node_offset(node_index) + 24, true);
|
|
127
|
+
return this.view.getUint32(this.node_offset(node_index) + 4, true);
|
|
133
128
|
}
|
|
134
129
|
// Read next sibling index (0 = no sibling)
|
|
135
130
|
get_next_sibling(node_index) {
|
|
136
|
-
return this.view.getUint32(this.node_offset(node_index) +
|
|
131
|
+
return this.view.getUint32(this.node_offset(node_index) + 8, true);
|
|
137
132
|
}
|
|
138
133
|
// Read start line
|
|
139
134
|
get_start_line(node_index) {
|
|
140
|
-
return this.view.getUint32(this.node_offset(node_index) +
|
|
135
|
+
return this.view.getUint32(this.node_offset(node_index) + 24, true);
|
|
141
136
|
}
|
|
142
137
|
// Read start column
|
|
143
138
|
get_start_column(node_index) {
|
|
144
|
-
return this.view.getUint16(this.node_offset(node_index) +
|
|
139
|
+
return this.view.getUint16(this.node_offset(node_index) + 28, true);
|
|
145
140
|
}
|
|
146
141
|
// Read value start offset (stored as delta from startOffset, declaration value / at-rule prelude)
|
|
147
142
|
get_value_start(node_index) {
|
|
148
143
|
const startOffset = this.get_start_offset(node_index);
|
|
149
|
-
const delta = this.view.getUint16(this.node_offset(node_index) +
|
|
144
|
+
const delta = this.view.getUint16(this.node_offset(node_index) + 18, true);
|
|
150
145
|
return startOffset + delta;
|
|
151
146
|
}
|
|
152
147
|
// Read value length
|
|
153
148
|
get_value_length(node_index) {
|
|
154
|
-
return this.view.getUint16(this.node_offset(node_index) +
|
|
149
|
+
return this.view.getUint16(this.node_offset(node_index) + 22, true);
|
|
155
150
|
}
|
|
156
151
|
// --- Write Methods ---
|
|
157
152
|
// Write node type
|
|
@@ -164,55 +159,51 @@ class CSSDataArena {
|
|
|
164
159
|
}
|
|
165
160
|
// Write start offset in source
|
|
166
161
|
set_start_offset(node_index, offset) {
|
|
167
|
-
this.view.setUint32(this.node_offset(node_index) +
|
|
162
|
+
this.view.setUint32(this.node_offset(node_index) + 12, offset, true);
|
|
168
163
|
}
|
|
169
164
|
// Write length in source
|
|
170
165
|
set_length(node_index, length) {
|
|
171
|
-
this.view.setUint16(this.node_offset(node_index) +
|
|
166
|
+
this.view.setUint16(this.node_offset(node_index) + 2, length, true);
|
|
172
167
|
}
|
|
173
168
|
// Write content start delta (offset from startOffset)
|
|
174
169
|
set_content_start_delta(node_index, delta) {
|
|
175
|
-
this.view.setUint16(this.node_offset(node_index) +
|
|
170
|
+
this.view.setUint16(this.node_offset(node_index) + 16, delta, true);
|
|
176
171
|
}
|
|
177
172
|
// Write content length
|
|
178
173
|
set_content_length(node_index, length) {
|
|
179
|
-
this.view.setUint16(this.node_offset(node_index) +
|
|
174
|
+
this.view.setUint16(this.node_offset(node_index) + 20, length, true);
|
|
180
175
|
}
|
|
181
176
|
// Write attribute operator (for NODE_SELECTOR_ATTRIBUTE)
|
|
182
177
|
set_attr_operator(node_index, operator) {
|
|
183
|
-
this.view.setUint8(this.node_offset(node_index) +
|
|
178
|
+
this.view.setUint8(this.node_offset(node_index) + 30, operator);
|
|
184
179
|
}
|
|
185
180
|
// Write attribute flags (for NODE_SELECTOR_ATTRIBUTE)
|
|
186
181
|
set_attr_flags(node_index, flags) {
|
|
187
|
-
this.view.setUint8(this.node_offset(node_index) +
|
|
182
|
+
this.view.setUint8(this.node_offset(node_index) + 31, flags);
|
|
188
183
|
}
|
|
189
184
|
// Write first child index
|
|
190
185
|
set_first_child(node_index, childIndex) {
|
|
191
|
-
this.view.setUint32(this.node_offset(node_index) +
|
|
192
|
-
}
|
|
193
|
-
// Write last child index
|
|
194
|
-
set_last_child(node_index, childIndex) {
|
|
195
|
-
this.view.setUint32(this.node_offset(node_index) + 24, childIndex, true);
|
|
186
|
+
this.view.setUint32(this.node_offset(node_index) + 4, childIndex, true);
|
|
196
187
|
}
|
|
197
188
|
// Write next sibling index
|
|
198
189
|
set_next_sibling(node_index, siblingIndex) {
|
|
199
|
-
this.view.setUint32(this.node_offset(node_index) +
|
|
190
|
+
this.view.setUint32(this.node_offset(node_index) + 8, siblingIndex, true);
|
|
200
191
|
}
|
|
201
192
|
// Write start line
|
|
202
193
|
set_start_line(node_index, line) {
|
|
203
|
-
this.view.setUint32(this.node_offset(node_index) +
|
|
194
|
+
this.view.setUint32(this.node_offset(node_index) + 24, line, true);
|
|
204
195
|
}
|
|
205
196
|
// Write start column
|
|
206
197
|
set_start_column(node_index, column) {
|
|
207
|
-
this.view.setUint16(this.node_offset(node_index) +
|
|
198
|
+
this.view.setUint16(this.node_offset(node_index) + 28, column, true);
|
|
208
199
|
}
|
|
209
200
|
// Write value start delta (offset from startOffset, declaration value / at-rule prelude)
|
|
210
201
|
set_value_start_delta(node_index, delta) {
|
|
211
|
-
this.view.setUint16(this.node_offset(node_index) +
|
|
202
|
+
this.view.setUint16(this.node_offset(node_index) + 18, delta, true);
|
|
212
203
|
}
|
|
213
204
|
// Write value length
|
|
214
205
|
set_value_length(node_index, length) {
|
|
215
|
-
this.view.setUint16(this.node_offset(node_index) +
|
|
206
|
+
this.view.setUint16(this.node_offset(node_index) + 22, length, true);
|
|
216
207
|
}
|
|
217
208
|
// --- Node Creation ---
|
|
218
209
|
// Grow the arena by 1.3x when capacity is exceeded
|
|
@@ -234,10 +225,10 @@ class CSSDataArena {
|
|
|
234
225
|
this.count++;
|
|
235
226
|
const offset = node_index * BYTES_PER_NODE;
|
|
236
227
|
this.view.setUint8(offset, type);
|
|
237
|
-
this.view.
|
|
238
|
-
this.view.
|
|
239
|
-
this.view.setUint32(offset +
|
|
240
|
-
this.view.setUint16(offset +
|
|
228
|
+
this.view.setUint16(offset + 2, length, true);
|
|
229
|
+
this.view.setUint32(offset + 12, start_offset, true);
|
|
230
|
+
this.view.setUint32(offset + 24, start_line, true);
|
|
231
|
+
this.view.setUint16(offset + 28, start_column, true);
|
|
241
232
|
return node_index;
|
|
242
233
|
}
|
|
243
234
|
// --- Tree Building Helpers ---
|
|
@@ -246,8 +237,7 @@ class CSSDataArena {
|
|
|
246
237
|
append_children(parent_index, children) {
|
|
247
238
|
if (children.length === 0) return;
|
|
248
239
|
const offset = this.node_offset(parent_index);
|
|
249
|
-
this.view.setUint32(offset +
|
|
250
|
-
this.view.setUint32(offset + 24, children[children.length - 1], true);
|
|
240
|
+
this.view.setUint32(offset + 4, children[0], true);
|
|
251
241
|
for (let i = 0; i < children.length - 1; i++) {
|
|
252
242
|
this.set_next_sibling(children[i], children[i + 1]);
|
|
253
243
|
}
|
|
@@ -277,4 +267,4 @@ class CSSDataArena {
|
|
|
277
267
|
}
|
|
278
268
|
}
|
|
279
269
|
|
|
280
|
-
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, CSSDataArena, DECLARATION, DIMENSION, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, FLAG_HAS_ERROR, FLAG_HAS_PARENS, FLAG_IMPORTANT,
|
|
270
|
+
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, CSSDataArena, DECLARATION, DIMENSION, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, FLAG_HAS_ERROR, FLAG_HAS_PARENS, 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 };
|
package/dist/constants.d.ts
CHANGED
|
@@ -1 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
import { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FLAG_IMPORTANT, 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';
|
|
2
|
+
export { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, SELECTOR_LIST, TYPE_SELECTOR, CLASS_SELECTOR, ID_SELECTOR, ATTRIBUTE_SELECTOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, COMBINATOR, UNIVERSAL_SELECTOR, NESTING_SELECTOR, NTH_SELECTOR, NTH_OF_SELECTOR, LANG_SELECTOR, MEDIA_QUERY, MEDIA_FEATURE, MEDIA_TYPE, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, PRELUDE_OPERATOR, FLAG_IMPORTANT, 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, };
|
|
3
|
+
export declare const NODE_TYPES: {
|
|
4
|
+
readonly STYLESHEET: 1;
|
|
5
|
+
readonly STYLE_RULE: 2;
|
|
6
|
+
readonly AT_RULE: 3;
|
|
7
|
+
readonly DECLARATION: 4;
|
|
8
|
+
readonly SELECTOR: 5;
|
|
9
|
+
readonly COMMENT: 6;
|
|
10
|
+
readonly BLOCK: 7;
|
|
11
|
+
readonly IDENTIFIER: 10;
|
|
12
|
+
readonly NUMBER: 11;
|
|
13
|
+
readonly DIMENSION: 12;
|
|
14
|
+
readonly STRING: 13;
|
|
15
|
+
readonly HASH: 14;
|
|
16
|
+
readonly FUNCTION: 15;
|
|
17
|
+
readonly OPERATOR: 16;
|
|
18
|
+
readonly PARENTHESIS: 17;
|
|
19
|
+
readonly URL: 18;
|
|
20
|
+
readonly SELECTOR_LIST: 20;
|
|
21
|
+
readonly TYPE_SELECTOR: 21;
|
|
22
|
+
readonly CLASS_SELECTOR: 22;
|
|
23
|
+
readonly ID_SELECTOR: 23;
|
|
24
|
+
readonly ATTRIBUTE_SELECTOR: 24;
|
|
25
|
+
readonly PSEUDO_CLASS_SELECTOR: 25;
|
|
26
|
+
readonly PSEUDO_ELEMENT_SELECTOR: 26;
|
|
27
|
+
readonly COMBINATOR: 27;
|
|
28
|
+
readonly UNIVERSAL_SELECTOR: 28;
|
|
29
|
+
readonly NESTING_SELECTOR: 29;
|
|
30
|
+
readonly NTH_SELECTOR: 30;
|
|
31
|
+
readonly NTH_OF_SELECTOR: 31;
|
|
32
|
+
readonly LANG_SELECTOR: 56;
|
|
33
|
+
readonly MEDIA_QUERY: 32;
|
|
34
|
+
readonly MEDIA_FEATURE: 33;
|
|
35
|
+
readonly MEDIA_TYPE: 34;
|
|
36
|
+
readonly CONTAINER_QUERY: 35;
|
|
37
|
+
readonly SUPPORTS_QUERY: 36;
|
|
38
|
+
readonly LAYER_NAME: 37;
|
|
39
|
+
readonly PRELUDE_OPERATOR: 38;
|
|
40
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_FEATURE, MEDIA_QUERY, LANG_SELECTOR, NTH_OF_SELECTOR, NTH_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, COMBINATOR, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, ATTRIBUTE_SELECTOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, SELECTOR_LIST, URL, PARENTHESIS, OPERATOR, FUNCTION, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, BLOCK, COMMENT, SELECTOR, DECLARATION, AT_RULE, STYLE_RULE, STYLESHEET } from './arena.js';
|
|
2
|
+
export { 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, FLAG_IMPORTANT } from './arena.js';
|
|
3
|
+
|
|
4
|
+
const NODE_TYPES = {
|
|
5
|
+
// Core nodes
|
|
6
|
+
STYLESHEET,
|
|
7
|
+
STYLE_RULE,
|
|
8
|
+
AT_RULE,
|
|
9
|
+
DECLARATION,
|
|
10
|
+
SELECTOR,
|
|
11
|
+
COMMENT,
|
|
12
|
+
BLOCK,
|
|
13
|
+
// Value nodes
|
|
14
|
+
IDENTIFIER,
|
|
15
|
+
NUMBER,
|
|
16
|
+
DIMENSION,
|
|
17
|
+
STRING,
|
|
18
|
+
HASH,
|
|
19
|
+
FUNCTION,
|
|
20
|
+
OPERATOR,
|
|
21
|
+
PARENTHESIS,
|
|
22
|
+
URL,
|
|
23
|
+
// Selector nodes
|
|
24
|
+
SELECTOR_LIST,
|
|
25
|
+
TYPE_SELECTOR,
|
|
26
|
+
CLASS_SELECTOR,
|
|
27
|
+
ID_SELECTOR,
|
|
28
|
+
ATTRIBUTE_SELECTOR,
|
|
29
|
+
PSEUDO_CLASS_SELECTOR,
|
|
30
|
+
PSEUDO_ELEMENT_SELECTOR,
|
|
31
|
+
COMBINATOR,
|
|
32
|
+
UNIVERSAL_SELECTOR,
|
|
33
|
+
NESTING_SELECTOR,
|
|
34
|
+
NTH_SELECTOR,
|
|
35
|
+
NTH_OF_SELECTOR,
|
|
36
|
+
LANG_SELECTOR,
|
|
37
|
+
// At-rule prelude nodes
|
|
38
|
+
MEDIA_QUERY,
|
|
39
|
+
MEDIA_FEATURE,
|
|
40
|
+
MEDIA_TYPE,
|
|
41
|
+
CONTAINER_QUERY,
|
|
42
|
+
SUPPORTS_QUERY,
|
|
43
|
+
LAYER_NAME,
|
|
44
|
+
PRELUDE_OPERATOR
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export { ATTRIBUTE_SELECTOR, AT_RULE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LANG_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_QUERY, MEDIA_TYPE, NESTING_SELECTOR, NODE_TYPES, 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 };
|
package/dist/css-node.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface CloneOptions {
|
|
|
47
47
|
*/
|
|
48
48
|
deep?: boolean;
|
|
49
49
|
/**
|
|
50
|
-
* Include location information (line, column,
|
|
50
|
+
* Include location information (line, column, start, length)
|
|
51
51
|
* @default false
|
|
52
52
|
*/
|
|
53
53
|
locations?: boolean;
|
|
@@ -71,54 +71,133 @@ export type PlainCSSNode = {
|
|
|
71
71
|
nth_b?: string | null;
|
|
72
72
|
line?: number;
|
|
73
73
|
column?: number;
|
|
74
|
-
|
|
74
|
+
start?: number;
|
|
75
75
|
length?: number;
|
|
76
|
+
end?: number;
|
|
76
77
|
};
|
|
77
78
|
export declare class CSSNode {
|
|
78
79
|
private arena;
|
|
79
80
|
private source;
|
|
80
81
|
private index;
|
|
81
82
|
constructor(arena: CSSDataArena, source: string, index: number);
|
|
82
|
-
|
|
83
|
+
/** Get node type as number (for performance) */
|
|
83
84
|
get type(): CSSNodeType;
|
|
85
|
+
/** Get node type as human-readable string */
|
|
84
86
|
get type_name(): TypeName;
|
|
87
|
+
/** Get the full text of this node from source */
|
|
85
88
|
get text(): string;
|
|
89
|
+
/** Get the "content" text (property name for declarations, at-rule name for at-rules, layer name for import layers) */
|
|
86
90
|
get name(): string;
|
|
91
|
+
/**
|
|
92
|
+
* Alias for name (for declarations: "color" in "color: blue")
|
|
93
|
+
* More semantic than `name` for declaration nodes
|
|
94
|
+
*/
|
|
87
95
|
get property(): string;
|
|
96
|
+
/**
|
|
97
|
+
* Get the value text (for declarations: "blue" in "color: blue")
|
|
98
|
+
* For dimension/number nodes: returns the numeric value as a number
|
|
99
|
+
* For string nodes: returns the string content without quotes
|
|
100
|
+
* For URL nodes with quoted string: returns the string with quotes (consistent with STRING node)
|
|
101
|
+
* For URL nodes with unquoted URL: returns the URL content without quotes
|
|
102
|
+
*/
|
|
88
103
|
get value(): string | number | null;
|
|
104
|
+
/** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
|
|
105
|
+
get value_as_number(): number | null;
|
|
106
|
+
/**
|
|
107
|
+
* Get the prelude text (for at-rules: "(min-width: 768px)" in "@media (min-width: 768px)")
|
|
108
|
+
* This is an alias for `value` to make at-rule usage more semantic
|
|
109
|
+
*/
|
|
89
110
|
get prelude(): string | null;
|
|
111
|
+
/**
|
|
112
|
+
* Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
|
|
113
|
+
* Returns one of the ATTR_OPERATOR_* constants
|
|
114
|
+
*/
|
|
90
115
|
get attr_operator(): number;
|
|
116
|
+
/**
|
|
117
|
+
* Get the attribute flags (for attribute selectors: i, s)
|
|
118
|
+
* Returns one of the ATTR_FLAG_* constants
|
|
119
|
+
*/
|
|
91
120
|
get attr_flags(): number;
|
|
121
|
+
/** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
|
|
92
122
|
get unit(): string | null;
|
|
123
|
+
/** Check if this declaration has !important */
|
|
93
124
|
get is_important(): boolean | null;
|
|
125
|
+
/** Check if this has a vendor prefix (computed on-demand) */
|
|
94
126
|
get is_vendor_prefixed(): boolean;
|
|
127
|
+
/** Check if this node has an error */
|
|
95
128
|
get has_error(): boolean;
|
|
129
|
+
/** Check if this at-rule has a prelude */
|
|
96
130
|
get has_prelude(): boolean;
|
|
131
|
+
/** Check if this rule has a block { } */
|
|
97
132
|
get has_block(): boolean;
|
|
133
|
+
/** Check if this style rule has declarations */
|
|
98
134
|
get has_declarations(): boolean;
|
|
135
|
+
/** Get the block node (for style rules and at-rules with blocks) */
|
|
99
136
|
get block(): CSSNode | null;
|
|
137
|
+
/** Check if this block is empty (no declarations or rules, only comments allowed) */
|
|
100
138
|
get is_empty(): boolean;
|
|
139
|
+
/** Get array of parsed value nodes (for declarations only) */
|
|
101
140
|
get values(): CSSNode[];
|
|
102
|
-
|
|
141
|
+
/** Get start line number */
|
|
103
142
|
get line(): number;
|
|
143
|
+
/** Get start column number */
|
|
104
144
|
get column(): number;
|
|
105
|
-
|
|
145
|
+
/** Get start offset in source */
|
|
146
|
+
get start(): number;
|
|
147
|
+
/** Get length in source */
|
|
106
148
|
get length(): number;
|
|
149
|
+
/**
|
|
150
|
+
* Get end offset in source
|
|
151
|
+
* End is not stored, must be calculated
|
|
152
|
+
*/
|
|
153
|
+
get end(): number;
|
|
154
|
+
/** Get first child node */
|
|
107
155
|
get first_child(): CSSNode | null;
|
|
156
|
+
/** Get next sibling node */
|
|
108
157
|
get next_sibling(): CSSNode | null;
|
|
158
|
+
/** Check if this node has a next sibling */
|
|
109
159
|
get has_next(): boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Check if this node has children
|
|
162
|
+
* For pseudo-class/pseudo-element functions, returns true if FLAG_HAS_PARENS is set
|
|
163
|
+
* This allows formatters to distinguish :lang() from :hover
|
|
164
|
+
*/
|
|
110
165
|
get has_children(): boolean;
|
|
166
|
+
/** Get all children as an array */
|
|
111
167
|
get children(): CSSNode[];
|
|
168
|
+
/** Make CSSNode iterable over its children */
|
|
112
169
|
[Symbol.iterator](): Iterator<CSSNode>;
|
|
170
|
+
/** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
|
|
113
171
|
get nth_a(): string | null;
|
|
172
|
+
/** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
|
|
114
173
|
get nth_b(): string | null;
|
|
174
|
+
/** Get the An+B formula node from :nth-child(2n+1 of .foo) */
|
|
115
175
|
get nth(): CSSNode | null;
|
|
176
|
+
/** Get the selector list from :nth-child(2n+1 of .foo) */
|
|
116
177
|
get selector(): CSSNode | null;
|
|
178
|
+
/**
|
|
179
|
+
* Get selector list from pseudo-class functions
|
|
180
|
+
* Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
|
|
181
|
+
*/
|
|
117
182
|
get selector_list(): CSSNode | null;
|
|
183
|
+
/**
|
|
184
|
+
* Iterator over first compound selector parts (zero allocation)
|
|
185
|
+
* Yields parts before the first combinator
|
|
186
|
+
*/
|
|
118
187
|
compound_parts(): IterableIterator<CSSNode>;
|
|
188
|
+
/**
|
|
189
|
+
* Get first compound selector as array
|
|
190
|
+
* Returns array of parts before first combinator
|
|
191
|
+
*/
|
|
119
192
|
get first_compound(): CSSNode[];
|
|
193
|
+
/**
|
|
194
|
+
* Split selector into compound selectors
|
|
195
|
+
* Returns array of compound arrays split by combinators
|
|
196
|
+
*/
|
|
120
197
|
get all_compounds(): CSSNode[][];
|
|
198
|
+
/** Check if selector is compound (no combinators) */
|
|
121
199
|
get is_compound(): boolean;
|
|
200
|
+
/** Get text of first compound selector (no node allocation) */
|
|
122
201
|
get first_compound_text(): string;
|
|
123
202
|
/**
|
|
124
203
|
* Clone this node as a mutable plain JavaScript object
|
|
@@ -128,7 +207,7 @@ export declare class CSSNode {
|
|
|
128
207
|
*
|
|
129
208
|
* @param options - Cloning configuration
|
|
130
209
|
* @param options.deep - Recursively clone children (default: true)
|
|
131
|
-
* @param options.locations - Include line/column/
|
|
210
|
+
* @param options.locations - Include line/column/start/length (default: false)
|
|
132
211
|
* @returns Plain object with children as array
|
|
133
212
|
*
|
|
134
213
|
* @example
|
package/dist/css-node.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { str_starts_with, is_whitespace, CHAR_MINUS_HYPHEN, CHAR_PLUS } from './string-utils.js';
|
|
1
|
+
import { DIMENSION, NUMBER, URL, STRING, DECLARATION, FLAG_IMPORTANT, IDENTIFIER, FUNCTION, AT_RULE, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, FLAG_HAS_ERROR, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, STYLE_RULE, BLOCK, COMMENT, 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, PARENTHESIS, OPERATOR, HASH, STYLESHEET } from './arena.js';
|
|
2
|
+
import { str_starts_with, is_vendor_prefixed, is_whitespace, CHAR_MINUS_HYPHEN, CHAR_PLUS } from './string-utils.js';
|
|
3
3
|
import { parse_dimension } from './parse-utils.js';
|
|
4
4
|
|
|
5
5
|
const TYPE_NAMES = {
|
|
@@ -49,51 +49,57 @@ class CSSNode {
|
|
|
49
49
|
this.source = source;
|
|
50
50
|
this.index = index;
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
get_index() {
|
|
54
|
-
return this.index;
|
|
55
|
-
}
|
|
56
|
-
// Get node type as number (for performance)
|
|
52
|
+
/** Get node type as number (for performance) */
|
|
57
53
|
get type() {
|
|
58
54
|
return this.arena.get_type(this.index);
|
|
59
55
|
}
|
|
60
|
-
|
|
56
|
+
/** Get node type as human-readable string */
|
|
61
57
|
get type_name() {
|
|
62
58
|
return TYPE_NAMES[this.type] || "unknown";
|
|
63
59
|
}
|
|
64
|
-
|
|
60
|
+
/** Get the full text of this node from source */
|
|
65
61
|
get text() {
|
|
66
62
|
let start = this.arena.get_start_offset(this.index);
|
|
67
63
|
let length = this.arena.get_length(this.index);
|
|
68
64
|
return this.source.substring(start, start + length);
|
|
69
65
|
}
|
|
70
|
-
|
|
66
|
+
/** Get the "content" text (property name for declarations, at-rule name for at-rules, layer name for import layers) */
|
|
71
67
|
get name() {
|
|
72
68
|
let start = this.arena.get_content_start(this.index);
|
|
73
69
|
let length = this.arena.get_content_length(this.index);
|
|
74
70
|
if (length === 0) return "";
|
|
75
71
|
return this.source.substring(start, start + length);
|
|
76
72
|
}
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Alias for name (for declarations: "color" in "color: blue")
|
|
75
|
+
* More semantic than `name` for declaration nodes
|
|
76
|
+
*/
|
|
79
77
|
get property() {
|
|
80
78
|
return this.name;
|
|
81
79
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Get the value text (for declarations: "blue" in "color: blue")
|
|
82
|
+
* For dimension/number nodes: returns the numeric value as a number
|
|
83
|
+
* For string nodes: returns the string content without quotes
|
|
84
|
+
* For URL nodes with quoted string: returns the string with quotes (consistent with STRING node)
|
|
85
|
+
* For URL nodes with unquoted URL: returns the URL content without quotes
|
|
86
|
+
*/
|
|
87
87
|
get value() {
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
let { type, text } = this;
|
|
89
|
+
if (type === DIMENSION) {
|
|
90
|
+
return parse_dimension(text).value;
|
|
91
|
+
}
|
|
92
|
+
if (type === NUMBER) {
|
|
93
|
+
return Number.parseFloat(this.text);
|
|
94
|
+
}
|
|
95
|
+
if (type === URL) {
|
|
96
|
+
let firstChild = this.first_child;
|
|
90
97
|
if (firstChild && firstChild.type === STRING) {
|
|
91
98
|
return firstChild.text;
|
|
92
99
|
}
|
|
93
|
-
const text = this.text;
|
|
94
100
|
if (str_starts_with(text, "url(")) {
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
let openParen = text.indexOf("(");
|
|
102
|
+
let closeParen = text.lastIndexOf(")");
|
|
97
103
|
if (openParen !== -1 && closeParen !== -1 && closeParen > openParen) {
|
|
98
104
|
let content = text.substring(openParen + 1, closeParen).trim();
|
|
99
105
|
return content;
|
|
@@ -102,61 +108,89 @@ class CSSNode {
|
|
|
102
108
|
return text;
|
|
103
109
|
}
|
|
104
110
|
}
|
|
105
|
-
if (this.type === DIMENSION || this.type === NUMBER) {
|
|
106
|
-
return parse_dimension(this.text).value;
|
|
107
|
-
}
|
|
108
111
|
let start = this.arena.get_value_start(this.index);
|
|
109
112
|
let length = this.arena.get_value_length(this.index);
|
|
110
113
|
if (length === 0) return null;
|
|
111
114
|
return this.source.substring(start, start + length);
|
|
112
115
|
}
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
/** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
|
|
117
|
+
get value_as_number() {
|
|
118
|
+
let text = this.text;
|
|
119
|
+
if (this.type === NUMBER) {
|
|
120
|
+
return Number.parseFloat(text);
|
|
121
|
+
}
|
|
122
|
+
if (this.type === DIMENSION) {
|
|
123
|
+
return parse_dimension(text).value;
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get the prelude text (for at-rules: "(min-width: 768px)" in "@media (min-width: 768px)")
|
|
129
|
+
* This is an alias for `value` to make at-rule usage more semantic
|
|
130
|
+
*/
|
|
115
131
|
get prelude() {
|
|
116
132
|
let val = this.value;
|
|
117
133
|
return typeof val === "string" ? val : null;
|
|
118
134
|
}
|
|
119
|
-
|
|
120
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
|
|
137
|
+
* Returns one of the ATTR_OPERATOR_* constants
|
|
138
|
+
*/
|
|
121
139
|
get attr_operator() {
|
|
122
140
|
return this.arena.get_attr_operator(this.index);
|
|
123
141
|
}
|
|
124
|
-
|
|
125
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Get the attribute flags (for attribute selectors: i, s)
|
|
144
|
+
* Returns one of the ATTR_FLAG_* constants
|
|
145
|
+
*/
|
|
126
146
|
get attr_flags() {
|
|
127
147
|
return this.arena.get_attr_flags(this.index);
|
|
128
148
|
}
|
|
129
|
-
|
|
149
|
+
/** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
|
|
130
150
|
get unit() {
|
|
131
151
|
if (this.type !== DIMENSION) return null;
|
|
132
152
|
return parse_dimension(this.text).unit;
|
|
133
153
|
}
|
|
134
|
-
|
|
154
|
+
/** Check if this declaration has !important */
|
|
135
155
|
get is_important() {
|
|
136
156
|
if (this.type !== DECLARATION) return null;
|
|
137
157
|
return this.arena.has_flag(this.index, FLAG_IMPORTANT);
|
|
138
158
|
}
|
|
139
|
-
|
|
159
|
+
/** Check if this has a vendor prefix (computed on-demand) */
|
|
140
160
|
get is_vendor_prefixed() {
|
|
141
|
-
|
|
161
|
+
switch (this.type) {
|
|
162
|
+
case DECLARATION:
|
|
163
|
+
return is_vendor_prefixed(this.name);
|
|
164
|
+
case PSEUDO_CLASS_SELECTOR:
|
|
165
|
+
case PSEUDO_ELEMENT_SELECTOR:
|
|
166
|
+
return is_vendor_prefixed(this.name);
|
|
167
|
+
case AT_RULE:
|
|
168
|
+
return is_vendor_prefixed(this.name);
|
|
169
|
+
case FUNCTION:
|
|
170
|
+
return is_vendor_prefixed(this.name);
|
|
171
|
+
case IDENTIFIER:
|
|
172
|
+
return is_vendor_prefixed(this.text);
|
|
173
|
+
default:
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
142
176
|
}
|
|
143
|
-
|
|
177
|
+
/** Check if this node has an error */
|
|
144
178
|
get has_error() {
|
|
145
179
|
return this.arena.has_flag(this.index, FLAG_HAS_ERROR);
|
|
146
180
|
}
|
|
147
|
-
|
|
181
|
+
/** Check if this at-rule has a prelude */
|
|
148
182
|
get has_prelude() {
|
|
149
183
|
return this.arena.get_value_length(this.index) > 0;
|
|
150
184
|
}
|
|
151
|
-
|
|
185
|
+
/** Check if this rule has a block { } */
|
|
152
186
|
get has_block() {
|
|
153
187
|
return this.arena.has_flag(this.index, FLAG_HAS_BLOCK);
|
|
154
188
|
}
|
|
155
|
-
|
|
189
|
+
/** Check if this style rule has declarations */
|
|
156
190
|
get has_declarations() {
|
|
157
191
|
return this.arena.has_flag(this.index, FLAG_HAS_DECLARATIONS);
|
|
158
192
|
}
|
|
159
|
-
|
|
193
|
+
/** Get the block node (for style rules and at-rules with blocks) */
|
|
160
194
|
get block() {
|
|
161
195
|
if (this.type === STYLE_RULE) {
|
|
162
196
|
let first = this.first_child;
|
|
@@ -179,7 +213,7 @@ class CSSNode {
|
|
|
179
213
|
}
|
|
180
214
|
return null;
|
|
181
215
|
}
|
|
182
|
-
|
|
216
|
+
/** Check if this block is empty (no declarations or rules, only comments allowed) */
|
|
183
217
|
get is_empty() {
|
|
184
218
|
if (this.type !== BLOCK) return false;
|
|
185
219
|
let child = this.first_child;
|
|
@@ -192,7 +226,7 @@ class CSSNode {
|
|
|
192
226
|
return true;
|
|
193
227
|
}
|
|
194
228
|
// --- Value Node Access (for declarations) ---
|
|
195
|
-
|
|
229
|
+
/** Get array of parsed value nodes (for declarations only) */
|
|
196
230
|
get values() {
|
|
197
231
|
let result = [];
|
|
198
232
|
let child = this.first_child;
|
|
@@ -202,52 +236,52 @@ class CSSNode {
|
|
|
202
236
|
}
|
|
203
237
|
return result;
|
|
204
238
|
}
|
|
205
|
-
|
|
206
|
-
get value_count() {
|
|
207
|
-
let count = 0;
|
|
208
|
-
let child = this.first_child;
|
|
209
|
-
while (child) {
|
|
210
|
-
count++;
|
|
211
|
-
child = child.next_sibling;
|
|
212
|
-
}
|
|
213
|
-
return count;
|
|
214
|
-
}
|
|
215
|
-
// Get start line number
|
|
239
|
+
/** Get start line number */
|
|
216
240
|
get line() {
|
|
217
241
|
return this.arena.get_start_line(this.index);
|
|
218
242
|
}
|
|
219
|
-
|
|
243
|
+
/** Get start column number */
|
|
220
244
|
get column() {
|
|
221
245
|
return this.arena.get_start_column(this.index);
|
|
222
246
|
}
|
|
223
|
-
|
|
224
|
-
get
|
|
247
|
+
/** Get start offset in source */
|
|
248
|
+
get start() {
|
|
225
249
|
return this.arena.get_start_offset(this.index);
|
|
226
250
|
}
|
|
227
|
-
|
|
251
|
+
/** Get length in source */
|
|
228
252
|
get length() {
|
|
229
253
|
return this.arena.get_length(this.index);
|
|
230
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Get end offset in source
|
|
257
|
+
* End is not stored, must be calculated
|
|
258
|
+
*/
|
|
259
|
+
get end() {
|
|
260
|
+
return this.start + this.length;
|
|
261
|
+
}
|
|
231
262
|
// --- Tree Traversal ---
|
|
232
|
-
|
|
263
|
+
/** Get first child node */
|
|
233
264
|
get first_child() {
|
|
234
265
|
let child_index = this.arena.get_first_child(this.index);
|
|
235
266
|
if (child_index === 0) return null;
|
|
236
267
|
return new CSSNode(this.arena, this.source, child_index);
|
|
237
268
|
}
|
|
238
|
-
|
|
269
|
+
/** Get next sibling node */
|
|
239
270
|
get next_sibling() {
|
|
240
271
|
let sibling_index = this.arena.get_next_sibling(this.index);
|
|
241
272
|
if (sibling_index === 0) return null;
|
|
242
273
|
return new CSSNode(this.arena, this.source, sibling_index);
|
|
243
274
|
}
|
|
275
|
+
/** Check if this node has a next sibling */
|
|
244
276
|
get has_next() {
|
|
245
277
|
let sibling_index = this.arena.get_next_sibling(this.index);
|
|
246
278
|
return sibling_index !== 0;
|
|
247
279
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Check if this node has children
|
|
282
|
+
* For pseudo-class/pseudo-element functions, returns true if FLAG_HAS_PARENS is set
|
|
283
|
+
* This allows formatters to distinguish :lang() from :hover
|
|
284
|
+
*/
|
|
251
285
|
get has_children() {
|
|
252
286
|
if (this.type === PSEUDO_CLASS_SELECTOR || this.type === PSEUDO_ELEMENT_SELECTOR) {
|
|
253
287
|
if (this.arena.has_flag(this.index, FLAG_HAS_PARENS)) {
|
|
@@ -256,7 +290,7 @@ class CSSNode {
|
|
|
256
290
|
}
|
|
257
291
|
return this.arena.has_children(this.index);
|
|
258
292
|
}
|
|
259
|
-
|
|
293
|
+
/** Get all children as an array */
|
|
260
294
|
get children() {
|
|
261
295
|
let result = [];
|
|
262
296
|
let child = this.first_child;
|
|
@@ -266,7 +300,7 @@ class CSSNode {
|
|
|
266
300
|
}
|
|
267
301
|
return result;
|
|
268
302
|
}
|
|
269
|
-
|
|
303
|
+
/** Make CSSNode iterable over its children */
|
|
270
304
|
*[Symbol.iterator]() {
|
|
271
305
|
let child = this.first_child;
|
|
272
306
|
while (child) {
|
|
@@ -275,7 +309,7 @@ class CSSNode {
|
|
|
275
309
|
}
|
|
276
310
|
}
|
|
277
311
|
// --- An+B Expression Helpers (for NODE_SELECTOR_NTH) ---
|
|
278
|
-
|
|
312
|
+
/** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
|
|
279
313
|
get nth_a() {
|
|
280
314
|
if (this.type !== NTH_SELECTOR) return null;
|
|
281
315
|
let len = this.arena.get_content_length(this.index);
|
|
@@ -283,7 +317,7 @@ class CSSNode {
|
|
|
283
317
|
let start = this.arena.get_content_start(this.index);
|
|
284
318
|
return this.source.substring(start, start + len);
|
|
285
319
|
}
|
|
286
|
-
|
|
320
|
+
/** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
|
|
287
321
|
get nth_b() {
|
|
288
322
|
if (this.type !== NTH_SELECTOR) return null;
|
|
289
323
|
let len = this.arena.get_value_length(this.index);
|
|
@@ -307,20 +341,22 @@ class CSSNode {
|
|
|
307
341
|
return value;
|
|
308
342
|
}
|
|
309
343
|
// --- Pseudo-Class Nth-Of Helpers (for NODE_SELECTOR_NTH_OF) ---
|
|
310
|
-
|
|
344
|
+
/** Get the An+B formula node from :nth-child(2n+1 of .foo) */
|
|
311
345
|
get nth() {
|
|
312
346
|
if (this.type !== NTH_OF_SELECTOR) return null;
|
|
313
347
|
return this.first_child;
|
|
314
348
|
}
|
|
315
|
-
|
|
349
|
+
/** Get the selector list from :nth-child(2n+1 of .foo) */
|
|
316
350
|
get selector() {
|
|
317
351
|
if (this.type !== NTH_OF_SELECTOR) return null;
|
|
318
352
|
let first = this.first_child;
|
|
319
353
|
return first ? first.next_sibling : null;
|
|
320
354
|
}
|
|
321
355
|
// --- Pseudo-Class Selector List Helper ---
|
|
322
|
-
|
|
323
|
-
|
|
356
|
+
/**
|
|
357
|
+
* Get selector list from pseudo-class functions
|
|
358
|
+
* Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
|
|
359
|
+
*/
|
|
324
360
|
get selector_list() {
|
|
325
361
|
if (this.type !== PSEUDO_CLASS_SELECTOR) return null;
|
|
326
362
|
let child = this.first_child;
|
|
@@ -334,8 +370,10 @@ class CSSNode {
|
|
|
334
370
|
return null;
|
|
335
371
|
}
|
|
336
372
|
// --- Compound Selector Helpers (for NODE_SELECTOR) ---
|
|
337
|
-
|
|
338
|
-
|
|
373
|
+
/**
|
|
374
|
+
* Iterator over first compound selector parts (zero allocation)
|
|
375
|
+
* Yields parts before the first combinator
|
|
376
|
+
*/
|
|
339
377
|
*compound_parts() {
|
|
340
378
|
if (this.type !== SELECTOR) return;
|
|
341
379
|
let child = this.first_child;
|
|
@@ -345,8 +383,10 @@ class CSSNode {
|
|
|
345
383
|
child = child.next_sibling;
|
|
346
384
|
}
|
|
347
385
|
}
|
|
348
|
-
|
|
349
|
-
|
|
386
|
+
/**
|
|
387
|
+
* Get first compound selector as array
|
|
388
|
+
* Returns array of parts before first combinator
|
|
389
|
+
*/
|
|
350
390
|
get first_compound() {
|
|
351
391
|
if (this.type !== SELECTOR) return [];
|
|
352
392
|
let result = [];
|
|
@@ -358,8 +398,10 @@ class CSSNode {
|
|
|
358
398
|
}
|
|
359
399
|
return result;
|
|
360
400
|
}
|
|
361
|
-
|
|
362
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Split selector into compound selectors
|
|
403
|
+
* Returns array of compound arrays split by combinators
|
|
404
|
+
*/
|
|
363
405
|
get all_compounds() {
|
|
364
406
|
if (this.type !== SELECTOR) return [];
|
|
365
407
|
let compounds = [];
|
|
@@ -381,7 +423,7 @@ class CSSNode {
|
|
|
381
423
|
}
|
|
382
424
|
return compounds;
|
|
383
425
|
}
|
|
384
|
-
|
|
426
|
+
/** Check if selector is compound (no combinators) */
|
|
385
427
|
get is_compound() {
|
|
386
428
|
if (this.type !== SELECTOR) return false;
|
|
387
429
|
let child = this.first_child;
|
|
@@ -391,7 +433,7 @@ class CSSNode {
|
|
|
391
433
|
}
|
|
392
434
|
return true;
|
|
393
435
|
}
|
|
394
|
-
|
|
436
|
+
/** Get text of first compound selector (no node allocation) */
|
|
395
437
|
get first_compound_text() {
|
|
396
438
|
if (this.type !== SELECTOR) return "";
|
|
397
439
|
let start = -1;
|
|
@@ -399,8 +441,8 @@ class CSSNode {
|
|
|
399
441
|
let child = this.first_child;
|
|
400
442
|
while (child) {
|
|
401
443
|
if (child.type === COMBINATOR) break;
|
|
402
|
-
if (start === -1) start = child.
|
|
403
|
-
end = child.
|
|
444
|
+
if (start === -1) start = child.start;
|
|
445
|
+
end = child.start + child.length;
|
|
404
446
|
child = child.next_sibling;
|
|
405
447
|
}
|
|
406
448
|
if (start === -1) return "";
|
|
@@ -415,7 +457,7 @@ class CSSNode {
|
|
|
415
457
|
*
|
|
416
458
|
* @param options - Cloning configuration
|
|
417
459
|
* @param options.deep - Recursively clone children (default: true)
|
|
418
|
-
* @param options.locations - Include line/column/
|
|
460
|
+
* @param options.locations - Include line/column/start/length (default: false)
|
|
419
461
|
* @returns Plain object with children as array
|
|
420
462
|
*
|
|
421
463
|
* @example
|
|
@@ -459,8 +501,9 @@ class CSSNode {
|
|
|
459
501
|
if (locations) {
|
|
460
502
|
plain.line = this.line;
|
|
461
503
|
plain.column = this.column;
|
|
462
|
-
plain.
|
|
504
|
+
plain.start = this.start;
|
|
463
505
|
plain.length = this.length;
|
|
506
|
+
plain.end = this.end;
|
|
464
507
|
}
|
|
465
508
|
if (deep) {
|
|
466
509
|
for (let child of this.children) {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ export { parse_selector } from './parse-selector';
|
|
|
3
3
|
export { parse_atrule_prelude } from './parse-atrule-prelude';
|
|
4
4
|
export { parse_value } from './parse-value';
|
|
5
5
|
export { tokenize } from './tokenize';
|
|
6
|
-
export { walk, traverse } from './walk';
|
|
6
|
+
export { walk, traverse, SKIP, BREAK } from './walk';
|
|
7
7
|
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
11
|
export * from './constants';
|
|
12
12
|
export * from './token-types';
|
|
13
|
+
export { NODE_TYPES } from './constants';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,8 @@ 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 { traverse, walk } from './walk.js';
|
|
6
|
+
export { BREAK, SKIP, traverse, walk } from './walk.js';
|
|
7
7
|
export { CSSNode, TYPE_NAMES } from './css-node.js';
|
|
8
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 { NODE_TYPES } from './constants.js';
|
|
9
10
|
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/parse-selector.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Lexer } from './lexer.js';
|
|
2
|
-
import { CSSDataArena, SELECTOR_LIST, SELECTOR, COMBINATOR, NESTING_SELECTOR, ID_SELECTOR, TYPE_SELECTOR, UNIVERSAL_SELECTOR, CLASS_SELECTOR, ATTRIBUTE_SELECTOR, ATTR_OPERATOR_NONE, ATTR_FLAG_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_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR,
|
|
2
|
+
import { CSSDataArena, SELECTOR_LIST, SELECTOR, COMBINATOR, NESTING_SELECTOR, ID_SELECTOR, TYPE_SELECTOR, UNIVERSAL_SELECTOR, CLASS_SELECTOR, ATTRIBUTE_SELECTOR, ATTR_OPERATOR_NONE, ATTR_FLAG_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_CASE_INSENSITIVE, ATTR_FLAG_CASE_SENSITIVE, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, FLAG_HAS_PARENS, LANG_SELECTOR, NTH_OF_SELECTOR } from './arena.js';
|
|
3
3
|
import { TOKEN_COMMA, TOKEN_DELIM, TOKEN_EOF, TOKEN_WHITESPACE, TOKEN_FUNCTION, TOKEN_COLON, TOKEN_LEFT_BRACKET, TOKEN_HASH, TOKEN_IDENT, TOKEN_RIGHT_BRACKET, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_STRING } from './token-types.js';
|
|
4
4
|
import { skip_whitespace_and_comments_forward, skip_whitespace_and_comments_backward, skip_whitespace_forward } from './parse-utils.js';
|
|
5
|
-
import { CHAR_GREATER_THAN, CHAR_PLUS, CHAR_TILDE, CHAR_PERIOD, CHAR_ASTERISK, CHAR_AMPERSAND, CHAR_PIPE, CHAR_SPACE, CHAR_NEWLINE, CHAR_CARRIAGE_RETURN, CHAR_FORM_FEED, is_combinator, is_whitespace, CHAR_EQUALS, CHAR_CARET, CHAR_DOLLAR, CHAR_SINGLE_QUOTE, CHAR_DOUBLE_QUOTE, CHAR_COLON,
|
|
5
|
+
import { CHAR_GREATER_THAN, CHAR_PLUS, CHAR_TILDE, CHAR_PERIOD, CHAR_ASTERISK, CHAR_AMPERSAND, CHAR_PIPE, CHAR_SPACE, CHAR_TAB, CHAR_NEWLINE, CHAR_CARRIAGE_RETURN, CHAR_FORM_FEED, is_combinator, is_whitespace, CHAR_EQUALS, CHAR_CARET, CHAR_DOLLAR, CHAR_SINGLE_QUOTE, CHAR_DOUBLE_QUOTE, CHAR_COLON, str_equals } from './string-utils.js';
|
|
6
6
|
import { ANplusBParser } from './parse-anplusb.js';
|
|
7
7
|
import { CSSNode } from './css-node.js';
|
|
8
8
|
|
|
@@ -44,7 +44,6 @@ class SelectorParser {
|
|
|
44
44
|
next_sibling = this.arena.get_next_sibling(last_component);
|
|
45
45
|
}
|
|
46
46
|
this.arena.set_first_child(selector_wrapper, complex_selector);
|
|
47
|
-
this.arena.set_last_child(selector_wrapper, last_component);
|
|
48
47
|
selectors.push(selector_wrapper);
|
|
49
48
|
}
|
|
50
49
|
this.skip_whitespace();
|
|
@@ -109,7 +108,10 @@ class SelectorParser {
|
|
|
109
108
|
}
|
|
110
109
|
const saved = this.lexer.save_position();
|
|
111
110
|
this.skip_whitespace();
|
|
112
|
-
if (this.lexer.pos >= this.selector_end)
|
|
111
|
+
if (this.lexer.pos >= this.selector_end) {
|
|
112
|
+
this.lexer.restore_position(saved);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
113
115
|
this.lexer.next_token_fast(false);
|
|
114
116
|
let token_type = this.lexer.token_type;
|
|
115
117
|
if (token_type === TOKEN_COMMA || this.lexer.pos >= this.selector_end) {
|
|
@@ -240,14 +242,17 @@ class SelectorParser {
|
|
|
240
242
|
let has_whitespace = false;
|
|
241
243
|
while (this.lexer.pos < this.selector_end) {
|
|
242
244
|
let ch = this.source.charCodeAt(this.lexer.pos);
|
|
243
|
-
if (ch === CHAR_SPACE || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
|
|
245
|
+
if (ch === CHAR_SPACE || ch === CHAR_TAB || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
|
|
244
246
|
has_whitespace = true;
|
|
245
247
|
this.lexer.pos++;
|
|
246
248
|
} else {
|
|
247
249
|
break;
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
|
-
if (this.lexer.pos >= this.selector_end)
|
|
252
|
+
if (this.lexer.pos >= this.selector_end) {
|
|
253
|
+
this.lexer.pos = whitespace_start;
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
251
256
|
this.lexer.next_token_fast(false);
|
|
252
257
|
if (this.lexer.token_type === TOKEN_DELIM) {
|
|
253
258
|
let ch = this.source.charCodeAt(this.lexer.token_start);
|
|
@@ -259,7 +264,7 @@ class SelectorParser {
|
|
|
259
264
|
this.lexer.pos = whitespace_start;
|
|
260
265
|
while (this.lexer.pos < this.selector_end) {
|
|
261
266
|
let ch = this.source.charCodeAt(this.lexer.pos);
|
|
262
|
-
if (ch === CHAR_SPACE || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
|
|
267
|
+
if (ch === CHAR_SPACE || ch === CHAR_TAB || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
|
|
263
268
|
this.lexer.pos++;
|
|
264
269
|
} else {
|
|
265
270
|
break;
|
|
@@ -422,9 +427,6 @@ class SelectorParser {
|
|
|
422
427
|
let node = this.create_node(is_pseudo_element ? PSEUDO_ELEMENT_SELECTOR : PSEUDO_CLASS_SELECTOR, start, this.lexer.token_end);
|
|
423
428
|
this.arena.set_content_start_delta(node, this.lexer.token_start - start);
|
|
424
429
|
this.arena.set_content_length(node, this.lexer.token_end - this.lexer.token_start);
|
|
425
|
-
if (is_vendor_prefixed(this.source, this.lexer.token_start, this.lexer.token_end)) {
|
|
426
|
-
this.arena.set_flag(node, FLAG_VENDOR_PREFIXED);
|
|
427
|
-
}
|
|
428
430
|
return node;
|
|
429
431
|
} else if (token_type === TOKEN_FUNCTION) {
|
|
430
432
|
return this.parse_pseudo_function_after_colon(start, is_pseudo_element);
|
|
@@ -462,16 +464,12 @@ class SelectorParser {
|
|
|
462
464
|
this.arena.set_content_start_delta(node, func_name_start - start);
|
|
463
465
|
this.arena.set_content_length(node, func_name_end - func_name_start);
|
|
464
466
|
this.arena.set_flag(node, FLAG_HAS_PARENS);
|
|
465
|
-
if (is_vendor_prefixed(this.source, func_name_start, func_name_end)) {
|
|
466
|
-
this.arena.set_flag(node, FLAG_VENDOR_PREFIXED);
|
|
467
|
-
}
|
|
468
467
|
if (content_end > content_start) {
|
|
469
468
|
let func_name_substr = this.source.substring(func_name_start, func_name_end);
|
|
470
469
|
if (this.is_nth_pseudo(func_name_substr)) {
|
|
471
470
|
let child = this.parse_nth_expression(content_start, content_end);
|
|
472
471
|
if (child !== null) {
|
|
473
472
|
this.arena.set_first_child(node, child);
|
|
474
|
-
this.arena.set_last_child(node, child);
|
|
475
473
|
}
|
|
476
474
|
} else if (str_equals("lang", func_name_substr)) {
|
|
477
475
|
this.parse_lang_identifiers(content_start, content_end, node);
|
|
@@ -484,7 +482,6 @@ class SelectorParser {
|
|
|
484
482
|
this.lexer.restore_position(saved);
|
|
485
483
|
if (child_selector !== null) {
|
|
486
484
|
this.arena.set_first_child(node, child_selector);
|
|
487
|
-
this.arena.set_last_child(node, child_selector);
|
|
488
485
|
}
|
|
489
486
|
}
|
|
490
487
|
}
|
|
@@ -531,9 +528,6 @@ class SelectorParser {
|
|
|
531
528
|
if (first_child !== null) {
|
|
532
529
|
this.arena.set_first_child(parent_node, first_child);
|
|
533
530
|
}
|
|
534
|
-
if (last_child !== null) {
|
|
535
|
-
this.arena.set_last_child(parent_node, last_child);
|
|
536
|
-
}
|
|
537
531
|
this.selector_end = saved_selector_end;
|
|
538
532
|
this.lexer.restore_position(saved);
|
|
539
533
|
}
|
|
@@ -562,11 +556,9 @@ class SelectorParser {
|
|
|
562
556
|
);
|
|
563
557
|
if (anplusb_node !== null && selector_list !== null) {
|
|
564
558
|
this.arena.set_first_child(of_node, anplusb_node);
|
|
565
|
-
this.arena.set_last_child(of_node, selector_list);
|
|
566
559
|
this.arena.set_next_sibling(anplusb_node, selector_list);
|
|
567
560
|
} else if (anplusb_node !== null) {
|
|
568
561
|
this.arena.set_first_child(of_node, anplusb_node);
|
|
569
|
-
this.arena.set_last_child(of_node, anplusb_node);
|
|
570
562
|
}
|
|
571
563
|
return of_node;
|
|
572
564
|
} else {
|
package/dist/parse.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { Lexer } from './lexer.js';
|
|
2
|
-
import { CSSDataArena, STYLESHEET, STYLE_RULE, FLAG_HAS_BLOCK, BLOCK, FLAG_HAS_DECLARATIONS, SELECTOR_LIST, DECLARATION,
|
|
2
|
+
import { CSSDataArena, STYLESHEET, STYLE_RULE, FLAG_HAS_BLOCK, BLOCK, FLAG_HAS_DECLARATIONS, SELECTOR_LIST, DECLARATION, FLAG_IMPORTANT, AT_RULE } from './arena.js';
|
|
3
3
|
import { CSSNode } from './css-node.js';
|
|
4
4
|
import { ValueParser } from './parse-value.js';
|
|
5
5
|
import { SelectorParser } from './parse-selector.js';
|
|
6
6
|
import { AtRulePreludeParser } from './parse-atrule-prelude.js';
|
|
7
7
|
import { TOKEN_EOF, TOKEN_AT_KEYWORD, TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE, TOKEN_IDENT, TOKEN_COLON, TOKEN_SEMICOLON, TOKEN_DELIM } from './token-types.js';
|
|
8
|
-
import { is_vendor_prefixed } from './string-utils.js';
|
|
9
8
|
import { trim_boundaries } from './parse-utils.js';
|
|
10
9
|
|
|
11
10
|
let DECLARATION_AT_RULES = /* @__PURE__ */ new Set(["font-face", "font-feature-values", "page", "property", "counter-style"]);
|
|
@@ -213,9 +212,6 @@ class Parser {
|
|
|
213
212
|
);
|
|
214
213
|
this.arena.set_content_start_delta(declaration, 0);
|
|
215
214
|
this.arena.set_content_length(declaration, prop_end - prop_start);
|
|
216
|
-
if (is_vendor_prefixed(this.source, prop_start, prop_end)) {
|
|
217
|
-
this.arena.set_flag(declaration, FLAG_VENDOR_PREFIXED);
|
|
218
|
-
}
|
|
219
215
|
let value_start = this.lexer.token_start;
|
|
220
216
|
let value_end = value_start;
|
|
221
217
|
let has_important = false;
|
package/dist/string-utils.d.ts
CHANGED
|
@@ -65,4 +65,5 @@ export declare function str_index_of(str: string, searchChar: string): number;
|
|
|
65
65
|
* - `--custom-property` → false (CSS custom property)
|
|
66
66
|
* - `border-radius` → false (doesn't start with hyphen)
|
|
67
67
|
*/
|
|
68
|
+
export declare function is_vendor_prefixed(text: string): boolean;
|
|
68
69
|
export declare function is_vendor_prefixed(source: string, start: number, end: number): boolean;
|
package/dist/string-utils.js
CHANGED
|
@@ -88,6 +88,10 @@ function str_index_of(str, searchChar) {
|
|
|
88
88
|
return -1;
|
|
89
89
|
}
|
|
90
90
|
function is_vendor_prefixed(source, start, end) {
|
|
91
|
+
if (start === void 0 || end === void 0) {
|
|
92
|
+
start = 0;
|
|
93
|
+
end = source.length;
|
|
94
|
+
}
|
|
91
95
|
if (source.charCodeAt(start) !== CHAR_MINUS_HYPHEN) {
|
|
92
96
|
return false;
|
|
93
97
|
}
|
package/dist/walk.d.ts
CHANGED
|
@@ -1,20 +1,74 @@
|
|
|
1
1
|
import type { CSSNode } from './css-node';
|
|
2
|
-
|
|
2
|
+
export declare const SKIP: unique symbol;
|
|
3
|
+
export declare const BREAK: unique symbol;
|
|
4
|
+
type WalkCallback = (node: CSSNode, depth: number) => void | typeof SKIP | typeof BREAK;
|
|
3
5
|
/**
|
|
4
6
|
* Walk the AST in depth-first order, calling the callback for each node
|
|
7
|
+
*
|
|
5
8
|
* @param node - The root node to start walking from
|
|
6
|
-
* @param callback - Function to call for each node visited. Receives the node and its depth (0 for root)
|
|
9
|
+
* @param callback - Function to call for each node visited. Receives the node and its depth (0 for root).
|
|
10
|
+
* Return SKIP to skip children of current node, or BREAK to stop traversal entirely.
|
|
11
|
+
* @param depth - Starting depth (default: 0)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* import { parse, walk, SKIP, BREAK } from '@projectwallace/css-parser'
|
|
15
|
+
*
|
|
16
|
+
* const ast = parse('.a { .b { .c { color: red; } } }')
|
|
17
|
+
*
|
|
18
|
+
* // Skip nested rules
|
|
19
|
+
* walk(ast, (node) => {
|
|
20
|
+
* if (node.type === STYLE_RULE) {
|
|
21
|
+
* console.log(node.text)
|
|
22
|
+
* return SKIP // Don't visit nested rules
|
|
23
|
+
* }
|
|
24
|
+
* })
|
|
25
|
+
* // Output: .a { ... }, but not .b or .c
|
|
26
|
+
*
|
|
27
|
+
* // Stop on first declaration
|
|
28
|
+
* walk(ast, (node) => {
|
|
29
|
+
* if (node.type === DECLARATION) {
|
|
30
|
+
* console.log(node.name)
|
|
31
|
+
* return BREAK // Stop traversal
|
|
32
|
+
* }
|
|
33
|
+
* })
|
|
7
34
|
*/
|
|
8
|
-
export declare function walk(node: CSSNode, callback: WalkCallback, depth?: number):
|
|
9
|
-
type WalkEnterLeaveCallback = (node: CSSNode) => void;
|
|
35
|
+
export declare function walk(node: CSSNode, callback: WalkCallback, depth?: number): boolean;
|
|
36
|
+
type WalkEnterLeaveCallback = (node: CSSNode) => void | typeof SKIP | typeof BREAK;
|
|
10
37
|
interface WalkEnterLeaveOptions {
|
|
11
38
|
enter?: WalkEnterLeaveCallback;
|
|
12
39
|
leave?: WalkEnterLeaveCallback;
|
|
13
40
|
}
|
|
14
41
|
/**
|
|
15
42
|
* Walk the AST in depth-first order, calling enter before visiting children and leave after
|
|
43
|
+
*
|
|
16
44
|
* @param node - The root node to start walking from
|
|
17
45
|
* @param options - Object with optional enter and leave callback functions
|
|
46
|
+
* @param options.enter - Called before visiting children. Return SKIP to skip children (leave still called),
|
|
47
|
+
* or BREAK to stop traversal entirely (leave NOT called).
|
|
48
|
+
* @param options.leave - Called after visiting children. Return BREAK to stop traversal.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* import { parse, traverse, SKIP, BREAK } from '@projectwallace/css-parser'
|
|
52
|
+
*
|
|
53
|
+
* const ast = parse('@media screen { .a { color: red; } }')
|
|
54
|
+
*
|
|
55
|
+
* // Track context with skip
|
|
56
|
+
* let depth = 0
|
|
57
|
+
* traverse(ast, {
|
|
58
|
+
* enter(node) {
|
|
59
|
+
* depth++
|
|
60
|
+
* if (node.type === AT_RULE) {
|
|
61
|
+
* console.log('Entering media query at depth', depth)
|
|
62
|
+
* return SKIP // Skip contents but still call leave
|
|
63
|
+
* }
|
|
64
|
+
* },
|
|
65
|
+
* leave(node) {
|
|
66
|
+
* if (node.type === AT_RULE) {
|
|
67
|
+
* console.log('Leaving media query at depth', depth)
|
|
68
|
+
* }
|
|
69
|
+
* depth--
|
|
70
|
+
* }
|
|
71
|
+
* })
|
|
18
72
|
*/
|
|
19
|
-
export declare function traverse(node: CSSNode, { enter, leave }?: WalkEnterLeaveOptions):
|
|
73
|
+
export declare function traverse(node: CSSNode, { enter, leave }?: WalkEnterLeaveOptions): boolean;
|
|
20
74
|
export {};
|
package/dist/walk.js
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
|
+
const SKIP = Symbol("SKIP");
|
|
2
|
+
const BREAK = Symbol("BREAK");
|
|
1
3
|
function walk(node, callback, depth = 0) {
|
|
2
|
-
callback(node, depth);
|
|
4
|
+
const result = callback(node, depth);
|
|
5
|
+
if (result === BREAK) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (result === SKIP) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
3
11
|
let child = node.first_child;
|
|
4
12
|
while (child) {
|
|
5
|
-
walk(child, callback, depth + 1);
|
|
13
|
+
const should_continue = walk(child, callback, depth + 1);
|
|
14
|
+
if (!should_continue) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
6
17
|
child = child.next_sibling;
|
|
7
18
|
}
|
|
19
|
+
return true;
|
|
8
20
|
}
|
|
9
21
|
const NOOP = function() {
|
|
10
22
|
};
|
|
11
23
|
function traverse(node, { enter = NOOP, leave = NOOP } = {}) {
|
|
12
|
-
enter(node);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
const enter_result = enter(node);
|
|
25
|
+
if (enter_result === BREAK) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (enter_result !== SKIP) {
|
|
29
|
+
let child = node.first_child;
|
|
30
|
+
while (child) {
|
|
31
|
+
const should_continue = traverse(child, { enter, leave });
|
|
32
|
+
if (!should_continue) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
child = child.next_sibling;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const leave_result = leave(node);
|
|
39
|
+
if (leave_result === BREAK) {
|
|
40
|
+
return false;
|
|
17
41
|
}
|
|
18
|
-
|
|
42
|
+
return true;
|
|
19
43
|
}
|
|
20
44
|
|
|
21
|
-
export { traverse, walk };
|
|
45
|
+
export { BREAK, SKIP, traverse, walk };
|
package/package.json
CHANGED