@projectwallace/css-parser 0.12.4 → 0.13.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 CHANGED
@@ -14,7 +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
+ export declare const UNICODE_RANGE = 19;
18
18
  export declare const SELECTOR_LIST = 20;
19
19
  export declare const TYPE_SELECTOR = 21;
20
20
  export declare const CLASS_SELECTOR = 22;
@@ -37,6 +37,7 @@ export declare const LAYER_NAME = 37;
37
37
  export declare const PRELUDE_OPERATOR = 38;
38
38
  export declare const FEATURE_RANGE = 39;
39
39
  export declare const AT_RULE_PRELUDE = 40;
40
+ export declare const VALUE = 50;
40
41
  export declare const FLAG_IMPORTANT: number;
41
42
  export declare const FLAG_HAS_ERROR: number;
42
43
  export declare const FLAG_LENGTH_OVERFLOW: number;
package/dist/arena.js CHANGED
@@ -15,7 +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
+ const UNICODE_RANGE = 19;
19
19
  const SELECTOR_LIST = 20;
20
20
  const TYPE_SELECTOR = 21;
21
21
  const CLASS_SELECTOR = 22;
@@ -38,6 +38,7 @@ const LAYER_NAME = 37;
38
38
  const PRELUDE_OPERATOR = 38;
39
39
  const FEATURE_RANGE = 39;
40
40
  const AT_RULE_PRELUDE = 40;
41
+ const VALUE = 50;
41
42
  const FLAG_IMPORTANT = 1 << 0;
42
43
  const FLAG_HAS_ERROR = 1 << 1;
43
44
  const FLAG_LENGTH_OVERFLOW = 1 << 2;
@@ -285,4 +286,4 @@ class CSSDataArena {
285
286
  }
286
287
  }
287
288
 
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 };
289
+ 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, UNICODE_RANGE, UNIVERSAL_SELECTOR, URL, VALUE };
@@ -17,7 +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
+ readonly VALUE: 50;
21
21
  readonly SELECTOR_LIST: 20;
22
22
  readonly TYPE_SELECTOR: 21;
23
23
  readonly CLASS_SELECTOR: 22;
@@ -17,7 +17,8 @@ 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
+ readonly 19: "UnicodeRange";
21
+ readonly 50: "Value";
21
22
  readonly 20: "SelectorList";
22
23
  readonly 21: "TypeSelector";
23
24
  readonly 22: "ClassSelector";
@@ -42,6 +43,8 @@ export declare const TYPE_NAMES: {
42
43
  readonly 40: "AtrulePrelude";
43
44
  };
44
45
  export type TypeName = (typeof TYPE_NAMES)[keyof typeof TYPE_NAMES] | 'unknown';
46
+ export declare const ATTR_OPERATOR_NAMES: Record<number, string | null>;
47
+ export declare const ATTR_FLAG_NAMES: Record<number, string | null>;
45
48
  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;
46
49
  export interface CloneOptions {
47
50
  /**
@@ -59,18 +62,18 @@ export type PlainCSSNode = {
59
62
  type: number;
60
63
  type_name: TypeName;
61
64
  text: string;
62
- children: PlainCSSNode[];
65
+ children?: PlainCSSNode[];
63
66
  name?: string;
64
67
  property?: string;
65
- value?: string | number | null;
68
+ value?: PlainCSSNode | string | number | null;
66
69
  unit?: string;
67
70
  prelude?: PlainCSSNode | null;
68
71
  is_important?: boolean;
69
72
  is_vendor_prefixed?: boolean;
70
73
  is_browserhack?: boolean;
71
74
  has_error?: boolean;
72
- attr_operator?: number;
73
- attr_flags?: number;
75
+ attr_operator?: string | null;
76
+ attr_flags?: string | null;
74
77
  nth_a?: string | null;
75
78
  nth_b?: string | null;
76
79
  line?: number;
@@ -84,19 +87,20 @@ export declare class CSSNode {
84
87
  private source;
85
88
  private index;
86
89
  constructor(arena: CSSDataArena, source: string, index: number);
90
+ private get_content;
87
91
  /** Get node type as number (for performance) */
88
92
  get type(): CSSNodeType;
89
93
  /** Get node type as human-readable string */
90
94
  get type_name(): TypeName;
91
95
  /** Get the full text of this node from source */
92
96
  get text(): string;
93
- /** Get the "content" text (property name for declarations, at-rule name for at-rules, layer name for import layers) */
94
- get name(): string;
97
+ /** Get the "content" text (at-rule name for at-rules, layer name for import layers) */
98
+ get name(): string | undefined;
95
99
  /**
96
100
  * Alias for name (for declarations: "color" in "color: blue")
97
101
  * More semantic than `name` for declaration nodes
98
102
  */
99
- get property(): string;
103
+ get property(): string | undefined;
100
104
  /**
101
105
  * Get the value text (for declarations: "blue" in "color: blue")
102
106
  * For dimension/number nodes: returns the numeric value as a number
@@ -104,7 +108,7 @@ export declare class CSSNode {
104
108
  * For URL nodes with quoted string: returns the string with quotes (consistent with STRING node)
105
109
  * For URL nodes with unquoted URL: returns the URL content without quotes
106
110
  */
107
- get value(): CSSNode | string | number | null;
111
+ get value(): CSSNode | string | number | null | undefined;
108
112
  /** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
109
113
  get value_as_number(): number | null;
110
114
  /**
package/dist/css-node.js CHANGED
@@ -1,4 +1,4 @@
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';
1
+ import { DECLARATION, OPERATOR, SELECTOR, MEDIA_FEATURE, DIMENSION, NUMBER, URL, STRING, AT_RULE, AT_RULE_PRELUDE, STYLE_RULE, ATTRIBUTE_SELECTOR, FLAG_IMPORTANT, FLAG_BROWSERHACK, IDENTIFIER, FUNCTION, PSEUDO_ELEMENT_SELECTOR, PSEUDO_CLASS_SELECTOR, FLAG_HAS_ERROR, FLAG_HAS_BLOCK, FLAG_HAS_DECLARATIONS, BLOCK, COMMENT, FLAG_HAS_PARENS, NTH_SELECTOR, NTH_OF_SELECTOR, SELECTOR_LIST, FEATURE_RANGE, PRELUDE_OPERATOR, LAYER_NAME, SUPPORTS_QUERY, CONTAINER_QUERY, MEDIA_TYPE, MEDIA_QUERY, LANG_SELECTOR, NESTING_SELECTOR, UNIVERSAL_SELECTOR, COMBINATOR, ID_SELECTOR, CLASS_SELECTOR, TYPE_SELECTOR, VALUE, UNICODE_RANGE, PARENTHESIS, HASH, STYLESHEET, ATTR_OPERATOR_STAR_EQUAL, ATTR_OPERATOR_DOLLAR_EQUAL, ATTR_OPERATOR_CARET_EQUAL, ATTR_OPERATOR_PIPE_EQUAL, ATTR_OPERATOR_TILDE_EQUAL, ATTR_OPERATOR_EQUAL, ATTR_OPERATOR_NONE, ATTR_FLAG_CASE_SENSITIVE, ATTR_FLAG_CASE_INSENSITIVE, ATTR_FLAG_NONE } from './arena.js';
2
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-dimension.js';
4
4
 
@@ -19,6 +19,7 @@ const TYPE_NAMES = {
19
19
  [OPERATOR]: "Operator",
20
20
  [PARENTHESIS]: "Parentheses",
21
21
  [URL]: "Url",
22
+ [UNICODE_RANGE]: "UnicodeRange",
22
23
  [VALUE]: "Value",
23
24
  [SELECTOR_LIST]: "SelectorList",
24
25
  [TYPE_SELECTOR]: "TypeSelector",
@@ -43,6 +44,20 @@ const TYPE_NAMES = {
43
44
  [FEATURE_RANGE]: "MediaFeatureRange",
44
45
  [AT_RULE_PRELUDE]: "AtrulePrelude"
45
46
  };
47
+ const ATTR_OPERATOR_NAMES = {
48
+ [ATTR_OPERATOR_NONE]: null,
49
+ [ATTR_OPERATOR_EQUAL]: "=",
50
+ [ATTR_OPERATOR_TILDE_EQUAL]: "~=",
51
+ [ATTR_OPERATOR_PIPE_EQUAL]: "|=",
52
+ [ATTR_OPERATOR_CARET_EQUAL]: "^=",
53
+ [ATTR_OPERATOR_DOLLAR_EQUAL]: "$=",
54
+ [ATTR_OPERATOR_STAR_EQUAL]: "*="
55
+ };
56
+ const ATTR_FLAG_NAMES = {
57
+ [ATTR_FLAG_NONE]: null,
58
+ [ATTR_FLAG_CASE_INSENSITIVE]: "i",
59
+ [ATTR_FLAG_CASE_SENSITIVE]: "s"
60
+ };
46
61
  class CSSNode {
47
62
  arena;
48
63
  source;
@@ -59,6 +74,12 @@ class CSSNode {
59
74
  __get_arena() {
60
75
  return this.arena;
61
76
  }
77
+ get_content() {
78
+ let start = this.arena.get_content_start(this.index);
79
+ let length = this.arena.get_content_length(this.index);
80
+ if (length === 0) return "";
81
+ return this.source.substring(start, start + length);
82
+ }
62
83
  /** Get node type as number (for performance) */
63
84
  get type() {
64
85
  return this.arena.get_type(this.index);
@@ -73,19 +94,20 @@ class CSSNode {
73
94
  let length = this.arena.get_length(this.index);
74
95
  return this.source.substring(start, start + length);
75
96
  }
76
- /** Get the "content" text (property name for declarations, at-rule name for at-rules, layer name for import layers) */
97
+ /** Get the "content" text (at-rule name for at-rules, layer name for import layers) */
77
98
  get name() {
78
- let start = this.arena.get_content_start(this.index);
79
- let length = this.arena.get_content_length(this.index);
80
- if (length === 0) return "";
81
- return this.source.substring(start, start + length);
99
+ let { type } = this;
100
+ if (type === DECLARATION || type === OPERATOR || type === SELECTOR) return;
101
+ return this.get_content();
82
102
  }
83
103
  /**
84
104
  * Alias for name (for declarations: "color" in "color: blue")
85
105
  * More semantic than `name` for declaration nodes
86
106
  */
87
107
  get property() {
88
- return this.name;
108
+ let { type } = this;
109
+ if (type !== DECLARATION && type !== MEDIA_FEATURE) return;
110
+ return this.get_content();
89
111
  }
90
112
  /**
91
113
  * Get the value text (for declarations: "blue" in "color: blue")
@@ -95,32 +117,34 @@ class CSSNode {
95
117
  * For URL nodes with unquoted URL: returns the URL content without quotes
96
118
  */
97
119
  get value() {
98
- let { type, text } = this;
99
- if (type === DECLARATION && this.first_child) {
100
- return this.first_child;
120
+ let { type, text, first_child } = this;
121
+ if (type === DECLARATION && first_child) {
122
+ return first_child;
101
123
  }
102
124
  if (type === DIMENSION) {
103
125
  return parse_dimension(text).value;
104
126
  }
105
127
  if (type === NUMBER) {
106
- return Number.parseFloat(this.text);
128
+ return Number.parseFloat(text);
107
129
  }
108
130
  if (type === URL) {
109
- let firstChild = this.first_child;
110
- if (firstChild && firstChild.type === STRING) {
111
- return firstChild.text;
131
+ if (first_child?.type === STRING) {
132
+ return first_child.text;
112
133
  }
113
134
  if (str_starts_with(text, "url(")) {
114
- let openParen = text.indexOf("(");
115
- let closeParen = text.lastIndexOf(")");
116
- if (openParen !== -1 && closeParen !== -1 && closeParen > openParen) {
117
- let content = text.substring(openParen + 1, closeParen).trim();
135
+ let open_paren = text.indexOf("(");
136
+ let close_paren = text.lastIndexOf(")");
137
+ if (open_paren !== -1 && close_paren !== -1 && close_paren > open_paren) {
138
+ let content = text.substring(open_paren + 1, close_paren).trim();
118
139
  return content;
119
140
  }
120
141
  } else if (text.startsWith('"') || text.startsWith("'")) {
121
142
  return text;
122
143
  }
123
144
  }
145
+ if (type === OPERATOR) {
146
+ return this.get_content();
147
+ }
124
148
  let start = this.arena.get_value_start(this.index);
125
149
  let length = this.arena.get_value_length(this.index);
126
150
  if (length === 0) return null;
@@ -128,11 +152,11 @@ class CSSNode {
128
152
  }
129
153
  /** Get the numeric value for NUMBER and DIMENSION nodes, or null for other node types */
130
154
  get value_as_number() {
131
- let text = this.text;
132
- if (this.type === NUMBER) {
155
+ let { text, type } = this;
156
+ if (type === NUMBER) {
133
157
  return Number.parseFloat(text);
134
158
  }
135
- if (this.type === DIMENSION) {
159
+ if (type === DIMENSION) {
136
160
  return parse_dimension(text).value;
137
161
  }
138
162
  return null;
@@ -191,14 +215,14 @@ class CSSNode {
191
215
  get is_vendor_prefixed() {
192
216
  switch (this.type) {
193
217
  case DECLARATION:
194
- return is_vendor_prefixed(this.name);
218
+ return is_vendor_prefixed(this.get_content());
195
219
  case PSEUDO_CLASS_SELECTOR:
196
220
  case PSEUDO_ELEMENT_SELECTOR:
197
- return is_vendor_prefixed(this.name);
221
+ return is_vendor_prefixed(this.get_content());
198
222
  case AT_RULE:
199
- return is_vendor_prefixed(this.name);
223
+ return is_vendor_prefixed(this.get_content());
200
224
  case FUNCTION:
201
- return is_vendor_prefixed(this.name);
225
+ return is_vendor_prefixed(this.get_content());
202
226
  case IDENTIFIER:
203
227
  return is_vendor_prefixed(this.text);
204
228
  default:
@@ -211,10 +235,11 @@ class CSSNode {
211
235
  }
212
236
  /** Check if this node has a prelude (at-rules and style rules) */
213
237
  get has_prelude() {
214
- if (this.type === AT_RULE) {
238
+ let { type } = this;
239
+ if (type === AT_RULE) {
215
240
  return this.arena.get_value_length(this.index) > 0;
216
241
  }
217
- if (this.type === STYLE_RULE) {
242
+ if (type === STYLE_RULE) {
218
243
  return this.first_child !== null;
219
244
  }
220
245
  return false;
@@ -229,16 +254,17 @@ class CSSNode {
229
254
  }
230
255
  /** Get the block node (for style rules and at-rules with blocks) */
231
256
  get block() {
232
- if (this.type === STYLE_RULE) {
257
+ let { type } = this;
258
+ if (type === STYLE_RULE) {
233
259
  let first = this.first_child;
234
260
  if (!first) return null;
235
- let blockNode = first.next_sibling;
236
- if (blockNode && blockNode.type === BLOCK) {
237
- return blockNode;
261
+ let block_node = first.next_sibling;
262
+ if (block_node?.type === BLOCK) {
263
+ return block_node;
238
264
  }
239
265
  return null;
240
266
  }
241
- if (this.type === AT_RULE) {
267
+ if (type === AT_RULE) {
242
268
  let child = this.first_child;
243
269
  while (child) {
244
270
  if (child.type === BLOCK && !child.next_sibling) {
@@ -309,7 +335,8 @@ class CSSNode {
309
335
  * This allows formatters to distinguish :lang() from :hover
310
336
  */
311
337
  get has_children() {
312
- if (this.type === PSEUDO_CLASS_SELECTOR || this.type === PSEUDO_ELEMENT_SELECTOR) {
338
+ let { type } = this;
339
+ if (type === PSEUDO_CLASS_SELECTOR || type === PSEUDO_ELEMENT_SELECTOR) {
313
340
  if (this.arena.has_flag(this.index, FLAG_HAS_PARENS)) {
314
341
  return true;
315
342
  }
@@ -337,22 +364,24 @@ class CSSNode {
337
364
  // --- An+B Expression Helpers (for NODE_SELECTOR_NTH) ---
338
365
  /** Get the 'a' coefficient from An+B expression (e.g., "2n" from "2n+1", "odd" from "odd") */
339
366
  get nth_a() {
340
- if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
341
- let len = this.arena.get_content_length(this.index);
367
+ let { type, arena, index } = this;
368
+ if (type !== NTH_SELECTOR && type !== NTH_OF_SELECTOR) return void 0;
369
+ let len = arena.get_content_length(index);
342
370
  if (len === 0) return void 0;
343
- let start = this.arena.get_content_start(this.index);
371
+ let start = arena.get_content_start(index);
344
372
  return this.source.substring(start, start + len);
345
373
  }
346
374
  /** Get the 'b' coefficient from An+B expression (e.g., "+1" from "2n+1") */
347
375
  get nth_b() {
348
- if (this.type !== NTH_SELECTOR && this.type !== NTH_OF_SELECTOR) return void 0;
349
- let len = this.arena.get_value_length(this.index);
376
+ let { type, arena, index, source } = this;
377
+ if (type !== NTH_SELECTOR && type !== NTH_OF_SELECTOR) return void 0;
378
+ let len = arena.get_value_length(index);
350
379
  if (len === 0) return void 0;
351
- let start = this.arena.get_value_start(this.index);
352
- let value = this.source.substring(start, start + len);
380
+ let start = arena.get_value_start(index);
381
+ let value = source.substring(start, start + len);
353
382
  let check_pos = start - 1;
354
383
  while (check_pos >= 0) {
355
- let ch = this.source.charCodeAt(check_pos);
384
+ let ch = source.charCodeAt(check_pos);
356
385
  if (is_whitespace(ch)) {
357
386
  check_pos--;
358
387
  continue;
@@ -406,29 +435,45 @@ class CSSNode {
406
435
  */
407
436
  clone(options = {}) {
408
437
  const { deep = true, locations = false } = options;
438
+ let { type, name, property, value, unit } = this;
409
439
  let plain = {
410
- type: this.type,
440
+ type,
411
441
  type_name: this.type_name,
412
- text: this.text,
413
- children: []
442
+ text: this.text
414
443
  };
415
- if (this.name) plain.name = this.name;
416
- if (this.type === DECLARATION) plain.property = this.name;
417
- if (this.value !== void 0 && this.value !== null) {
418
- plain.value = this.value;
419
- if (this.unit) plain.unit = this.unit;
444
+ if (name) {
445
+ plain.name = name;
446
+ }
447
+ if (property) {
448
+ plain.property = property;
449
+ }
450
+ if (value) {
451
+ plain.value = value;
452
+ if (unit) {
453
+ plain.unit = unit;
454
+ }
455
+ }
456
+ if (type === DECLARATION) {
457
+ let { is_important, is_browserhack } = this;
458
+ if (is_important) {
459
+ plain.is_important = true;
460
+ }
461
+ if (is_browserhack) {
462
+ plain.is_browserhack = true;
463
+ }
464
+ }
465
+ let { is_vendor_prefixed: is_vendor_prefixed2, has_error } = this;
466
+ if (is_vendor_prefixed2) {
467
+ plain.is_vendor_prefixed = true;
420
468
  }
421
- if (this.type === DECLARATION) {
422
- plain.is_important = this.is_important;
423
- plain.is_browserhack = this.is_browserhack;
469
+ if (has_error) {
470
+ plain.has_error = true;
424
471
  }
425
- plain.is_vendor_prefixed = this.is_vendor_prefixed;
426
- plain.has_error = this.has_error;
427
- if (this.type === ATTRIBUTE_SELECTOR) {
428
- plain.attr_operator = this.attr_operator;
429
- plain.attr_flags = this.attr_flags;
472
+ if (type === ATTRIBUTE_SELECTOR) {
473
+ plain.attr_operator = ATTR_OPERATOR_NAMES[this.attr_operator];
474
+ plain.attr_flags = ATTR_FLAG_NAMES[this.attr_flags];
430
475
  }
431
- if (this.type === NTH_SELECTOR || this.type === NTH_OF_SELECTOR) {
476
+ if (type === NTH_SELECTOR || type === NTH_OF_SELECTOR) {
432
477
  plain.nth_a = this.nth_a;
433
478
  plain.nth_b = this.nth_b;
434
479
  }
@@ -439,7 +484,8 @@ class CSSNode {
439
484
  plain.length = this.length;
440
485
  plain.end = this.end;
441
486
  }
442
- if (deep) {
487
+ if (deep && type !== DECLARATION) {
488
+ plain.children = [];
443
489
  for (let child of this.children) {
444
490
  plain.children.push(child.clone({ deep: true, locations }));
445
491
  }
@@ -448,4 +494,4 @@ class CSSNode {
448
494
  }
449
495
  }
450
496
 
451
- export { CSSNode, TYPE_NAMES };
497
+ export { ATTR_FLAG_NAMES, ATTR_OPERATOR_NAMES, CSSNode, TYPE_NAMES };
package/dist/index.js CHANGED
@@ -10,4 +10,4 @@ export { is_custom, is_vendor_prefixed, str_equals, str_index_of, str_starts_wit
10
10
  export { CSSNode, TYPE_NAMES } from './css-node.js';
11
11
  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';
12
12
  export { NODE_TYPES } from './constants.js';
13
- 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';
13
+ 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_UNICODE_RANGE, TOKEN_URL, TOKEN_WHITESPACE } from './token-types.js';
@@ -1,6 +1,6 @@
1
1
  import { Lexer } from './tokenize.js';
2
- import { CSSDataArena, VALUE, OPERATOR, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, URL, FUNCTION, PARENTHESIS } from './arena.js';
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';
2
+ import { CSSDataArena, VALUE, OPERATOR, UNICODE_RANGE, HASH, STRING, DIMENSION, NUMBER, IDENTIFIER, URL, FUNCTION, PARENTHESIS } from './arena.js';
3
+ import { TOKEN_EOF, TOKEN_LEFT_PAREN, TOKEN_COMMA, TOKEN_DELIM, TOKEN_FUNCTION, TOKEN_UNICODE_RANGE, 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';
6
6
 
@@ -78,6 +78,8 @@ class ValueParser {
78
78
  return this.create_node(STRING, start, end);
79
79
  case TOKEN_HASH:
80
80
  return this.create_node(HASH, start, end);
81
+ case TOKEN_UNICODE_RANGE:
82
+ return this.create_node(UNICODE_RANGE, start, end);
81
83
  case TOKEN_FUNCTION:
82
84
  return this.parse_function_node(start, end);
83
85
  case TOKEN_DELIM:
@@ -24,7 +24,8 @@ export declare const TOKEN_LEFT_BRACE = 23;
24
24
  export declare const TOKEN_RIGHT_BRACE = 24;
25
25
  export declare const TOKEN_COMMENT = 25;
26
26
  export declare const TOKEN_EOF = 26;
27
- export type TokenType = typeof TOKEN_IDENT | typeof TOKEN_FUNCTION | typeof TOKEN_AT_KEYWORD | typeof TOKEN_HASH | typeof TOKEN_STRING | typeof TOKEN_BAD_STRING | typeof TOKEN_URL | typeof TOKEN_BAD_URL | typeof TOKEN_DELIM | typeof TOKEN_NUMBER | typeof TOKEN_PERCENTAGE | typeof TOKEN_DIMENSION | typeof TOKEN_WHITESPACE | typeof TOKEN_CDO | typeof TOKEN_CDC | typeof TOKEN_COLON | typeof TOKEN_SEMICOLON | typeof TOKEN_COMMA | typeof TOKEN_LEFT_BRACKET | typeof TOKEN_RIGHT_BRACKET | typeof TOKEN_LEFT_PAREN | typeof TOKEN_RIGHT_PAREN | typeof TOKEN_LEFT_BRACE | typeof TOKEN_RIGHT_BRACE | typeof TOKEN_COMMENT | typeof TOKEN_EOF;
27
+ export declare const TOKEN_UNICODE_RANGE = 27;
28
+ export type TokenType = typeof TOKEN_IDENT | typeof TOKEN_FUNCTION | typeof TOKEN_AT_KEYWORD | typeof TOKEN_HASH | typeof TOKEN_STRING | typeof TOKEN_BAD_STRING | typeof TOKEN_URL | typeof TOKEN_BAD_URL | typeof TOKEN_DELIM | typeof TOKEN_NUMBER | typeof TOKEN_PERCENTAGE | typeof TOKEN_DIMENSION | typeof TOKEN_WHITESPACE | typeof TOKEN_CDO | typeof TOKEN_CDC | typeof TOKEN_COLON | typeof TOKEN_SEMICOLON | typeof TOKEN_COMMA | typeof TOKEN_LEFT_BRACKET | typeof TOKEN_RIGHT_BRACKET | typeof TOKEN_LEFT_PAREN | typeof TOKEN_RIGHT_PAREN | typeof TOKEN_LEFT_BRACE | typeof TOKEN_RIGHT_BRACE | typeof TOKEN_COMMENT | typeof TOKEN_EOF | typeof TOKEN_UNICODE_RANGE;
28
29
  export type Token = {
29
30
  type: TokenType;
30
31
  start: number;
@@ -24,5 +24,6 @@ const TOKEN_LEFT_BRACE = 23;
24
24
  const TOKEN_RIGHT_BRACE = 24;
25
25
  const TOKEN_COMMENT = 25;
26
26
  const TOKEN_EOF = 26;
27
+ const TOKEN_UNICODE_RANGE = 27;
27
28
 
28
- 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 };
29
+ 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_UNICODE_RANGE, TOKEN_URL, TOKEN_WHITESPACE };
package/dist/tokenize.js CHANGED
@@ -1,5 +1,5 @@
1
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_STRING, TOKEN_BAD_STRING, TOKEN_PERCENTAGE, TOKEN_DIMENSION, TOKEN_NUMBER, TOKEN_FUNCTION, TOKEN_IDENT, TOKEN_AT_KEYWORD, TOKEN_HASH } from './token-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_STRING, TOKEN_BAD_STRING, TOKEN_PERCENTAGE, TOKEN_DIMENSION, TOKEN_NUMBER, TOKEN_FUNCTION, TOKEN_IDENT, TOKEN_UNICODE_RANGE, 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;
@@ -26,6 +26,9 @@ const CHAR_PLUS = 43;
26
26
  const CHAR_PERCENT = 37;
27
27
  const CHAR_LOWERCASE_E = 101;
28
28
  const CHAR_UPPERCASE_E = 69;
29
+ const CHAR_LOWERCASE_U = 117;
30
+ const CHAR_UPPERCASE_U = 85;
31
+ const CHAR_QUESTION_MARK = 63;
29
32
  const CHAR_CARRIAGE_RETURN = 13;
30
33
  const CHAR_LINE_FEED = 10;
31
34
  class Lexer {
@@ -330,12 +333,58 @@ class Lexer {
330
333
  break;
331
334
  }
332
335
  }
336
+ if (this.pos - start === 1) {
337
+ let first_ch = this.source.charCodeAt(start);
338
+ if ((first_ch === CHAR_LOWERCASE_U || first_ch === CHAR_UPPERCASE_U) && this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_PLUS) {
339
+ return this.consume_unicode_range(start, start_line, start_column);
340
+ }
341
+ }
333
342
  if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_LEFT_PAREN) {
334
343
  this.advance();
335
344
  return this.make_token(TOKEN_FUNCTION, start, this.pos, start_line, start_column);
336
345
  }
337
346
  return this.make_token(TOKEN_IDENT, start, this.pos, start_line, start_column);
338
347
  }
348
+ consume_unicode_range(start, start_line, start_column) {
349
+ this.advance();
350
+ let hex_digits = 0;
351
+ let has_question = false;
352
+ while (this.pos < this.source.length && hex_digits < 6) {
353
+ let ch = this.source.charCodeAt(this.pos);
354
+ if (is_hex_digit(ch)) {
355
+ if (has_question) {
356
+ break;
357
+ }
358
+ this.advance();
359
+ hex_digits++;
360
+ } else if (ch === CHAR_QUESTION_MARK) {
361
+ this.advance();
362
+ hex_digits++;
363
+ has_question = true;
364
+ } else {
365
+ break;
366
+ }
367
+ }
368
+ if (has_question) {
369
+ return this.make_token(TOKEN_UNICODE_RANGE, start, this.pos, start_line, start_column);
370
+ }
371
+ if (this.pos < this.source.length && this.source.charCodeAt(this.pos) === CHAR_HYPHEN) {
372
+ if (this.pos + 1 < this.source.length && is_hex_digit(this.source.charCodeAt(this.pos + 1))) {
373
+ this.advance();
374
+ let end_hex_digits = 0;
375
+ while (this.pos < this.source.length && end_hex_digits < 6) {
376
+ let ch = this.source.charCodeAt(this.pos);
377
+ if (is_hex_digit(ch)) {
378
+ this.advance();
379
+ end_hex_digits++;
380
+ } else {
381
+ break;
382
+ }
383
+ }
384
+ }
385
+ }
386
+ return this.make_token(TOKEN_UNICODE_RANGE, start, this.pos, start_line, start_column);
387
+ }
339
388
  consume_at_keyword(start_line, start_column) {
340
389
  let start = this.pos;
341
390
  this.advance();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-parser",
3
- "version": "0.12.4",
3
+ "version": "0.13.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",