@projectwallace/css-parser 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/arena.d.ts +2 -0
- package/dist/arena.js +6 -2
- package/dist/char-types.d.ts +6 -0
- package/dist/char-types.js +1 -1
- package/dist/constants.d.ts +4 -2
- package/dist/constants.js +5 -3
- package/dist/css-node.d.ts +23 -40
- package/dist/css-node.js +47 -116
- package/dist/index.js +1 -1
- package/dist/parse-declaration.js +6 -2
- package/dist/parse-selector.js +9 -3
- package/dist/parse-value.d.ts +3 -3
- package/dist/parse-value.js +16 -6
- package/dist/parse.js +28 -11
- package/dist/tokenize.js +70 -55
- package/package.json +3 -2
package/dist/arena.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const FUNCTION = 15;
|
|
|
14
14
|
export declare const OPERATOR = 16;
|
|
15
15
|
export declare const PARENTHESIS = 17;
|
|
16
16
|
export declare const URL = 18;
|
|
17
|
+
export declare const VALUE = 19;
|
|
17
18
|
export declare const SELECTOR_LIST = 20;
|
|
18
19
|
export declare const TYPE_SELECTOR = 21;
|
|
19
20
|
export declare const CLASS_SELECTOR = 22;
|
|
@@ -35,6 +36,7 @@ export declare const SUPPORTS_QUERY = 36;
|
|
|
35
36
|
export declare const LAYER_NAME = 37;
|
|
36
37
|
export declare const PRELUDE_OPERATOR = 38;
|
|
37
38
|
export declare const FEATURE_RANGE = 39;
|
|
39
|
+
export declare const AT_RULE_PRELUDE = 40;
|
|
38
40
|
export declare const FLAG_IMPORTANT: number;
|
|
39
41
|
export declare const FLAG_HAS_ERROR: number;
|
|
40
42
|
export declare const FLAG_LENGTH_OVERFLOW: number;
|
package/dist/arena.js
CHANGED
|
@@ -15,6 +15,7 @@ const FUNCTION = 15;
|
|
|
15
15
|
const OPERATOR = 16;
|
|
16
16
|
const PARENTHESIS = 17;
|
|
17
17
|
const URL = 18;
|
|
18
|
+
const VALUE = 19;
|
|
18
19
|
const SELECTOR_LIST = 20;
|
|
19
20
|
const TYPE_SELECTOR = 21;
|
|
20
21
|
const CLASS_SELECTOR = 22;
|
|
@@ -36,6 +37,7 @@ const SUPPORTS_QUERY = 36;
|
|
|
36
37
|
const LAYER_NAME = 37;
|
|
37
38
|
const PRELUDE_OPERATOR = 38;
|
|
38
39
|
const FEATURE_RANGE = 39;
|
|
40
|
+
const AT_RULE_PRELUDE = 40;
|
|
39
41
|
const FLAG_IMPORTANT = 1 << 0;
|
|
40
42
|
const FLAG_HAS_ERROR = 1 << 1;
|
|
41
43
|
const FLAG_LENGTH_OVERFLOW = 1 << 2;
|
|
@@ -67,7 +69,9 @@ class CSSDataArena {
|
|
|
67
69
|
// Growth multiplier when capacity is exceeded
|
|
68
70
|
static GROWTH_FACTOR = 1.3;
|
|
69
71
|
// Estimated nodes per KB of CSS (based on real-world data)
|
|
70
|
-
|
|
72
|
+
// Increased from 270 to 325 to account for VALUE wrapper nodes
|
|
73
|
+
// (~20% of nodes are declarations, +1 VALUE node per declaration = +20% nodes)
|
|
74
|
+
static NODES_PER_KB = 325;
|
|
71
75
|
// Buffer to avoid frequent growth (15%)
|
|
72
76
|
static CAPACITY_BUFFER = 1.2;
|
|
73
77
|
constructor(initial_capacity = 1024) {
|
|
@@ -281,4 +285,4 @@ class CSSDataArena {
|
|
|
281
285
|
}
|
|
282
286
|
}
|
|
283
287
|
|
|
284
|
-
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, FEATURE_RANGE, 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 };
|
|
288
|
+
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, AT_RULE_PRELUDE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, CSSDataArena, DECLARATION, DIMENSION, FEATURE_RANGE, 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, VALUE };
|
package/dist/char-types.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export declare let CHAR_ALPHA: number;
|
|
2
|
+
export declare let CHAR_DIGIT: number;
|
|
3
|
+
export declare let CHAR_HEX: number;
|
|
4
|
+
export declare let CHAR_WHITESPACE: number;
|
|
5
|
+
export declare let CHAR_NEWLINE: number;
|
|
6
|
+
export declare let char_types: Uint8Array<ArrayBuffer>;
|
|
1
7
|
export declare function is_digit(ch: number): boolean;
|
|
2
8
|
export declare function is_hex_digit(ch: number): boolean;
|
|
3
9
|
export declare function is_alpha(ch: number): boolean;
|
package/dist/char-types.js
CHANGED
|
@@ -52,4 +52,4 @@ function is_ident_char(ch) {
|
|
|
52
52
|
return is_ident_start(ch) || is_digit(ch);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export { is_alpha, is_digit, is_hex_digit, is_ident_char, is_ident_start, is_newline, is_whitespace };
|
|
55
|
+
export { CHAR_ALPHA, CHAR_DIGIT, CHAR_HEX, CHAR_NEWLINE, CHAR_WHITESPACE, char_types, is_alpha, is_digit, is_hex_digit, is_ident_char, is_ident_start, is_newline, is_whitespace };
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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, FEATURE_RANGE, 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, FEATURE_RANGE, 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, };
|
|
1
|
+
import { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, VALUE, 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, FEATURE_RANGE, AT_RULE_PRELUDE, 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, VALUE, 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, FEATURE_RANGE, AT_RULE_PRELUDE, 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
3
|
export declare const NODE_TYPES: {
|
|
4
4
|
readonly STYLESHEET: 1;
|
|
5
5
|
readonly STYLE_RULE: 2;
|
|
@@ -17,6 +17,7 @@ export declare const NODE_TYPES: {
|
|
|
17
17
|
readonly OPERATOR: 16;
|
|
18
18
|
readonly PARENTHESIS: 17;
|
|
19
19
|
readonly URL: 18;
|
|
20
|
+
readonly VALUE: 19;
|
|
20
21
|
readonly SELECTOR_LIST: 20;
|
|
21
22
|
readonly TYPE_SELECTOR: 21;
|
|
22
23
|
readonly CLASS_SELECTOR: 22;
|
|
@@ -38,4 +39,5 @@ export declare const NODE_TYPES: {
|
|
|
38
39
|
readonly LAYER_NAME: 37;
|
|
39
40
|
readonly PRELUDE_OPERATOR: 38;
|
|
40
41
|
readonly FEATURE_RANGE: 39;
|
|
42
|
+
readonly AT_RULE_PRELUDE: 40;
|
|
41
43
|
};
|
package/dist/constants.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FEATURE_RANGE, 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';
|
|
1
|
+
import { AT_RULE_PRELUDE, FEATURE_RANGE, 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, VALUE, URL, PARENTHESIS, OPERATOR, FUNCTION, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, BLOCK, COMMENT, SELECTOR, DECLARATION, AT_RULE, STYLE_RULE, STYLESHEET } from './arena.js';
|
|
2
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
3
|
|
|
4
4
|
const NODE_TYPES = {
|
|
@@ -20,6 +20,7 @@ const NODE_TYPES = {
|
|
|
20
20
|
OPERATOR,
|
|
21
21
|
PARENTHESIS,
|
|
22
22
|
URL,
|
|
23
|
+
VALUE,
|
|
23
24
|
// Selector nodes
|
|
24
25
|
SELECTOR_LIST,
|
|
25
26
|
TYPE_SELECTOR,
|
|
@@ -42,7 +43,8 @@ const NODE_TYPES = {
|
|
|
42
43
|
SUPPORTS_QUERY,
|
|
43
44
|
LAYER_NAME,
|
|
44
45
|
PRELUDE_OPERATOR,
|
|
45
|
-
FEATURE_RANGE
|
|
46
|
+
FEATURE_RANGE,
|
|
47
|
+
AT_RULE_PRELUDE
|
|
46
48
|
};
|
|
47
49
|
|
|
48
|
-
export { ATTRIBUTE_SELECTOR, AT_RULE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FEATURE_RANGE, 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 };
|
|
50
|
+
export { ATTRIBUTE_SELECTOR, AT_RULE, AT_RULE_PRELUDE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FEATURE_RANGE, 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, VALUE };
|
package/dist/css-node.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CSSDataArena } from './arena';
|
|
2
|
-
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, FEATURE_RANGE } from './arena';
|
|
2
|
+
import { STYLESHEET, STYLE_RULE, AT_RULE, DECLARATION, SELECTOR, COMMENT, BLOCK, IDENTIFIER, NUMBER, DIMENSION, STRING, HASH, FUNCTION, OPERATOR, PARENTHESIS, URL, VALUE, 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, FEATURE_RANGE, AT_RULE_PRELUDE } from './arena';
|
|
3
3
|
export declare const TYPE_NAMES: {
|
|
4
4
|
readonly 1: "StyleSheet";
|
|
5
5
|
readonly 2: "Rule";
|
|
@@ -17,6 +17,7 @@ export declare const TYPE_NAMES: {
|
|
|
17
17
|
readonly 16: "Operator";
|
|
18
18
|
readonly 17: "Parentheses";
|
|
19
19
|
readonly 18: "Url";
|
|
20
|
+
readonly 19: "Value";
|
|
20
21
|
readonly 20: "SelectorList";
|
|
21
22
|
readonly 21: "TypeSelector";
|
|
22
23
|
readonly 22: "ClassSelector";
|
|
@@ -38,9 +39,10 @@ export declare const TYPE_NAMES: {
|
|
|
38
39
|
readonly 37: "Layer";
|
|
39
40
|
readonly 38: "Operator";
|
|
40
41
|
readonly 39: "MediaFeatureRange";
|
|
42
|
+
readonly 40: "AtrulePrelude";
|
|
41
43
|
};
|
|
42
44
|
export type TypeName = (typeof TYPE_NAMES)[keyof typeof TYPE_NAMES] | 'unknown';
|
|
43
|
-
export type CSSNodeType = typeof STYLESHEET | typeof STYLE_RULE | typeof AT_RULE | typeof DECLARATION | typeof SELECTOR | typeof COMMENT | typeof BLOCK | typeof IDENTIFIER | typeof NUMBER | typeof DIMENSION | typeof STRING | typeof HASH | typeof FUNCTION | typeof OPERATOR | typeof PARENTHESIS | typeof URL | typeof SELECTOR_LIST | typeof TYPE_SELECTOR | typeof CLASS_SELECTOR | typeof ID_SELECTOR | typeof ATTRIBUTE_SELECTOR | typeof PSEUDO_CLASS_SELECTOR | typeof PSEUDO_ELEMENT_SELECTOR | typeof COMBINATOR | typeof UNIVERSAL_SELECTOR | typeof NESTING_SELECTOR | typeof NTH_SELECTOR | typeof NTH_OF_SELECTOR | typeof LANG_SELECTOR | typeof MEDIA_QUERY | typeof MEDIA_FEATURE | typeof MEDIA_TYPE | typeof CONTAINER_QUERY | typeof SUPPORTS_QUERY | typeof LAYER_NAME | typeof PRELUDE_OPERATOR | typeof FEATURE_RANGE;
|
|
45
|
+
export type CSSNodeType = typeof STYLESHEET | typeof STYLE_RULE | typeof AT_RULE | typeof DECLARATION | typeof SELECTOR | typeof COMMENT | typeof BLOCK | typeof IDENTIFIER | typeof NUMBER | typeof DIMENSION | typeof STRING | typeof HASH | typeof FUNCTION | typeof OPERATOR | typeof PARENTHESIS | typeof URL | typeof VALUE | typeof SELECTOR_LIST | typeof TYPE_SELECTOR | typeof CLASS_SELECTOR | typeof ID_SELECTOR | typeof ATTRIBUTE_SELECTOR | typeof PSEUDO_CLASS_SELECTOR | typeof PSEUDO_ELEMENT_SELECTOR | typeof COMBINATOR | typeof UNIVERSAL_SELECTOR | typeof NESTING_SELECTOR | typeof NTH_SELECTOR | typeof NTH_OF_SELECTOR | typeof LANG_SELECTOR | typeof MEDIA_QUERY | typeof MEDIA_FEATURE | typeof MEDIA_TYPE | typeof CONTAINER_QUERY | typeof SUPPORTS_QUERY | typeof LAYER_NAME | typeof PRELUDE_OPERATOR | typeof FEATURE_RANGE | typeof AT_RULE_PRELUDE;
|
|
44
46
|
export interface CloneOptions {
|
|
45
47
|
/**
|
|
46
48
|
* Recursively clone all children
|
|
@@ -62,7 +64,7 @@ export type PlainCSSNode = {
|
|
|
62
64
|
property?: string;
|
|
63
65
|
value?: string | number | null;
|
|
64
66
|
unit?: string;
|
|
65
|
-
prelude?:
|
|
67
|
+
prelude?: PlainCSSNode | null;
|
|
66
68
|
is_important?: boolean;
|
|
67
69
|
is_vendor_prefixed?: boolean;
|
|
68
70
|
is_browserhack?: boolean;
|
|
@@ -102,35 +104,37 @@ export declare class CSSNode {
|
|
|
102
104
|
* For URL nodes with quoted string: returns the string with quotes (consistent with STRING node)
|
|
103
105
|
* For URL nodes with unquoted URL: returns the URL content without quotes
|
|
104
106
|
*/
|
|
105
|
-
get value(): string | number | null;
|
|
107
|
+
get value(): CSSNode | string | number | null;
|
|
106
108
|
/** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
|
|
107
109
|
get value_as_number(): number | null;
|
|
108
110
|
/**
|
|
109
|
-
* Get the prelude
|
|
110
|
-
*
|
|
111
|
+
* Get the prelude node:
|
|
112
|
+
* - For at-rules: AT_RULE_PRELUDE wrapper containing structured prelude children (media queries, layer names, etc.)
|
|
113
|
+
* - For style rules: SELECTOR_LIST or SELECTOR node
|
|
114
|
+
* Returns null if no prelude exists
|
|
111
115
|
*/
|
|
112
|
-
get prelude():
|
|
116
|
+
get prelude(): CSSNode | null | undefined;
|
|
113
117
|
/**
|
|
114
118
|
* Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
|
|
115
119
|
* Returns one of the ATTR_OPERATOR_* constants
|
|
116
120
|
*/
|
|
117
|
-
get attr_operator(): number;
|
|
121
|
+
get attr_operator(): number | undefined;
|
|
118
122
|
/**
|
|
119
123
|
* Get the attribute flags (for attribute selectors: i, s)
|
|
120
124
|
* Returns one of the ATTR_FLAG_* constants
|
|
121
125
|
*/
|
|
122
|
-
get attr_flags(): number;
|
|
126
|
+
get attr_flags(): number | undefined;
|
|
123
127
|
/** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
|
|
124
|
-
get unit(): string |
|
|
128
|
+
get unit(): string | undefined;
|
|
125
129
|
/** Check if this declaration has !important */
|
|
126
|
-
get is_important(): boolean |
|
|
130
|
+
get is_important(): boolean | undefined;
|
|
127
131
|
/** Check if this declaration has a browser hack prefix */
|
|
128
|
-
get is_browserhack(): boolean |
|
|
132
|
+
get is_browserhack(): boolean | undefined;
|
|
129
133
|
/** Check if this has a vendor prefix (computed on-demand) */
|
|
130
134
|
get is_vendor_prefixed(): boolean;
|
|
131
135
|
/** Check if this node has an error */
|
|
132
136
|
get has_error(): boolean;
|
|
133
|
-
/** Check if this
|
|
137
|
+
/** Check if this node has a prelude (at-rules and style rules) */
|
|
134
138
|
get has_prelude(): boolean;
|
|
135
139
|
/** Check if this rule has a block { } */
|
|
136
140
|
get has_block(): boolean;
|
|
@@ -139,9 +143,7 @@ export declare class CSSNode {
|
|
|
139
143
|
/** Get the block node (for style rules and at-rules with blocks) */
|
|
140
144
|
get block(): CSSNode | null;
|
|
141
145
|
/** Check if this block is empty (no declarations or rules, only comments allowed) */
|
|
142
|
-
get is_empty(): boolean;
|
|
143
|
-
/** Get array of parsed value nodes (for declarations only) */
|
|
144
|
-
get values(): CSSNode[];
|
|
146
|
+
get is_empty(): boolean | undefined;
|
|
145
147
|
/** Get start line number */
|
|
146
148
|
get line(): number;
|
|
147
149
|
/** Get start column number */
|
|
@@ -172,37 +174,18 @@ export declare class CSSNode {
|
|
|
172
174
|
/** Make CSSNode iterable over its children */
|
|
173
175
|
[Symbol.iterator](): Iterator<CSSNode>;
|
|
174
176
|
/** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
|
|
175
|
-
get nth_a(): string |
|
|
177
|
+
get nth_a(): string | undefined;
|
|
176
178
|
/** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
|
|
177
|
-
get nth_b(): string |
|
|
179
|
+
get nth_b(): string | undefined;
|
|
178
180
|
/** Get the An+B formula node from :nth-child(2n+1 of .foo) */
|
|
179
|
-
get nth(): CSSNode |
|
|
181
|
+
get nth(): CSSNode | undefined;
|
|
180
182
|
/** Get the selector list from :nth-child(2n+1 of .foo) */
|
|
181
|
-
get selector(): CSSNode |
|
|
183
|
+
get selector(): CSSNode | undefined;
|
|
182
184
|
/**
|
|
183
185
|
* Get selector list from pseudo-class functions
|
|
184
186
|
* Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
|
|
185
187
|
*/
|
|
186
|
-
get selector_list(): CSSNode |
|
|
187
|
-
/**
|
|
188
|
-
* Iterator over first compound selector parts (zero allocation)
|
|
189
|
-
* Yields parts before the first combinator
|
|
190
|
-
*/
|
|
191
|
-
compound_parts(): IterableIterator<CSSNode>;
|
|
192
|
-
/**
|
|
193
|
-
* Get first compound selector as array
|
|
194
|
-
* Returns array of parts before first combinator
|
|
195
|
-
*/
|
|
196
|
-
get first_compound(): CSSNode[];
|
|
197
|
-
/**
|
|
198
|
-
* Split selector into compound selectors
|
|
199
|
-
* Returns array of compound arrays split by combinators
|
|
200
|
-
*/
|
|
201
|
-
get all_compounds(): CSSNode[][];
|
|
202
|
-
/** Check if selector is compound (no combinators) */
|
|
203
|
-
get is_compound(): boolean;
|
|
204
|
-
/** Get text of first compound selector (no node allocation) */
|
|
205
|
-
get first_compound_text(): string;
|
|
188
|
+
get selector_list(): CSSNode | undefined;
|
|
206
189
|
/**
|
|
207
190
|
* Clone this node as a mutable plain JavaScript object with children as arrays.
|
|
208
191
|
* See API.md for examples.
|
package/dist/css-node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DIMENSION, NUMBER, URL, STRING,
|
|
1
|
+
import { DECLARATION, 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_FEATURE, MEDIA_QUERY, LANG_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, COMBINATOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, VALUE, PARENTHESIS, OPERATOR, HASH, SELECTOR, 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
|
|
|
@@ -19,6 +19,7 @@ const TYPE_NAMES = {
|
|
|
19
19
|
[OPERATOR]: "Operator",
|
|
20
20
|
[PARENTHESIS]: "Parentheses",
|
|
21
21
|
[URL]: "Url",
|
|
22
|
+
[VALUE]: "Value",
|
|
22
23
|
[SELECTOR_LIST]: "SelectorList",
|
|
23
24
|
[TYPE_SELECTOR]: "TypeSelector",
|
|
24
25
|
[CLASS_SELECTOR]: "ClassSelector",
|
|
@@ -39,7 +40,8 @@ const TYPE_NAMES = {
|
|
|
39
40
|
[SUPPORTS_QUERY]: "SupportsQuery",
|
|
40
41
|
[LAYER_NAME]: "Layer",
|
|
41
42
|
[PRELUDE_OPERATOR]: "Operator",
|
|
42
|
-
[FEATURE_RANGE]: "MediaFeatureRange"
|
|
43
|
+
[FEATURE_RANGE]: "MediaFeatureRange",
|
|
44
|
+
[AT_RULE_PRELUDE]: "AtrulePrelude"
|
|
43
45
|
};
|
|
44
46
|
class CSSNode {
|
|
45
47
|
arena;
|
|
@@ -94,6 +96,9 @@ class CSSNode {
|
|
|
94
96
|
*/
|
|
95
97
|
get value() {
|
|
96
98
|
let { type, text } = this;
|
|
99
|
+
if (type === DECLARATION && this.first_child) {
|
|
100
|
+
return this.first_child;
|
|
101
|
+
}
|
|
97
102
|
if (type === DIMENSION) {
|
|
98
103
|
return parse_dimension(text).value;
|
|
99
104
|
}
|
|
@@ -133,18 +138,30 @@ class CSSNode {
|
|
|
133
138
|
return null;
|
|
134
139
|
}
|
|
135
140
|
/**
|
|
136
|
-
* Get the prelude
|
|
137
|
-
*
|
|
141
|
+
* Get the prelude node:
|
|
142
|
+
* - For at-rules: AT_RULE_PRELUDE wrapper containing structured prelude children (media queries, layer names, etc.)
|
|
143
|
+
* - For style rules: SELECTOR_LIST or SELECTOR node
|
|
144
|
+
* Returns null if no prelude exists
|
|
138
145
|
*/
|
|
139
146
|
get prelude() {
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
if (this.type === AT_RULE) {
|
|
148
|
+
let first = this.first_child;
|
|
149
|
+
if (first && first.type === AT_RULE_PRELUDE) {
|
|
150
|
+
return first;
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
if (this.type === STYLE_RULE) {
|
|
155
|
+
return this.first_child;
|
|
156
|
+
}
|
|
157
|
+
return void 0;
|
|
142
158
|
}
|
|
143
159
|
/**
|
|
144
160
|
* Get the attribute operator (for attribute selectors: =, ~=, |=, ^=, $=, *=)
|
|
145
161
|
* Returns one of the ATTR_OPERATOR_* constants
|
|
146
162
|
*/
|
|
147
163
|
get attr_operator() {
|
|
164
|
+
if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
|
|
148
165
|
return this.arena.get_attr_operator(this.index);
|
|
149
166
|
}
|
|
150
167
|
/**
|
|
@@ -152,21 +169,22 @@ class CSSNode {
|
|
|
152
169
|
* Returns one of the ATTR_FLAG_* constants
|
|
153
170
|
*/
|
|
154
171
|
get attr_flags() {
|
|
172
|
+
if (this.type !== ATTRIBUTE_SELECTOR) return void 0;
|
|
155
173
|
return this.arena.get_attr_flags(this.index);
|
|
156
174
|
}
|
|
157
175
|
/** Get the unit for dimension nodes (e.g., "px" from "100px", "%" from "50%") */
|
|
158
176
|
get unit() {
|
|
159
|
-
if (this.type !== DIMENSION) return
|
|
177
|
+
if (this.type !== DIMENSION) return void 0;
|
|
160
178
|
return parse_dimension(this.text).unit;
|
|
161
179
|
}
|
|
162
180
|
/** Check if this declaration has !important */
|
|
163
181
|
get is_important() {
|
|
164
|
-
if (this.type !== DECLARATION) return
|
|
182
|
+
if (this.type !== DECLARATION) return void 0;
|
|
165
183
|
return this.arena.has_flag(this.index, FLAG_IMPORTANT);
|
|
166
184
|
}
|
|
167
185
|
/** Check if this declaration has a browser hack prefix */
|
|
168
186
|
get is_browserhack() {
|
|
169
|
-
if (this.type !== DECLARATION) return
|
|
187
|
+
if (this.type !== DECLARATION) return void 0;
|
|
170
188
|
return this.arena.has_flag(this.index, FLAG_BROWSERHACK);
|
|
171
189
|
}
|
|
172
190
|
/** Check if this has a vendor prefix (computed on-demand) */
|
|
@@ -191,9 +209,15 @@ class CSSNode {
|
|
|
191
209
|
get has_error() {
|
|
192
210
|
return this.arena.has_flag(this.index, FLAG_HAS_ERROR);
|
|
193
211
|
}
|
|
194
|
-
/** Check if this
|
|
212
|
+
/** Check if this node has a prelude (at-rules and style rules) */
|
|
195
213
|
get has_prelude() {
|
|
196
|
-
|
|
214
|
+
if (this.type === AT_RULE) {
|
|
215
|
+
return this.arena.get_value_length(this.index) > 0;
|
|
216
|
+
}
|
|
217
|
+
if (this.type === STYLE_RULE) {
|
|
218
|
+
return this.first_child !== null;
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
197
221
|
}
|
|
198
222
|
/** Check if this rule has a block { } */
|
|
199
223
|
get has_block() {
|
|
@@ -228,7 +252,7 @@ class CSSNode {
|
|
|
228
252
|
}
|
|
229
253
|
/** Check if this block is empty (no declarations or rules, only comments allowed) */
|
|
230
254
|
get is_empty() {
|
|
231
|
-
if (this.type !== BLOCK) return
|
|
255
|
+
if (this.type !== BLOCK) return void 0;
|
|
232
256
|
let child = this.first_child;
|
|
233
257
|
while (child) {
|
|
234
258
|
if (child.type !== COMMENT) {
|
|
@@ -238,17 +262,6 @@ class CSSNode {
|
|
|
238
262
|
}
|
|
239
263
|
return true;
|
|
240
264
|
}
|
|
241
|
-
// --- Value Node Access (for declarations) ---
|
|
242
|
-
/** Get array of parsed value nodes (for declarations only) */
|
|
243
|
-
get values() {
|
|
244
|
-
let result = [];
|
|
245
|
-
let child = this.first_child;
|
|
246
|
-
while (child) {
|
|
247
|
-
result.push(child);
|
|
248
|
-
child = child.next_sibling;
|
|
249
|
-
}
|
|
250
|
-
return result;
|
|
251
|
-
}
|
|
252
265
|
/** Get start line number */
|
|
253
266
|
get line() {
|
|
254
267
|
return this.arena.get_start_line(this.index);
|
|
@@ -324,17 +337,17 @@ class CSSNode {
|
|
|
324
337
|
// --- An+B Expression Helpers (for NODE_SELECTOR_NTH) ---
|
|
325
338
|
/** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
|
|
326
339
|
get nth_a() {
|
|
327
|
-
if (this.type !== NTH_SELECTOR) return
|
|
340
|
+
if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
|
|
328
341
|
let len = this.arena.get_content_length(this.index);
|
|
329
|
-
if (len === 0) return
|
|
342
|
+
if (len === 0) return void 0;
|
|
330
343
|
let start = this.arena.get_content_start(this.index);
|
|
331
344
|
return this.source.substring(start, start + len);
|
|
332
345
|
}
|
|
333
346
|
/** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
|
|
334
347
|
get nth_b() {
|
|
335
|
-
if (this.type !== NTH_SELECTOR) return
|
|
348
|
+
if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
|
|
336
349
|
let len = this.arena.get_value_length(this.index);
|
|
337
|
-
if (len === 0) return
|
|
350
|
+
if (len === 0) return void 0;
|
|
338
351
|
let start = this.arena.get_value_start(this.index);
|
|
339
352
|
let value = this.source.substring(start, start + len);
|
|
340
353
|
let check_pos = start - 1;
|
|
@@ -356,14 +369,14 @@ class CSSNode {
|
|
|
356
369
|
// --- Pseudo-Class Nth-Of Helpers (for NODE_SELECTOR_NTH_OF) ---
|
|
357
370
|
/** Get the An+B formula node from :nth-child(2n+1 of .foo) */
|
|
358
371
|
get nth() {
|
|
359
|
-
if (this.type !== NTH_OF_SELECTOR) return
|
|
360
|
-
return this.first_child;
|
|
372
|
+
if (this.type !== NTH_OF_SELECTOR) return void 0;
|
|
373
|
+
return this.first_child ?? void 0;
|
|
361
374
|
}
|
|
362
375
|
/** Get the selector list from :nth-child(2n+1 of .foo) */
|
|
363
376
|
get selector() {
|
|
364
|
-
if (this.type !== NTH_OF_SELECTOR) return
|
|
377
|
+
if (this.type !== NTH_OF_SELECTOR) return void 0;
|
|
365
378
|
let first = this.first_child;
|
|
366
|
-
return first
|
|
379
|
+
return first?.next_sibling ?? void 0;
|
|
367
380
|
}
|
|
368
381
|
// --- Pseudo-Class Selector List Helper ---
|
|
369
382
|
/**
|
|
@@ -371,95 +384,16 @@ class CSSNode {
|
|
|
371
384
|
* Works for :is(.a), :not(.b), :has(.c), :where(.d), :nth-child(2n of .e)
|
|
372
385
|
*/
|
|
373
386
|
get selector_list() {
|
|
374
|
-
if (this.type !== PSEUDO_CLASS_SELECTOR) return
|
|
387
|
+
if (this.type !== PSEUDO_CLASS_SELECTOR) return void 0;
|
|
375
388
|
let child = this.first_child;
|
|
376
|
-
if (!child) return
|
|
389
|
+
if (!child) return void 0;
|
|
377
390
|
if (child.type === SELECTOR_LIST) {
|
|
378
391
|
return child;
|
|
379
392
|
}
|
|
380
393
|
if (child.type === NTH_OF_SELECTOR) {
|
|
381
394
|
return child.selector;
|
|
382
395
|
}
|
|
383
|
-
return
|
|
384
|
-
}
|
|
385
|
-
// --- Compound Selector Helpers (for NODE_SELECTOR) ---
|
|
386
|
-
/**
|
|
387
|
-
* Iterator over first compound selector parts (zero allocation)
|
|
388
|
-
* Yields parts before the first combinator
|
|
389
|
-
*/
|
|
390
|
-
*compound_parts() {
|
|
391
|
-
if (this.type !== SELECTOR) return;
|
|
392
|
-
let child = this.first_child;
|
|
393
|
-
while (child) {
|
|
394
|
-
if (child.type === COMBINATOR) break;
|
|
395
|
-
yield child;
|
|
396
|
-
child = child.next_sibling;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Get first compound selector as array
|
|
401
|
-
* Returns array of parts before first combinator
|
|
402
|
-
*/
|
|
403
|
-
get first_compound() {
|
|
404
|
-
if (this.type !== SELECTOR) return [];
|
|
405
|
-
let result = [];
|
|
406
|
-
let child = this.first_child;
|
|
407
|
-
while (child) {
|
|
408
|
-
if (child.type === COMBINATOR) break;
|
|
409
|
-
result.push(child);
|
|
410
|
-
child = child.next_sibling;
|
|
411
|
-
}
|
|
412
|
-
return result;
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Split selector into compound selectors
|
|
416
|
-
* Returns array of compound arrays split by combinators
|
|
417
|
-
*/
|
|
418
|
-
get all_compounds() {
|
|
419
|
-
if (this.type !== SELECTOR) return [];
|
|
420
|
-
let compounds = [];
|
|
421
|
-
let current_compound = [];
|
|
422
|
-
let child = this.first_child;
|
|
423
|
-
while (child) {
|
|
424
|
-
if (child.type === COMBINATOR) {
|
|
425
|
-
if (current_compound.length > 0) {
|
|
426
|
-
compounds.push(current_compound);
|
|
427
|
-
current_compound = [];
|
|
428
|
-
}
|
|
429
|
-
} else {
|
|
430
|
-
current_compound.push(child);
|
|
431
|
-
}
|
|
432
|
-
child = child.next_sibling;
|
|
433
|
-
}
|
|
434
|
-
if (current_compound.length > 0) {
|
|
435
|
-
compounds.push(current_compound);
|
|
436
|
-
}
|
|
437
|
-
return compounds;
|
|
438
|
-
}
|
|
439
|
-
/** Check if selector is compound (no combinators) */
|
|
440
|
-
get is_compound() {
|
|
441
|
-
if (this.type !== SELECTOR) return false;
|
|
442
|
-
let child = this.first_child;
|
|
443
|
-
while (child) {
|
|
444
|
-
if (child.type === COMBINATOR) return false;
|
|
445
|
-
child = child.next_sibling;
|
|
446
|
-
}
|
|
447
|
-
return true;
|
|
448
|
-
}
|
|
449
|
-
/** Get text of first compound selector (no node allocation) */
|
|
450
|
-
get first_compound_text() {
|
|
451
|
-
if (this.type !== SELECTOR) return "";
|
|
452
|
-
let start = -1;
|
|
453
|
-
let end = -1;
|
|
454
|
-
let child = this.first_child;
|
|
455
|
-
while (child) {
|
|
456
|
-
if (child.type === COMBINATOR) break;
|
|
457
|
-
if (start === -1) start = child.start;
|
|
458
|
-
end = child.start + child.length;
|
|
459
|
-
child = child.next_sibling;
|
|
460
|
-
}
|
|
461
|
-
if (start === -1) return "";
|
|
462
|
-
return this.source.substring(start, end);
|
|
396
|
+
return void 0;
|
|
463
397
|
}
|
|
464
398
|
// --- Node Cloning ---
|
|
465
399
|
/**
|
|
@@ -484,9 +418,6 @@ class CSSNode {
|
|
|
484
418
|
plain.value = this.value;
|
|
485
419
|
if (this.unit) plain.unit = this.unit;
|
|
486
420
|
}
|
|
487
|
-
if (this.type === AT_RULE && this.prelude) {
|
|
488
|
-
plain.prelude = this.prelude;
|
|
489
|
-
}
|
|
490
421
|
if (this.type === DECLARATION) {
|
|
491
422
|
plain.is_important = this.is_important;
|
|
492
423
|
plain.is_browserhack = this.is_browserhack;
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,6 @@ export { tokenize } from './tokenize.js';
|
|
|
7
7
|
export { BREAK, SKIP, traverse, walk } from './walk.js';
|
|
8
8
|
export { is_custom, is_vendor_prefixed, str_equals, str_index_of, str_starts_with } from './string-utils.js';
|
|
9
9
|
export { CSSNode, TYPE_NAMES } from './css-node.js';
|
|
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, FEATURE_RANGE, 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
|
+
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, AT_RULE_PRELUDE, BLOCK, CLASS_SELECTOR, COMBINATOR, COMMENT, CONTAINER_QUERY, DECLARATION, DIMENSION, FEATURE_RANGE, 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, VALUE } from './arena.js';
|
|
11
11
|
export { NODE_TYPES } from './constants.js';
|
|
12
12
|
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';
|
|
@@ -124,12 +124,16 @@ class DeclarationParser {
|
|
|
124
124
|
this.arena.set_value_start_delta(declaration, trimmed[0] - prop_start);
|
|
125
125
|
this.arena.set_value_length(declaration, trimmed[1] - trimmed[0]);
|
|
126
126
|
if (this.value_parser) {
|
|
127
|
-
let
|
|
128
|
-
this.arena.append_children(declaration,
|
|
127
|
+
let valueNode = this.value_parser.parse_value(value_start, trimmed[1], value_start_line, value_start_column);
|
|
128
|
+
this.arena.append_children(declaration, [valueNode]);
|
|
129
129
|
}
|
|
130
130
|
} else {
|
|
131
131
|
this.arena.set_value_start_delta(declaration, value_start - prop_start);
|
|
132
132
|
this.arena.set_value_length(declaration, 0);
|
|
133
|
+
if (this.value_parser) {
|
|
134
|
+
let valueNode = this.value_parser.parse_value(value_start, value_start, value_start_line, value_start_column);
|
|
135
|
+
this.arena.append_children(declaration, [valueNode]);
|
|
136
|
+
}
|
|
133
137
|
}
|
|
134
138
|
if (has_important) {
|
|
135
139
|
this.arena.set_flag(declaration, FLAG_IMPORTANT);
|
package/dist/parse-selector.js
CHANGED
|
@@ -34,9 +34,13 @@ class SelectorParser {
|
|
|
34
34
|
let list_column = this.lexer.column;
|
|
35
35
|
while (this.lexer.pos < this.selector_end) {
|
|
36
36
|
let selector_start = this.lexer.pos;
|
|
37
|
+
let selector_line = this.lexer.line;
|
|
38
|
+
let selector_column = this.lexer.column;
|
|
37
39
|
let complex_selector = this.parse_complex_selector(allow_relative);
|
|
38
40
|
if (complex_selector !== null) {
|
|
39
|
-
let selector_wrapper = this.create_node(SELECTOR, selector_start, this.lexer.pos);
|
|
41
|
+
let selector_wrapper = this.arena.create_node(SELECTOR, selector_start, this.lexer.pos - selector_start, selector_line, selector_column);
|
|
42
|
+
this.arena.set_content_start_delta(selector_wrapper, 0);
|
|
43
|
+
this.arena.set_content_length(selector_wrapper, this.lexer.pos - selector_start);
|
|
40
44
|
let last_component = complex_selector;
|
|
41
45
|
let next_sibling = this.arena.get_next_sibling(last_component);
|
|
42
46
|
while (next_sibling !== 0) {
|
|
@@ -573,9 +577,11 @@ class SelectorParser {
|
|
|
573
577
|
this.arena.set_content_length(node, end - start);
|
|
574
578
|
return node;
|
|
575
579
|
}
|
|
576
|
-
// Helper to skip whitespace
|
|
580
|
+
// Helper to skip whitespace and update line/column
|
|
577
581
|
skip_whitespace() {
|
|
578
|
-
this.lexer.pos
|
|
582
|
+
while (this.lexer.pos < this.selector_end && is_whitespace(this.source.charCodeAt(this.lexer.pos))) {
|
|
583
|
+
this.lexer.advance();
|
|
584
|
+
}
|
|
579
585
|
}
|
|
580
586
|
}
|
|
581
587
|
function parse_selector(source) {
|
package/dist/parse-value.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CSSNode } from './css-node';
|
|
2
2
|
/**
|
|
3
|
-
* Parse a CSS declaration value string and return
|
|
3
|
+
* Parse a CSS declaration value string and return a VALUE node
|
|
4
4
|
* @param value_string - The CSS value to parse (e.g., "1px solid red")
|
|
5
|
-
* @returns
|
|
5
|
+
* @returns A CSSNode VALUE wrapper containing the parsed value tokens as children
|
|
6
6
|
*/
|
|
7
|
-
export declare function parse_value(value_string: string): CSSNode
|
|
7
|
+
export declare function parse_value(value_string: string): CSSNode;
|
package/dist/parse-value.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Lexer } from './tokenize.js';
|
|
2
|
-
import { CSSDataArena, OPERATOR, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, URL, FUNCTION, PARENTHESIS } from './arena.js';
|
|
2
|
+
import { CSSDataArena, VALUE, 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';
|
|
5
5
|
import { CSSNode } from './css-node.js';
|
|
@@ -15,14 +15,24 @@ class ValueParser {
|
|
|
15
15
|
this.lexer = new Lexer(source, false);
|
|
16
16
|
this.value_end = 0;
|
|
17
17
|
}
|
|
18
|
-
// Parse a declaration value range into
|
|
19
|
-
// Returns
|
|
18
|
+
// Parse a declaration value range into a VALUE wrapper node
|
|
19
|
+
// Returns single VALUE node index
|
|
20
20
|
parse_value(start, end, start_line, start_column) {
|
|
21
21
|
this.value_end = end;
|
|
22
22
|
this.lexer.pos = start;
|
|
23
23
|
this.lexer.line = start_line;
|
|
24
24
|
this.lexer.column = start_column;
|
|
25
|
-
|
|
25
|
+
let value_nodes = this.parse_value_tokens();
|
|
26
|
+
if (value_nodes.length === 0) {
|
|
27
|
+
let value_node2 = this.arena.create_node(VALUE, start, 0, start_line, start_column);
|
|
28
|
+
return value_node2;
|
|
29
|
+
}
|
|
30
|
+
let first_node_start = this.arena.get_start_offset(value_nodes[0]);
|
|
31
|
+
let last_node_index = value_nodes[value_nodes.length - 1];
|
|
32
|
+
let last_node_end = this.arena.get_start_offset(last_node_index) + this.arena.get_length(last_node_index);
|
|
33
|
+
let value_node = this.arena.create_node(VALUE, first_node_start, last_node_end - first_node_start, start_line, start_column);
|
|
34
|
+
this.arena.append_children(value_node, value_nodes);
|
|
35
|
+
return value_node;
|
|
26
36
|
}
|
|
27
37
|
// Core token parsing logic
|
|
28
38
|
parse_value_tokens() {
|
|
@@ -210,8 +220,8 @@ class ValueParser {
|
|
|
210
220
|
function parse_value(value_string) {
|
|
211
221
|
const arena = new CSSDataArena(CSSDataArena.capacity_for_source(value_string.length));
|
|
212
222
|
const value_parser = new ValueParser(arena, value_string);
|
|
213
|
-
const
|
|
214
|
-
return
|
|
223
|
+
const value_node_index = value_parser.parse_value(0, value_string.length, 1, 1);
|
|
224
|
+
return new CSSNode(arena, value_string, value_node_index);
|
|
215
225
|
}
|
|
216
226
|
|
|
217
227
|
export { ValueParser, parse_value };
|
package/dist/parse.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Lexer } from './tokenize.js';
|
|
2
|
-
import { CSSDataArena, STYLESHEET, STYLE_RULE, FLAG_HAS_BLOCK, BLOCK, FLAG_HAS_DECLARATIONS, SELECTOR_LIST, AT_RULE } from './arena.js';
|
|
2
|
+
import { CSSDataArena, STYLESHEET, STYLE_RULE, FLAG_HAS_BLOCK, BLOCK, FLAG_HAS_DECLARATIONS, SELECTOR_LIST, AT_RULE, AT_RULE_PRELUDE } 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';
|
|
@@ -167,13 +167,13 @@ class Parser {
|
|
|
167
167
|
this.next_token();
|
|
168
168
|
}
|
|
169
169
|
if (this.parse_selectors_enabled && this.selector_parser) {
|
|
170
|
-
let
|
|
171
|
-
if (
|
|
172
|
-
return
|
|
170
|
+
let selector = this.selector_parser.parse_selector(selector_start, last_end, selector_line, selector_column);
|
|
171
|
+
if (selector !== null) {
|
|
172
|
+
return selector;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
let
|
|
176
|
-
return
|
|
175
|
+
let selector_list = this.arena.create_node(SELECTOR_LIST, selector_start, last_end - selector_start, selector_line, selector_column);
|
|
176
|
+
return selector_list;
|
|
177
177
|
}
|
|
178
178
|
// Parse a declaration: property: value;
|
|
179
179
|
parse_declaration() {
|
|
@@ -221,12 +221,16 @@ class Parser {
|
|
|
221
221
|
this.next_token();
|
|
222
222
|
}
|
|
223
223
|
let trimmed = trim_boundaries(this.source, prelude_start, prelude_end);
|
|
224
|
-
let
|
|
224
|
+
let prelude_wrapper = null;
|
|
225
225
|
if (trimmed) {
|
|
226
226
|
this.arena.set_value_start_delta(at_rule, trimmed[0] - at_rule_start);
|
|
227
227
|
this.arena.set_value_length(at_rule, trimmed[1] - trimmed[0]);
|
|
228
228
|
if (this.prelude_parser) {
|
|
229
|
-
prelude_nodes = this.prelude_parser.parse_prelude(at_rule_name, trimmed[0], trimmed[1], at_rule_line, at_rule_column);
|
|
229
|
+
let prelude_nodes = this.prelude_parser.parse_prelude(at_rule_name, trimmed[0], trimmed[1], at_rule_line, at_rule_column);
|
|
230
|
+
if (prelude_nodes.length > 0) {
|
|
231
|
+
prelude_wrapper = this.arena.create_node(AT_RULE_PRELUDE, trimmed[0], trimmed[1] - trimmed[0], at_rule_line, at_rule_column);
|
|
232
|
+
this.arena.append_children(prelude_wrapper, prelude_nodes);
|
|
233
|
+
}
|
|
230
234
|
}
|
|
231
235
|
}
|
|
232
236
|
let last_end = this.lexer.token_end;
|
|
@@ -304,13 +308,26 @@ class Parser {
|
|
|
304
308
|
this.arena.set_length(block_node, last_end - block_start);
|
|
305
309
|
}
|
|
306
310
|
this.arena.append_children(block_node, block_children);
|
|
307
|
-
|
|
311
|
+
let at_rule_children = [];
|
|
312
|
+
if (prelude_wrapper !== null) {
|
|
313
|
+
at_rule_children.push(prelude_wrapper);
|
|
314
|
+
}
|
|
315
|
+
at_rule_children.push(block_node);
|
|
316
|
+
this.arena.set_length(at_rule, last_end - at_rule_start);
|
|
317
|
+
this.arena.append_children(at_rule, at_rule_children);
|
|
308
318
|
} else if (this.peek_type() === TOKEN_SEMICOLON) {
|
|
309
319
|
last_end = this.lexer.token_end;
|
|
310
320
|
this.next_token();
|
|
321
|
+
this.arena.set_length(at_rule, last_end - at_rule_start);
|
|
322
|
+
if (prelude_wrapper !== null) {
|
|
323
|
+
this.arena.append_children(at_rule, [prelude_wrapper]);
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
this.arena.set_length(at_rule, last_end - at_rule_start);
|
|
327
|
+
if (prelude_wrapper !== null) {
|
|
328
|
+
this.arena.append_children(at_rule, [prelude_wrapper]);
|
|
329
|
+
}
|
|
311
330
|
}
|
|
312
|
-
this.arena.set_length(at_rule, last_end - at_rule_start);
|
|
313
|
-
this.arena.append_children(at_rule, prelude_nodes);
|
|
314
331
|
return at_rule;
|
|
315
332
|
}
|
|
316
333
|
// Determine if an at-rule contains declarations or nested rules
|
package/dist/tokenize.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { TOKEN_EOF,
|
|
1
|
+
import { char_types, CHAR_WHITESPACE, CHAR_NEWLINE, CHAR_DIGIT, is_ident_start, is_newline, is_hex_digit, is_whitespace, is_ident_char } from './char-types.js';
|
|
2
|
+
import { TOKEN_EOF, TOKEN_RIGHT_PAREN, TOKEN_LEFT_PAREN, TOKEN_RIGHT_BRACKET, TOKEN_LEFT_BRACKET, TOKEN_COMMA, TOKEN_SEMICOLON, TOKEN_COLON, TOKEN_RIGHT_BRACE, TOKEN_LEFT_BRACE, TOKEN_CDO, TOKEN_CDC, TOKEN_DELIM, TOKEN_WHITESPACE, TOKEN_COMMENT, TOKEN_STRING, TOKEN_BAD_STRING, TOKEN_PERCENTAGE, TOKEN_DIMENSION, TOKEN_NUMBER, TOKEN_FUNCTION, TOKEN_IDENT, TOKEN_AT_KEYWORD, TOKEN_HASH } from './token-types.js';
|
|
3
3
|
|
|
4
4
|
const CHAR_LEFT_BRACE = 123;
|
|
5
5
|
const CHAR_RIGHT_BRACE = 125;
|
|
@@ -57,7 +57,7 @@ class Lexer {
|
|
|
57
57
|
if (skip_whitespace) {
|
|
58
58
|
while (this.pos < this.source.length) {
|
|
59
59
|
let ch2 = this.source.charCodeAt(this.pos);
|
|
60
|
-
if (
|
|
60
|
+
if (ch2 >= 128 || (char_types[ch2] & (CHAR_WHITESPACE | CHAR_NEWLINE)) === 0) break;
|
|
61
61
|
this.advance();
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -68,43 +68,36 @@ class Lexer {
|
|
|
68
68
|
let start = this.pos;
|
|
69
69
|
let start_line = this.line;
|
|
70
70
|
let start_column = this.column;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (ch === CHAR_LEFT_PAREN) {
|
|
100
|
-
this.advance();
|
|
101
|
-
return this.make_token(TOKEN_LEFT_PAREN, start, this.pos, start_line, start_column);
|
|
102
|
-
}
|
|
103
|
-
if (ch === CHAR_RIGHT_PAREN) {
|
|
104
|
-
this.advance();
|
|
105
|
-
return this.make_token(TOKEN_RIGHT_PAREN, start, this.pos, start_line, start_column);
|
|
71
|
+
switch (ch) {
|
|
72
|
+
case CHAR_LEFT_BRACE:
|
|
73
|
+
this.advance();
|
|
74
|
+
return this.make_token(TOKEN_LEFT_BRACE, start, this.pos, start_line, start_column);
|
|
75
|
+
case CHAR_RIGHT_BRACE:
|
|
76
|
+
this.advance();
|
|
77
|
+
return this.make_token(TOKEN_RIGHT_BRACE, start, this.pos, start_line, start_column);
|
|
78
|
+
case CHAR_COLON:
|
|
79
|
+
this.advance();
|
|
80
|
+
return this.make_token(TOKEN_COLON, start, this.pos, start_line, start_column);
|
|
81
|
+
case CHAR_SEMICOLON:
|
|
82
|
+
this.advance();
|
|
83
|
+
return this.make_token(TOKEN_SEMICOLON, start, this.pos, start_line, start_column);
|
|
84
|
+
case CHAR_COMMA:
|
|
85
|
+
this.advance();
|
|
86
|
+
return this.make_token(TOKEN_COMMA, start, this.pos, start_line, start_column);
|
|
87
|
+
case CHAR_LEFT_BRACKET:
|
|
88
|
+
this.advance();
|
|
89
|
+
return this.make_token(TOKEN_LEFT_BRACKET, start, this.pos, start_line, start_column);
|
|
90
|
+
case CHAR_RIGHT_BRACKET:
|
|
91
|
+
this.advance();
|
|
92
|
+
return this.make_token(TOKEN_RIGHT_BRACKET, start, this.pos, start_line, start_column);
|
|
93
|
+
case CHAR_LEFT_PAREN:
|
|
94
|
+
this.advance();
|
|
95
|
+
return this.make_token(TOKEN_LEFT_PAREN, start, this.pos, start_line, start_column);
|
|
96
|
+
case CHAR_RIGHT_PAREN:
|
|
97
|
+
this.advance();
|
|
98
|
+
return this.make_token(TOKEN_RIGHT_PAREN, start, this.pos, start_line, start_column);
|
|
106
99
|
}
|
|
107
|
-
if (
|
|
100
|
+
if (ch < 128 && (char_types[ch] & (CHAR_WHITESPACE | CHAR_NEWLINE)) !== 0) {
|
|
108
101
|
return this.consume_whitespace(start_line, start_column);
|
|
109
102
|
}
|
|
110
103
|
if (ch === CHAR_FORWARD_SLASH && this.peek() === CHAR_ASTERISK) {
|
|
@@ -112,7 +105,7 @@ class Lexer {
|
|
|
112
105
|
this.advance(2);
|
|
113
106
|
while (this.pos < this.source.length - 1) {
|
|
114
107
|
let ch2 = this.source.charCodeAt(this.pos);
|
|
115
|
-
if (ch2 === CHAR_ASTERISK && this.
|
|
108
|
+
if (ch2 === CHAR_ASTERISK && this.peek() === CHAR_FORWARD_SLASH) {
|
|
116
109
|
this.advance(2);
|
|
117
110
|
break;
|
|
118
111
|
}
|
|
@@ -125,20 +118,23 @@ class Lexer {
|
|
|
125
118
|
if (ch === CHAR_DOUBLE_QUOTE || ch === CHAR_SINGLE_QUOTE) {
|
|
126
119
|
return this.consume_string(ch, start_line, start_column);
|
|
127
120
|
}
|
|
128
|
-
if (
|
|
121
|
+
if (ch < 128 && (char_types[ch] & CHAR_DIGIT) !== 0) {
|
|
129
122
|
return this.consume_number(start_line, start_column);
|
|
130
123
|
}
|
|
131
|
-
if (ch === CHAR_DOT
|
|
132
|
-
|
|
124
|
+
if (ch === CHAR_DOT) {
|
|
125
|
+
let next = this.peek();
|
|
126
|
+
if (next < 128 && (char_types[next] & CHAR_DIGIT) !== 0) {
|
|
127
|
+
return this.consume_number(start_line, start_column);
|
|
128
|
+
}
|
|
133
129
|
}
|
|
134
130
|
if (ch === CHAR_LESS_THAN && this.pos + 3 < this.source.length) {
|
|
135
|
-
if (this.
|
|
131
|
+
if (this.peek() === CHAR_EXCLAMATION && this.peek(2) === CHAR_HYPHEN && this.peek(3) === CHAR_HYPHEN) {
|
|
136
132
|
this.advance(4);
|
|
137
133
|
return this.make_token(TOKEN_CDO, start, this.pos, start_line, start_column);
|
|
138
134
|
}
|
|
139
135
|
}
|
|
140
136
|
if (ch === CHAR_HYPHEN && this.pos + 2 < this.source.length) {
|
|
141
|
-
if (this.
|
|
137
|
+
if (this.peek() === CHAR_HYPHEN && this.peek(2) === CHAR_GREATER_THAN) {
|
|
142
138
|
this.advance(3);
|
|
143
139
|
return this.make_token(TOKEN_CDC, start, this.pos, start_line, start_column);
|
|
144
140
|
}
|
|
@@ -166,9 +162,16 @@ class Lexer {
|
|
|
166
162
|
}
|
|
167
163
|
if (ch === CHAR_HYPHEN || ch === CHAR_PLUS) {
|
|
168
164
|
let next = this.peek();
|
|
169
|
-
|
|
165
|
+
let is_next_digit = next < 128 && (char_types[next] & CHAR_DIGIT) !== 0;
|
|
166
|
+
if (is_next_digit) {
|
|
170
167
|
return this.consume_number(start_line, start_column);
|
|
171
168
|
}
|
|
169
|
+
if (next === CHAR_DOT) {
|
|
170
|
+
let next2 = this.peek(2);
|
|
171
|
+
if (next2 < 128 && (char_types[next2] & CHAR_DIGIT) !== 0) {
|
|
172
|
+
return this.consume_number(start_line, start_column);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
172
175
|
}
|
|
173
176
|
this.advance();
|
|
174
177
|
return this.make_token(TOKEN_DELIM, start, this.pos, start_line, start_column);
|
|
@@ -177,7 +180,7 @@ class Lexer {
|
|
|
177
180
|
let start = this.pos;
|
|
178
181
|
while (this.pos < this.source.length) {
|
|
179
182
|
let ch = this.source.charCodeAt(this.pos);
|
|
180
|
-
if (
|
|
183
|
+
if (ch >= 128 || (char_types[ch] & (CHAR_WHITESPACE | CHAR_NEWLINE)) === 0) break;
|
|
181
184
|
this.advance();
|
|
182
185
|
}
|
|
183
186
|
return this.make_token(TOKEN_WHITESPACE, start, this.pos, start_line, start_column);
|
|
@@ -187,7 +190,7 @@ class Lexer {
|
|
|
187
190
|
this.advance(2);
|
|
188
191
|
while (this.pos < this.source.length - 1) {
|
|
189
192
|
let ch = this.source.charCodeAt(this.pos);
|
|
190
|
-
if (ch === CHAR_ASTERISK && this.
|
|
193
|
+
if (ch === CHAR_ASTERISK && this.peek() === CHAR_FORWARD_SLASH) {
|
|
191
194
|
this.advance(2);
|
|
192
195
|
break;
|
|
193
196
|
}
|
|
@@ -246,20 +249,30 @@ class Lexer {
|
|
|
246
249
|
if (ch === CHAR_PLUS || ch === CHAR_HYPHEN) {
|
|
247
250
|
this.advance();
|
|
248
251
|
}
|
|
249
|
-
while (this.pos < this.source.length
|
|
252
|
+
while (this.pos < this.source.length) {
|
|
253
|
+
let ch2 = this.source.charCodeAt(this.pos);
|
|
254
|
+
if (ch2 >= 128 || (char_types[ch2] & CHAR_DIGIT) === 0) break;
|
|
250
255
|
this.advance();
|
|
251
256
|
}
|
|
252
|
-
if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_DOT && this.pos + 1 < this.source.length
|
|
253
|
-
this.
|
|
254
|
-
|
|
257
|
+
if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_DOT && this.pos + 1 < this.source.length) {
|
|
258
|
+
let next = this.peek();
|
|
259
|
+
if (next < 128 && (char_types[next] & CHAR_DIGIT) !== 0) {
|
|
255
260
|
this.advance();
|
|
261
|
+
while (this.pos < this.source.length) {
|
|
262
|
+
let ch2 = this.source.charCodeAt(this.pos);
|
|
263
|
+
if (ch2 >= 128 || (char_types[ch2] & CHAR_DIGIT) === 0) break;
|
|
264
|
+
this.advance();
|
|
265
|
+
}
|
|
256
266
|
}
|
|
257
267
|
}
|
|
258
268
|
if (this.pos < this.source.length) {
|
|
259
269
|
let ch2 = this.source.charCodeAt(this.pos);
|
|
260
270
|
if (ch2 === CHAR_LOWERCASE_E || ch2 === CHAR_UPPERCASE_E) {
|
|
261
271
|
let next = this.peek();
|
|
262
|
-
|
|
272
|
+
let is_next_digit = next < 128 && (char_types[next] & CHAR_DIGIT) !== 0;
|
|
273
|
+
let next2 = this.peek(2);
|
|
274
|
+
let is_next2_digit = next2 < 128 && (char_types[next2] & CHAR_DIGIT) !== 0;
|
|
275
|
+
if (is_next_digit || (next === CHAR_PLUS || next === CHAR_HYPHEN) && is_next2_digit) {
|
|
263
276
|
this.advance();
|
|
264
277
|
if (this.pos < this.source.length) {
|
|
265
278
|
let sign = this.source.charCodeAt(this.pos);
|
|
@@ -267,7 +280,9 @@ class Lexer {
|
|
|
267
280
|
this.advance();
|
|
268
281
|
}
|
|
269
282
|
}
|
|
270
|
-
while (this.pos < this.source.length
|
|
283
|
+
while (this.pos < this.source.length) {
|
|
284
|
+
let ch3 = this.source.charCodeAt(this.pos);
|
|
285
|
+
if (ch3 >= 128 || (char_types[ch3] & CHAR_DIGIT) === 0) break;
|
|
271
286
|
this.advance();
|
|
272
287
|
}
|
|
273
288
|
}
|
|
@@ -294,7 +309,7 @@ class Lexer {
|
|
|
294
309
|
let ch = this.source.charCodeAt(this.pos);
|
|
295
310
|
if (ch === CHAR_BACKSLASH) {
|
|
296
311
|
if (this.pos + 1 >= this.source.length) break;
|
|
297
|
-
let next = this.
|
|
312
|
+
let next = this.peek();
|
|
298
313
|
if (is_newline(next)) break;
|
|
299
314
|
this.advance();
|
|
300
315
|
if (is_hex_digit(next)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projectwallace/css-parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "High-performance CSS lexer and parser, optimized for CSS inspection and analysis",
|
|
5
5
|
"author": "Bart Veneman <bart@projectwallace.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
"benchmark:memory": "npm run build && node --expose-gc benchmark/memory.ts",
|
|
61
61
|
"lint": "oxlint --config .oxlintrc.json",
|
|
62
62
|
"lint-package": "publint",
|
|
63
|
-
"check": "tsc --noEmit"
|
|
63
|
+
"check": "tsc --noEmit",
|
|
64
|
+
"precommit": "npm run test -- --run; npm run lint; npm run check"
|
|
64
65
|
},
|
|
65
66
|
"keywords": [
|
|
66
67
|
"css",
|