@projectwallace/css-parser 0.8.5 → 0.8.7
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 +1 -0
- package/dist/arena.js +27 -22
- package/dist/css-node.d.ts +5 -15
- package/dist/css-node.js +12 -17
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/parse-anplusb.js +1 -1
- package/dist/parse-atrule-prelude.js +1 -1
- package/dist/parse-declaration.js +50 -7
- package/dist/parse-selector.js +1 -1
- package/dist/parse-value.js +1 -1
- package/dist/parse.js +14 -5
- package/dist/string-utils.d.ts +14 -0
- package/dist/string-utils.js +5 -1
- package/dist/tokenize.d.ts +11 -1
- package/dist/tokenize.js +429 -3
- package/dist/walk.d.ts +5 -52
- package/dist/walk.js +2 -2
- package/package.json +7 -1
- package/dist/lexer.d.ts +0 -11
- package/dist/lexer.js +0 -431
package/dist/arena.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export declare const FLAG_HAS_BLOCK: number;
|
|
|
41
41
|
export declare const FLAG_VENDOR_PREFIXED: number;
|
|
42
42
|
export declare const FLAG_HAS_DECLARATIONS: number;
|
|
43
43
|
export declare const FLAG_HAS_PARENS: number;
|
|
44
|
+
export declare const FLAG_BROWSERHACK: number;
|
|
44
45
|
export declare const ATTR_OPERATOR_NONE = 0;
|
|
45
46
|
export declare const ATTR_OPERATOR_EQUAL = 1;
|
|
46
47
|
export declare const ATTR_OPERATOR_TILDE_EQUAL = 2;
|
package/dist/arena.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
let BYTES_PER_NODE =
|
|
1
|
+
let BYTES_PER_NODE = 36;
|
|
2
2
|
const STYLESHEET = 1;
|
|
3
3
|
const STYLE_RULE = 2;
|
|
4
4
|
const AT_RULE = 3;
|
|
@@ -37,9 +37,11 @@ const LAYER_NAME = 37;
|
|
|
37
37
|
const PRELUDE_OPERATOR = 38;
|
|
38
38
|
const FLAG_IMPORTANT = 1 << 0;
|
|
39
39
|
const FLAG_HAS_ERROR = 1 << 1;
|
|
40
|
+
const FLAG_LENGTH_OVERFLOW = 1 << 2;
|
|
40
41
|
const FLAG_HAS_BLOCK = 1 << 3;
|
|
41
42
|
const FLAG_HAS_DECLARATIONS = 1 << 5;
|
|
42
43
|
const FLAG_HAS_PARENS = 1 << 6;
|
|
44
|
+
const FLAG_BROWSERHACK = 1 << 7;
|
|
43
45
|
const ATTR_OPERATOR_NONE = 0;
|
|
44
46
|
const ATTR_OPERATOR_EQUAL = 1;
|
|
45
47
|
const ATTR_OPERATOR_TILDE_EQUAL = 2;
|
|
@@ -59,6 +61,8 @@ class CSSDataArena {
|
|
|
59
61
|
// Number of nodes currently allocated
|
|
60
62
|
growth_count;
|
|
61
63
|
// Number of times the arena has grown
|
|
64
|
+
overflow_lengths;
|
|
65
|
+
// Stores actual lengths for nodes > 65535 chars
|
|
62
66
|
// Growth multiplier when capacity is exceeded
|
|
63
67
|
static GROWTH_FACTOR = 1.3;
|
|
64
68
|
// Estimated nodes per KB of CSS (based on real-world data)
|
|
@@ -71,6 +75,7 @@ class CSSDataArena {
|
|
|
71
75
|
this.growth_count = 0;
|
|
72
76
|
this.buffer = new ArrayBuffer(initial_capacity * BYTES_PER_NODE);
|
|
73
77
|
this.view = new DataView(this.buffer);
|
|
78
|
+
this.overflow_lengths = /* @__PURE__ */ new Map();
|
|
74
79
|
}
|
|
75
80
|
// Calculate recommended initial capacity based on CSS source size
|
|
76
81
|
static capacity_for_source(source_length) {
|
|
@@ -109,6 +114,12 @@ class CSSDataArena {
|
|
|
109
114
|
}
|
|
110
115
|
// Read length in source
|
|
111
116
|
get_length(node_index) {
|
|
117
|
+
if (this.has_flag(node_index, FLAG_LENGTH_OVERFLOW)) {
|
|
118
|
+
const overflow_length = this.overflow_lengths.get(node_index);
|
|
119
|
+
if (overflow_length !== void 0) {
|
|
120
|
+
return overflow_length;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
112
123
|
return this.view.getUint16(this.node_offset(node_index) + 2, true);
|
|
113
124
|
}
|
|
114
125
|
// Read content start offset (stored as delta from startOffset)
|
|
@@ -123,11 +134,11 @@ class CSSDataArena {
|
|
|
123
134
|
}
|
|
124
135
|
// Read attribute operator (for NODE_SELECTOR_ATTRIBUTE)
|
|
125
136
|
get_attr_operator(node_index) {
|
|
126
|
-
return this.view.getUint8(this.node_offset(node_index) +
|
|
137
|
+
return this.view.getUint8(this.node_offset(node_index) + 32);
|
|
127
138
|
}
|
|
128
139
|
// Read attribute flags (for NODE_SELECTOR_ATTRIBUTE)
|
|
129
140
|
get_attr_flags(node_index) {
|
|
130
|
-
return this.view.getUint8(this.node_offset(node_index) +
|
|
141
|
+
return this.view.getUint8(this.node_offset(node_index) + 33);
|
|
131
142
|
}
|
|
132
143
|
// Read first child index (0 = no children)
|
|
133
144
|
get_first_child(node_index) {
|
|
@@ -143,7 +154,7 @@ class CSSDataArena {
|
|
|
143
154
|
}
|
|
144
155
|
// Read start column
|
|
145
156
|
get_start_column(node_index) {
|
|
146
|
-
return this.view.
|
|
157
|
+
return this.view.getUint32(this.node_offset(node_index) + 28, true);
|
|
147
158
|
}
|
|
148
159
|
// Read value start offset (stored as delta from startOffset, declaration value / at-rule prelude)
|
|
149
160
|
get_value_start(node_index) {
|
|
@@ -164,13 +175,15 @@ class CSSDataArena {
|
|
|
164
175
|
set_flags(node_index, flags) {
|
|
165
176
|
this.view.setUint8(this.node_offset(node_index) + 1, flags);
|
|
166
177
|
}
|
|
167
|
-
// Write start offset in source
|
|
168
|
-
set_start_offset(node_index, offset) {
|
|
169
|
-
this.view.setUint32(this.node_offset(node_index) + 12, offset, true);
|
|
170
|
-
}
|
|
171
178
|
// Write length in source
|
|
172
179
|
set_length(node_index, length) {
|
|
173
|
-
|
|
180
|
+
if (length > 65535) {
|
|
181
|
+
this.view.setUint16(this.node_offset(node_index) + 2, 65535, true);
|
|
182
|
+
this.set_flag(node_index, FLAG_LENGTH_OVERFLOW);
|
|
183
|
+
this.overflow_lengths.set(node_index, length);
|
|
184
|
+
} else {
|
|
185
|
+
this.view.setUint16(this.node_offset(node_index) + 2, length, true);
|
|
186
|
+
}
|
|
174
187
|
}
|
|
175
188
|
// Write content start delta (offset from startOffset)
|
|
176
189
|
set_content_start_delta(node_index, delta) {
|
|
@@ -182,11 +195,11 @@ class CSSDataArena {
|
|
|
182
195
|
}
|
|
183
196
|
// Write attribute operator (for NODE_SELECTOR_ATTRIBUTE)
|
|
184
197
|
set_attr_operator(node_index, operator) {
|
|
185
|
-
this.view.setUint8(this.node_offset(node_index) +
|
|
198
|
+
this.view.setUint8(this.node_offset(node_index) + 32, operator);
|
|
186
199
|
}
|
|
187
200
|
// Write attribute flags (for NODE_SELECTOR_ATTRIBUTE)
|
|
188
201
|
set_attr_flags(node_index, flags) {
|
|
189
|
-
this.view.setUint8(this.node_offset(node_index) +
|
|
202
|
+
this.view.setUint8(this.node_offset(node_index) + 33, flags);
|
|
190
203
|
}
|
|
191
204
|
// Write first child index
|
|
192
205
|
set_first_child(node_index, childIndex) {
|
|
@@ -196,14 +209,6 @@ class CSSDataArena {
|
|
|
196
209
|
set_next_sibling(node_index, siblingIndex) {
|
|
197
210
|
this.view.setUint32(this.node_offset(node_index) + 8, siblingIndex, true);
|
|
198
211
|
}
|
|
199
|
-
// Write start line
|
|
200
|
-
set_start_line(node_index, line) {
|
|
201
|
-
this.view.setUint32(this.node_offset(node_index) + 24, line, true);
|
|
202
|
-
}
|
|
203
|
-
// Write start column
|
|
204
|
-
set_start_column(node_index, column) {
|
|
205
|
-
this.view.setUint16(this.node_offset(node_index) + 28, column, true);
|
|
206
|
-
}
|
|
207
212
|
// Write value start delta (offset from startOffset, declaration value / at-rule prelude)
|
|
208
213
|
set_value_start_delta(node_index, delta) {
|
|
209
214
|
this.view.setUint16(this.node_offset(node_index) + 18, delta, true);
|
|
@@ -233,10 +238,10 @@ class CSSDataArena {
|
|
|
233
238
|
this.count++;
|
|
234
239
|
const offset = node_index * BYTES_PER_NODE;
|
|
235
240
|
this.view.setUint8(offset, type);
|
|
236
|
-
this.view.setUint16(offset + 2, length, true);
|
|
237
241
|
this.view.setUint32(offset + 12, start_offset, true);
|
|
238
242
|
this.view.setUint32(offset + 24, start_line, true);
|
|
239
|
-
this.view.
|
|
243
|
+
this.view.setUint32(offset + 28, start_column, true);
|
|
244
|
+
this.set_length(node_index, length);
|
|
240
245
|
return node_index;
|
|
241
246
|
}
|
|
242
247
|
// --- Tree Building Helpers ---
|
|
@@ -275,4 +280,4 @@ class CSSDataArena {
|
|
|
275
280
|
}
|
|
276
281
|
}
|
|
277
282
|
|
|
278
|
-
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 };
|
|
283
|
+
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_BROWSERHACK, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, FLAG_HAS_ERROR, FLAG_HAS_PARENS, FLAG_IMPORTANT, FLAG_LENGTH_OVERFLOW, 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/css-node.d.ts
CHANGED
|
@@ -64,6 +64,7 @@ export type PlainCSSNode = {
|
|
|
64
64
|
prelude?: string;
|
|
65
65
|
is_important?: boolean;
|
|
66
66
|
is_vendor_prefixed?: boolean;
|
|
67
|
+
is_browserhack?: boolean;
|
|
67
68
|
has_error?: boolean;
|
|
68
69
|
attr_operator?: number;
|
|
69
70
|
attr_flags?: number;
|
|
@@ -124,6 +125,8 @@ export declare class CSSNode {
|
|
|
124
125
|
get unit(): string | null;
|
|
125
126
|
/** Check if this declaration has !important */
|
|
126
127
|
get is_important(): boolean | null;
|
|
128
|
+
/** Check if this declaration has a browser hack prefix */
|
|
129
|
+
get is_browserhack(): boolean | null;
|
|
127
130
|
/** Check if this has a vendor prefix (computed on-demand) */
|
|
128
131
|
get is_vendor_prefixed(): boolean;
|
|
129
132
|
/** Check if this node has an error */
|
|
@@ -202,25 +205,12 @@ export declare class CSSNode {
|
|
|
202
205
|
/** Get text of first compound selector (no node allocation) */
|
|
203
206
|
get first_compound_text(): string;
|
|
204
207
|
/**
|
|
205
|
-
* Clone this node as a mutable plain JavaScript object
|
|
206
|
-
*
|
|
207
|
-
* Extracts all properties from the arena into a plain object with children as an array.
|
|
208
|
-
* The resulting object can be freely modified.
|
|
208
|
+
* Clone this node as a mutable plain JavaScript object with children as arrays.
|
|
209
|
+
* See API.md for examples.
|
|
209
210
|
*
|
|
210
211
|
* @param options - Cloning configuration
|
|
211
212
|
* @param options.deep - Recursively clone children (default: true)
|
|
212
213
|
* @param options.locations - Include line/column/start/length (default: false)
|
|
213
|
-
* @returns Plain object with children as array
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* const ast = parse('div { color: red; }')
|
|
217
|
-
* const decl = ast.first_child.block.first_child
|
|
218
|
-
* const plain = decl.clone()
|
|
219
|
-
*
|
|
220
|
-
* // Access children as array
|
|
221
|
-
* plain.children.length
|
|
222
|
-
* plain.children[0]
|
|
223
|
-
* plain.children.push(newChild)
|
|
224
214
|
*/
|
|
225
215
|
clone(options?: CloneOptions): PlainCSSNode;
|
|
226
216
|
}
|
package/dist/css-node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
1
|
+
import { DIMENSION, NUMBER, URL, STRING, DECLARATION, FLAG_IMPORTANT, FLAG_BROWSERHACK, 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
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
|
|
|
@@ -160,6 +160,11 @@ class CSSNode {
|
|
|
160
160
|
if (this.type !== DECLARATION) return null;
|
|
161
161
|
return this.arena.has_flag(this.index, FLAG_IMPORTANT);
|
|
162
162
|
}
|
|
163
|
+
/** Check if this declaration has a browser hack prefix */
|
|
164
|
+
get is_browserhack() {
|
|
165
|
+
if (this.type !== DECLARATION) return null;
|
|
166
|
+
return this.arena.has_flag(this.index, FLAG_BROWSERHACK);
|
|
167
|
+
}
|
|
163
168
|
/** Check if this has a vendor prefix (computed on-demand) */
|
|
164
169
|
get is_vendor_prefixed() {
|
|
165
170
|
switch (this.type) {
|
|
@@ -454,25 +459,12 @@ class CSSNode {
|
|
|
454
459
|
}
|
|
455
460
|
// --- Node Cloning ---
|
|
456
461
|
/**
|
|
457
|
-
* Clone this node as a mutable plain JavaScript object
|
|
458
|
-
*
|
|
459
|
-
* Extracts all properties from the arena into a plain object with children as an array.
|
|
460
|
-
* The resulting object can be freely modified.
|
|
462
|
+
* Clone this node as a mutable plain JavaScript object with children as arrays.
|
|
463
|
+
* See API.md for examples.
|
|
461
464
|
*
|
|
462
465
|
* @param options - Cloning configuration
|
|
463
466
|
* @param options.deep - Recursively clone children (default: true)
|
|
464
467
|
* @param options.locations - Include line/column/start/length (default: false)
|
|
465
|
-
* @returns Plain object with children as array
|
|
466
|
-
*
|
|
467
|
-
* @example
|
|
468
|
-
* const ast = parse('div { color: red; }')
|
|
469
|
-
* const decl = ast.first_child.block.first_child
|
|
470
|
-
* const plain = decl.clone()
|
|
471
|
-
*
|
|
472
|
-
* // Access children as array
|
|
473
|
-
* plain.children.length
|
|
474
|
-
* plain.children[0]
|
|
475
|
-
* plain.children.push(newChild)
|
|
476
468
|
*/
|
|
477
469
|
clone(options = {}) {
|
|
478
470
|
const { deep = true, locations = false } = options;
|
|
@@ -491,7 +483,10 @@ class CSSNode {
|
|
|
491
483
|
if (this.type === AT_RULE && this.prelude) {
|
|
492
484
|
plain.prelude = this.prelude;
|
|
493
485
|
}
|
|
494
|
-
if (this.type === DECLARATION)
|
|
486
|
+
if (this.type === DECLARATION) {
|
|
487
|
+
plain.is_important = this.is_important;
|
|
488
|
+
plain.is_browserhack = this.is_browserhack;
|
|
489
|
+
}
|
|
495
490
|
plain.is_vendor_prefixed = this.is_vendor_prefixed;
|
|
496
491
|
plain.has_error = this.has_error;
|
|
497
492
|
if (this.type === ATTRIBUTE_SELECTOR) {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ export { parse_declaration } from './parse-declaration';
|
|
|
5
5
|
export { parse_value } from './parse-value';
|
|
6
6
|
export { tokenize } from './tokenize';
|
|
7
7
|
export { walk, traverse, SKIP, BREAK } from './walk';
|
|
8
|
+
export { is_custom, is_vendor_prefixed, str_equals, str_starts_with, str_index_of } from './string-utils';
|
|
8
9
|
export { type ParserOptions } from './parse';
|
|
9
10
|
export { CSSNode, type CSSNodeType, TYPE_NAMES, type CloneOptions, type PlainCSSNode } from './css-node';
|
|
10
|
-
export type { LexerPosition } from './
|
|
11
|
+
export type { LexerPosition } from './tokenize';
|
|
11
12
|
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';
|
|
12
13
|
export * from './constants';
|
|
13
14
|
export * from './token-types';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { parse_declaration } from './parse-declaration.js';
|
|
|
5
5
|
export { parse_value } from './parse-value.js';
|
|
6
6
|
export { tokenize } from './tokenize.js';
|
|
7
7
|
export { BREAK, SKIP, traverse, walk } from './walk.js';
|
|
8
|
+
export { is_custom, is_vendor_prefixed, str_equals, str_index_of, str_starts_with } from './string-utils.js';
|
|
8
9
|
export { CSSNode, TYPE_NAMES } from './css-node.js';
|
|
9
10
|
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';
|
|
10
11
|
export { NODE_TYPES } from './constants.js';
|
package/dist/parse-anplusb.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Lexer } from './
|
|
1
|
+
import { Lexer } from './tokenize.js';
|
|
2
2
|
import { NTH_SELECTOR, CSSDataArena } from './arena.js';
|
|
3
3
|
import { TOKEN_IDENT, TOKEN_DELIM, TOKEN_DIMENSION, TOKEN_NUMBER } from './token-types.js';
|
|
4
4
|
import { str_equals, CHAR_MINUS_HYPHEN, CHAR_PLUS, str_index_of } from './string-utils.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Lexer } from './
|
|
1
|
+
import { Lexer } from './tokenize.js';
|
|
2
2
|
import { CSSDataArena, PRELUDE_OPERATOR, MEDIA_TYPE, MEDIA_QUERY, MEDIA_FEATURE, IDENTIFIER, CONTAINER_QUERY, SUPPORTS_QUERY, LAYER_NAME, URL } from './arena.js';
|
|
3
3
|
import { TOKEN_COMMA, TOKEN_IDENT, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_WHITESPACE, TOKEN_URL, TOKEN_FUNCTION, TOKEN_STRING, TOKEN_EOF } from './token-types.js';
|
|
4
4
|
import { str_equals } from './string-utils.js';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Lexer } from './
|
|
2
|
-
import { CSSDataArena, DECLARATION, FLAG_IMPORTANT } from './arena.js';
|
|
1
|
+
import { Lexer } from './tokenize.js';
|
|
2
|
+
import { CSSDataArena, DECLARATION, FLAG_IMPORTANT, FLAG_BROWSERHACK } from './arena.js';
|
|
3
3
|
import { ValueParser } from './parse-value.js';
|
|
4
|
-
import {
|
|
4
|
+
import { is_vendor_prefixed } from './string-utils.js';
|
|
5
|
+
import { TOKEN_AT_KEYWORD, TOKEN_HASH, TOKEN_IDENT, TOKEN_DELIM, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, TOKEN_COMMA, TOKEN_COLON, TOKEN_EOF, TOKEN_SEMICOLON, TOKEN_RIGHT_BRACE, TOKEN_LEFT_BRACE } from './token-types.js';
|
|
5
6
|
import { trim_boundaries } from './parse-utils.js';
|
|
6
7
|
import { CSSNode } from './css-node.js';
|
|
7
8
|
|
|
@@ -25,13 +26,52 @@ class DeclarationParser {
|
|
|
25
26
|
}
|
|
26
27
|
// Parse a declaration using a provided lexer (used by Parser to avoid re-tokenization)
|
|
27
28
|
parse_declaration_with_lexer(lexer, end) {
|
|
28
|
-
|
|
29
|
+
let has_browser_hack = false;
|
|
30
|
+
let browser_hack_start = 0;
|
|
31
|
+
let browser_hack_line = 1;
|
|
32
|
+
let browser_hack_column = 1;
|
|
33
|
+
if (lexer.token_type === TOKEN_AT_KEYWORD || lexer.token_type === TOKEN_HASH) {
|
|
34
|
+
has_browser_hack = true;
|
|
35
|
+
browser_hack_start = lexer.token_start;
|
|
36
|
+
browser_hack_line = lexer.token_line;
|
|
37
|
+
browser_hack_column = lexer.token_column;
|
|
38
|
+
} else if (lexer.token_type === TOKEN_IDENT) {
|
|
39
|
+
const first_char = this.source.charCodeAt(lexer.token_start);
|
|
40
|
+
if (first_char === 95) {
|
|
41
|
+
has_browser_hack = true;
|
|
42
|
+
browser_hack_start = lexer.token_start;
|
|
43
|
+
browser_hack_line = lexer.token_line;
|
|
44
|
+
browser_hack_column = lexer.token_column;
|
|
45
|
+
} else if (first_char === 45) {
|
|
46
|
+
if (!is_vendor_prefixed(this.source, lexer.token_start, lexer.token_end)) {
|
|
47
|
+
has_browser_hack = true;
|
|
48
|
+
browser_hack_start = lexer.token_start;
|
|
49
|
+
browser_hack_line = lexer.token_line;
|
|
50
|
+
browser_hack_column = lexer.token_column;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
const is_browser_hack_token = lexer.token_type === TOKEN_DELIM || lexer.token_type === TOKEN_LEFT_PAREN || lexer.token_type === TOKEN_RIGHT_PAREN || lexer.token_type === TOKEN_LEFT_BRACKET || lexer.token_type === TOKEN_RIGHT_BRACKET || lexer.token_type === TOKEN_COMMA || lexer.token_type === TOKEN_COLON;
|
|
55
|
+
if (is_browser_hack_token) {
|
|
56
|
+
const delim_saved = lexer.save_position();
|
|
57
|
+
browser_hack_start = lexer.token_start;
|
|
58
|
+
browser_hack_line = lexer.token_line;
|
|
59
|
+
browser_hack_column = lexer.token_column;
|
|
60
|
+
lexer.next_token_fast(true);
|
|
61
|
+
if (lexer.token_type === TOKEN_IDENT) {
|
|
62
|
+
has_browser_hack = true;
|
|
63
|
+
} else {
|
|
64
|
+
lexer.restore_position(delim_saved);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (lexer.token_type !== TOKEN_IDENT && lexer.token_type !== TOKEN_AT_KEYWORD && lexer.token_type !== TOKEN_HASH) {
|
|
29
69
|
return null;
|
|
30
70
|
}
|
|
31
|
-
let prop_start = lexer.token_start;
|
|
71
|
+
let prop_start = has_browser_hack ? browser_hack_start : lexer.token_start;
|
|
32
72
|
let prop_end = lexer.token_end;
|
|
33
|
-
let decl_line = lexer.token_line;
|
|
34
|
-
let decl_column = lexer.token_column;
|
|
73
|
+
let decl_line = has_browser_hack ? browser_hack_line : lexer.token_line;
|
|
74
|
+
let decl_column = has_browser_hack ? browser_hack_column : lexer.token_column;
|
|
35
75
|
const saved = lexer.save_position();
|
|
36
76
|
lexer.next_token_fast(true);
|
|
37
77
|
if (lexer.token_type !== TOKEN_COLON) {
|
|
@@ -92,6 +132,9 @@ class DeclarationParser {
|
|
|
92
132
|
if (has_important) {
|
|
93
133
|
this.arena.set_flag(declaration, FLAG_IMPORTANT);
|
|
94
134
|
}
|
|
135
|
+
if (has_browser_hack) {
|
|
136
|
+
this.arena.set_flag(declaration, FLAG_BROWSERHACK);
|
|
137
|
+
}
|
|
95
138
|
if (lexer.token_type === TOKEN_SEMICOLON) {
|
|
96
139
|
last_end = lexer.token_end;
|
|
97
140
|
lexer.next_token_fast(true);
|
package/dist/parse-selector.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Lexer } from './
|
|
1
|
+
import { Lexer } from './tokenize.js';
|
|
2
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';
|
package/dist/parse-value.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Lexer } from './
|
|
1
|
+
import { Lexer } from './tokenize.js';
|
|
2
2
|
import { CSSDataArena, OPERATOR, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, URL, FUNCTION, PARENTHESIS } from './arena.js';
|
|
3
3
|
import { TOKEN_EOF, TOKEN_LEFT_PAREN, TOKEN_COMMA, TOKEN_DELIM, TOKEN_FUNCTION, TOKEN_HASH, TOKEN_STRING, TOKEN_DIMENSION, TOKEN_PERCENTAGE, TOKEN_NUMBER, TOKEN_IDENT, TOKEN_RIGHT_PAREN } from './token-types.js';
|
|
4
4
|
import { is_whitespace, CHAR_PLUS, CHAR_MINUS_HYPHEN, CHAR_ASTERISK, CHAR_FORWARD_SLASH, str_equals } from './string-utils.js';
|
package/dist/parse.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { Lexer } from './
|
|
1
|
+
import { Lexer } from './tokenize.js';
|
|
2
2
|
import { CSSDataArena, STYLESHEET, STYLE_RULE, FLAG_HAS_BLOCK, BLOCK, FLAG_HAS_DECLARATIONS, SELECTOR_LIST, AT_RULE } from './arena.js';
|
|
3
3
|
import { CSSNode } from './css-node.js';
|
|
4
4
|
import { SelectorParser } from './parse-selector.js';
|
|
5
5
|
import { AtRulePreludeParser } from './parse-atrule-prelude.js';
|
|
6
6
|
import { DeclarationParser } from './parse-declaration.js';
|
|
7
|
-
import { TOKEN_EOF, TOKEN_AT_KEYWORD, TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE, TOKEN_IDENT, TOKEN_SEMICOLON } from './token-types.js';
|
|
7
|
+
import { TOKEN_EOF, TOKEN_AT_KEYWORD, TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE, TOKEN_IDENT, TOKEN_HASH, TOKEN_DELIM, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, TOKEN_COMMA, TOKEN_COLON, TOKEN_SEMICOLON } from './token-types.js';
|
|
8
8
|
import { trim_boundaries } from './parse-utils.js';
|
|
9
|
+
import { CHAR_PERIOD, CHAR_GREATER_THAN, CHAR_PLUS, CHAR_TILDE, CHAR_AMPERSAND } from './string-utils.js';
|
|
9
10
|
|
|
10
11
|
let DECLARATION_AT_RULES = /* @__PURE__ */ new Set(["font-face", "font-feature-values", "page", "property", "counter-style"]);
|
|
11
12
|
let CONDITIONAL_AT_RULES = /* @__PURE__ */ new Set(["media", "supports", "container", "layer", "nest"]);
|
|
@@ -176,10 +177,18 @@ class Parser {
|
|
|
176
177
|
}
|
|
177
178
|
// Parse a declaration: property: value;
|
|
178
179
|
parse_declaration() {
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
const token_type = this.peek_type();
|
|
181
|
+
if (token_type === TOKEN_IDENT || token_type === TOKEN_AT_KEYWORD || token_type === TOKEN_HASH) {
|
|
182
|
+
return this.declaration_parser.parse_declaration_with_lexer(this.lexer, this.source.length);
|
|
183
|
+
}
|
|
184
|
+
if (token_type === TOKEN_DELIM || token_type === TOKEN_LEFT_PAREN || token_type === TOKEN_RIGHT_PAREN || token_type === TOKEN_LEFT_BRACKET || token_type === TOKEN_RIGHT_BRACKET || token_type === TOKEN_COMMA || token_type === TOKEN_COLON) {
|
|
185
|
+
const char_code = this.source.charCodeAt(this.lexer.token_start);
|
|
186
|
+
if (char_code === CHAR_PERIOD || char_code === CHAR_GREATER_THAN || char_code === CHAR_PLUS || char_code === CHAR_TILDE || char_code === CHAR_AMPERSAND) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
return this.declaration_parser.parse_declaration_with_lexer(this.lexer, this.source.length);
|
|
181
190
|
}
|
|
182
|
-
return
|
|
191
|
+
return null;
|
|
183
192
|
}
|
|
184
193
|
// Parse an at-rule: @media, @import, @font-face, etc.
|
|
185
194
|
parse_atrule() {
|
package/dist/string-utils.d.ts
CHANGED
|
@@ -67,3 +67,17 @@ export declare function str_index_of(str: string, searchChar: string): number;
|
|
|
67
67
|
*/
|
|
68
68
|
export declare function is_vendor_prefixed(text: string): boolean;
|
|
69
69
|
export declare function is_vendor_prefixed(source: string, start: number, end: number): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Check if a string is a CSS custom property (starts with --)
|
|
72
|
+
*
|
|
73
|
+
* @param str - The string to check
|
|
74
|
+
* @returns true if the string starts with -- (custom property)
|
|
75
|
+
*
|
|
76
|
+
* Examples:
|
|
77
|
+
* - `--primary-color` → true
|
|
78
|
+
* - `--my-var` → true
|
|
79
|
+
* - `-webkit-transform` → false (vendor prefix, not custom)
|
|
80
|
+
* - `border-radius` → false (standard property)
|
|
81
|
+
* - `color` → false
|
|
82
|
+
*/
|
|
83
|
+
export declare function is_custom(str: string): boolean;
|
package/dist/string-utils.js
CHANGED
|
@@ -109,5 +109,9 @@ function is_vendor_prefixed(source, start, end) {
|
|
|
109
109
|
}
|
|
110
110
|
return false;
|
|
111
111
|
}
|
|
112
|
+
function is_custom(str) {
|
|
113
|
+
if (str.length < 3) return false;
|
|
114
|
+
return str.charCodeAt(0) === CHAR_MINUS_HYPHEN && str.charCodeAt(1) === CHAR_MINUS_HYPHEN;
|
|
115
|
+
}
|
|
112
116
|
|
|
113
|
-
export { CHAR_AMPERSAND, CHAR_ASTERISK, CHAR_CARET, CHAR_CARRIAGE_RETURN, CHAR_COLON, CHAR_DOLLAR, CHAR_DOUBLE_QUOTE, CHAR_EQUALS, CHAR_FORM_FEED, CHAR_FORWARD_SLASH, CHAR_GREATER_THAN, CHAR_MINUS_HYPHEN, CHAR_NEWLINE, CHAR_PERIOD, CHAR_PIPE, CHAR_PLUS, CHAR_SINGLE_QUOTE, CHAR_SPACE, CHAR_TAB, CHAR_TILDE, is_combinator, is_digit, is_vendor_prefixed, is_whitespace, str_equals, str_index_of, str_starts_with };
|
|
117
|
+
export { CHAR_AMPERSAND, CHAR_ASTERISK, CHAR_CARET, CHAR_CARRIAGE_RETURN, CHAR_COLON, CHAR_DOLLAR, CHAR_DOUBLE_QUOTE, CHAR_EQUALS, CHAR_FORM_FEED, CHAR_FORWARD_SLASH, CHAR_GREATER_THAN, CHAR_MINUS_HYPHEN, CHAR_NEWLINE, CHAR_PERIOD, CHAR_PIPE, CHAR_PLUS, CHAR_SINGLE_QUOTE, CHAR_SPACE, CHAR_TAB, CHAR_TILDE, is_combinator, is_custom, is_digit, is_vendor_prefixed, is_whitespace, str_equals, str_index_of, str_starts_with };
|
package/dist/tokenize.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Token, type TokenType } from './token-types';
|
|
2
|
+
export interface LexerPosition {
|
|
3
|
+
pos: number;
|
|
4
|
+
line: number;
|
|
5
|
+
column: number;
|
|
6
|
+
token_type: TokenType;
|
|
7
|
+
token_start: number;
|
|
8
|
+
token_end: number;
|
|
9
|
+
token_line: number;
|
|
10
|
+
token_column: number;
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Tokenize CSS source code
|
|
4
14
|
* @param source - The CSS source code to tokenize
|