@tbela99/css-parser 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.editorconfig +484 -0
  2. package/README.md +13 -8
  3. package/dist/index-umd-web.js +2451 -2554
  4. package/dist/index.cjs +2605 -2708
  5. package/dist/index.d.ts +73 -23
  6. package/dist/lib/ast/expand.js +29 -4
  7. package/dist/lib/ast/math/expression.js +1 -1
  8. package/dist/lib/ast/minify.js +31 -17
  9. package/dist/lib/ast/types.js +2 -0
  10. package/dist/lib/ast/walk.js +12 -0
  11. package/dist/lib/parser/declaration/map.js +59 -52
  12. package/dist/lib/parser/declaration/set.js +0 -12
  13. package/dist/lib/parser/parse.js +204 -139
  14. package/dist/lib/parser/tokenize.js +15 -3
  15. package/dist/lib/renderer/color/color.js +2 -2
  16. package/dist/lib/renderer/color/hex.js +1 -1
  17. package/dist/lib/renderer/color/hsl.js +1 -1
  18. package/dist/lib/renderer/color/hwb.js +2 -2
  19. package/dist/lib/renderer/color/lab.js +3 -2
  20. package/dist/lib/renderer/color/lch.js +1 -1
  21. package/dist/lib/renderer/color/oklab.js +2 -2
  22. package/dist/lib/renderer/color/oklch.js +1 -1
  23. package/dist/lib/renderer/color/p3.js +1 -1
  24. package/dist/lib/renderer/color/prophotoRgb.js +2 -2
  25. package/dist/lib/renderer/color/prophotorgb.js +2 -2
  26. package/dist/lib/renderer/color/rgb.js +1 -1
  27. package/dist/lib/renderer/color/srgb.js +2 -2
  28. package/dist/lib/renderer/color/utils/constants.js +1 -1
  29. package/dist/lib/renderer/color/xyz.js +2 -18
  30. package/dist/lib/renderer/color/xyzd50.js +20 -2
  31. package/dist/lib/renderer/render.js +37 -8
  32. package/dist/lib/renderer/sourcemap/sourcemap.js +1 -1
  33. package/dist/lib/syntax/syntax.js +337 -1
  34. package/dist/lib/validation/at-rules/container.js +353 -0
  35. package/dist/lib/validation/at-rules/counter-style.js +2 -2
  36. package/dist/lib/validation/at-rules/custom-media.js +52 -0
  37. package/dist/lib/validation/at-rules/document.js +40 -60
  38. package/dist/lib/validation/at-rules/else.js +5 -0
  39. package/dist/lib/validation/at-rules/font-feature-values.js +3 -0
  40. package/dist/lib/validation/at-rules/import.js +64 -59
  41. package/dist/lib/validation/at-rules/layer.js +3 -0
  42. package/dist/lib/validation/at-rules/media.js +118 -29
  43. package/dist/lib/validation/at-rules/supports.js +51 -20
  44. package/dist/lib/validation/at-rules/when.js +178 -0
  45. package/dist/lib/validation/atrule.js +25 -10
  46. package/dist/lib/validation/config.js +20 -15
  47. package/dist/lib/validation/config.json.js +242 -74
  48. package/dist/lib/validation/declaration.js +32 -10
  49. package/dist/lib/validation/parser/parse.js +87 -103
  50. package/dist/lib/validation/parser/types.js +2 -2
  51. package/dist/lib/validation/selector.js +6 -3
  52. package/dist/lib/validation/syntax.js +86 -6
  53. package/dist/lib/validation/syntaxes/complex-selector-list.js +16 -12
  54. package/dist/lib/validation/syntaxes/complex-selector.js +17 -247
  55. package/dist/lib/validation/syntaxes/compound-selector.js +226 -0
  56. package/dist/lib/validation/syntaxes/image.js +29 -0
  57. package/dist/lib/validation/syntaxes/keyframe-block-list.js +1 -1
  58. package/dist/lib/validation/syntaxes/keyframe-selector.js +1 -1
  59. package/dist/lib/validation/syntaxes/layer-name.js +5 -16
  60. package/dist/lib/validation/syntaxes/relative-selector-list.js +43 -13
  61. package/dist/lib/validation/utils/list.js +2 -2
  62. package/dist/node/index.js +1 -1
  63. package/dist/web/index.js +1 -1
  64. package/package.json +10 -9
@@ -1,4 +1,4 @@
1
- import { isPseudo, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isDimension, parseDimension, isIdent, isHexColor, isHash, isIdentStart, mathFuncs, isColor, mediaTypes } from '../syntax/syntax.js';
1
+ import { webkitPseudoAliasMap, isIdentStart, isIdent, mathFuncs, isColor, isHexColor, isPseudo, pseudoElements, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isDimension, parseDimension, isHash, mediaTypes } from '../syntax/syntax.js';
2
2
  import './utils/config.js';
3
3
  import { EnumToken, funcLike, ValidationLevel } from '../ast/types.js';
4
4
  import { minify, definedPropertySettings, combinators } from '../ast/minify.js';
@@ -13,6 +13,7 @@ import '../validation/parser/types.js';
13
13
  import '../validation/parser/parse.js';
14
14
  import { validateSelector } from '../validation/selector.js';
15
15
  import { validateAtRule } from '../validation/atrule.js';
16
+ import '../validation/syntaxes/complex-selector.js';
16
17
 
17
18
  const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
18
19
  const trimWhiteSpace = [EnumToken.CommentTokenType, EnumToken.GtTokenType, EnumToken.GteTokenType, EnumToken.LtTokenType, EnumToken.LteTokenType, EnumToken.ColumnCombinatorTokenType];
@@ -29,51 +30,14 @@ const enumTokenHints = new Set([
29
30
  EnumToken.StartMatchTokenType, EnumToken.EndMatchTokenType, EnumToken.IncludeMatchTokenType, EnumToken.DashMatchTokenType, EnumToken.ContainMatchTokenType,
30
31
  EnumToken.EOFTokenType
31
32
  ]);
32
- const webkitPseudoAliasMap = {
33
- '-webkit-autofill': 'autofill',
34
- '-webkit-any': 'is',
35
- '-moz-any': 'is',
36
- '-webkit-border-after': 'border-block-end',
37
- '-webkit-border-after-color': 'border-block-end-color',
38
- '-webkit-border-after-style': 'border-block-end-style',
39
- '-webkit-border-after-width': 'border-block-end-width',
40
- '-webkit-border-before': 'border-block-start',
41
- '-webkit-border-before-color': 'border-block-start-color',
42
- '-webkit-border-before-style': 'border-block-start-style',
43
- '-webkit-border-before-width': 'border-block-start-width',
44
- '-webkit-border-end': 'border-inline-end',
45
- '-webkit-border-end-color': 'border-inline-end-color',
46
- '-webkit-border-end-style': 'border-inline-end-style',
47
- '-webkit-border-end-width': 'border-inline-end-width',
48
- '-webkit-border-start': 'border-inline-start',
49
- '-webkit-border-start-color': 'border-inline-start-color',
50
- '-webkit-border-start-style': 'border-inline-start-style',
51
- '-webkit-border-start-width': 'border-inline-start-width',
52
- '-webkit-box-align': 'align-items',
53
- '-webkit-box-direction': 'flex-direction',
54
- '-webkit-box-flex': 'flex-grow',
55
- '-webkit-box-lines': 'flex-flow',
56
- '-webkit-box-ordinal-group': 'order',
57
- '-webkit-box-orient': 'flex-direction',
58
- '-webkit-box-pack': 'justify-content',
59
- '-webkit-column-break-after': 'break-after',
60
- '-webkit-column-break-before': 'break-before',
61
- '-webkit-column-break-inside': 'break-inside',
62
- '-webkit-font-feature-settings': 'font-feature-settings',
63
- '-webkit-hyphenate-character': 'hyphenate-character',
64
- '-webkit-initial-letter': 'initial-letter',
65
- '-webkit-margin-end': 'margin-block-end',
66
- '-webkit-margin-start': 'margin-block-start',
67
- '-webkit-padding-after': 'padding-block-end',
68
- '-webkit-padding-before': 'padding-block-start',
69
- '-webkit-padding-end': 'padding-inline-end',
70
- '-webkit-padding-start': 'padding-inline-start',
71
- '-webkit-min-device-pixel-ratio': 'min-resolution',
72
- '-webkit-max-device-pixel-ratio': 'max-resolution'
73
- };
74
33
  function reject(reason) {
75
34
  throw new Error(reason ?? 'Parsing aborted');
76
35
  }
36
+ /**
37
+ * parse css string
38
+ * @param iterator
39
+ * @param options
40
+ */
77
41
  async function doParse(iterator, options = {}) {
78
42
  if (options.signal != null) {
79
43
  options.signal.addEventListener('abort', reject);
@@ -82,6 +46,7 @@ async function doParse(iterator, options = {}) {
82
46
  src: '',
83
47
  sourcemap: false,
84
48
  minify: true,
49
+ pass: 1,
85
50
  parseColor: true,
86
51
  nestingRules: false,
87
52
  resolveImport: false,
@@ -95,6 +60,7 @@ async function doParse(iterator, options = {}) {
95
60
  setParent: true,
96
61
  removePrefix: false,
97
62
  validation: true,
63
+ lenient: true,
98
64
  ...options
99
65
  };
100
66
  if (options.expandNestingRules) {
@@ -133,9 +99,10 @@ async function doParse(iterator, options = {}) {
133
99
  }
134
100
  const iter = tokenize(iterator);
135
101
  let item;
102
+ const rawTokens = [];
136
103
  while (item = iter.next().value) {
137
104
  stats.bytesIn = item.bytesIn;
138
- //
105
+ rawTokens.push(item);
139
106
  // doParse error
140
107
  if (item.hint != null && BadTokensTypes.includes(item.hint)) {
141
108
  // bad token
@@ -145,7 +112,8 @@ async function doParse(iterator, options = {}) {
145
112
  tokens.push(item);
146
113
  }
147
114
  if (item.token == ';' || item.token == '{') {
148
- let node = await parseNode(tokens, context, stats, options, errors, src, map);
115
+ let node = await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
116
+ rawTokens.length = 0;
149
117
  if (node != null) {
150
118
  // @ts-ignore
151
119
  stack.push(node);
@@ -171,7 +139,8 @@ async function doParse(iterator, options = {}) {
171
139
  map = new Map;
172
140
  }
173
141
  else if (item.token == '}') {
174
- await parseNode(tokens, context, stats, options, errors, src, map);
142
+ await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
143
+ rawTokens.length = 0;
175
144
  const previousNode = stack.pop();
176
145
  // @ts-ignore
177
146
  context = stack[stack.length - 1] ?? ast;
@@ -194,9 +163,10 @@ async function doParse(iterator, options = {}) {
194
163
  }
195
164
  }
196
165
  if (tokens.length > 0) {
197
- await parseNode(tokens, context, stats, options, errors, src, map);
166
+ await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
167
+ rawTokens.length = 0;
198
168
  if (context != null && context.typ == EnumToken.InvalidRuleTokenType) {
199
- const index = context.chi.findIndex(node => node == context);
169
+ const index = context.chi.findIndex((node) => node == context);
200
170
  if (index > -1) {
201
171
  context.chi.splice(index, 1);
202
172
  }
@@ -206,6 +176,7 @@ async function doParse(iterator, options = {}) {
206
176
  const previousNode = stack.pop();
207
177
  // @ts-ignore
208
178
  context = stack[stack.length - 1] ?? ast;
179
+ // remove empty nodes
209
180
  // @ts-ignore
210
181
  if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
211
182
  // @ts-ignore
@@ -255,36 +226,12 @@ async function doParse(iterator, options = {}) {
255
226
  }
256
227
  if (options.minify) {
257
228
  if (ast.chi.length > 0) {
258
- minify(ast, options, true, errors, false);
229
+ let passes = options.pass ?? 1;
230
+ while (passes--) {
231
+ minify(ast, options, true, errors, false);
232
+ }
259
233
  }
260
234
  }
261
- // if (options.setParent) {
262
- //
263
- // const nodes: Array<AstRule | AstAtRule | AstRuleStyleSheet> = [ast];
264
- // let node: AstNode;
265
- //
266
- // while ((node = nodes.shift()!)) {
267
- //
268
- // // @ts-ignore
269
- // if (node.chi.length > 0) {
270
- //
271
- // // @ts-ignore
272
- // for (const child of node.chi) {
273
- //
274
- // if (child.parent != node) {
275
- //
276
- // Object.defineProperty(child, 'parent', {...definedPropertySettings, value: node});
277
- // }
278
- //
279
- // if ('chi' in child && child.chi.length > 0) {
280
- //
281
- // // @ts-ignore
282
- // nodes.push(<AstRule | AstAtRule>child);
283
- // }
284
- // }
285
- // }
286
- // }
287
- // }
288
235
  const endTime = performance.now();
289
236
  if (options.signal != null) {
290
237
  options.signal.removeEventListener('abort', reject);
@@ -301,7 +248,17 @@ async function doParse(iterator, options = {}) {
301
248
  }
302
249
  };
303
250
  }
304
- async function parseNode(results, context, stats, options, errors, src, map) {
251
+ function getLastNode(context) {
252
+ let i = context.chi.length;
253
+ while (i--) {
254
+ if ([EnumToken.CommentTokenType, EnumToken.CDOCOMMTokenType, EnumToken.WhitespaceTokenType].includes(context.chi[i].typ)) {
255
+ continue;
256
+ }
257
+ return context.chi[i];
258
+ }
259
+ return null;
260
+ }
261
+ async function parseNode(results, context, stats, options, errors, src, map, rawTokens) {
305
262
  let tokens = [];
306
263
  for (const t of results) {
307
264
  const node = getTokenType(t.token, t.hint);
@@ -356,24 +313,6 @@ async function parseNode(results, context, stats, options, errors, src, map) {
356
313
  if (tokens[0]?.typ == EnumToken.AtRuleTokenType) {
357
314
  const atRule = tokens.shift();
358
315
  const position = map.get(atRule);
359
- // if (atRule.val == 'charset') {
360
- //
361
- // if (context.typ != EnumToken.StyleSheetNodeType || context.chi.some(t => t.typ != EnumToken.CDOCOMMTokenType && t.typ != EnumToken.CommentNodeType)) {
362
- //
363
- // errors.push({
364
- // action: 'drop',
365
- // message: 'doParse: invalid @charset',
366
- // location: {src, ...position}
367
- // });
368
- //
369
- // return null;
370
- // }
371
- //
372
- // if (options.removeCharset) {
373
- //
374
- // return null;
375
- // }
376
- // }
377
316
  // @ts-ignore
378
317
  while ([EnumToken.WhitespaceTokenType].includes(tokens[0]?.typ)) {
379
318
  tokens.shift();
@@ -391,8 +330,13 @@ async function parseNode(results, context, stats, options, errors, src, map) {
391
330
  continue;
392
331
  }
393
332
  if (type != EnumToken.AtRuleNodeType) {
394
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
395
- return null;
333
+ // @ts-ignore
334
+ if (!(type == EnumToken.InvalidAtRuleTokenType &&
335
+ // @ts-ignore
336
+ ['charset', 'layer', 'import'].includes(context.chi[i].nam))) {
337
+ errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
338
+ return null;
339
+ }
396
340
  }
397
341
  // @ts-ignore
398
342
  const name = context.chi[i].nam;
@@ -427,12 +371,20 @@ async function parseNode(results, context, stats, options, errors, src, map) {
427
371
  if (tokens[0].typ == EnumToken.UrlFunctionTokenType) {
428
372
  if (tokens[1].typ == EnumToken.UrlTokenTokenType || tokens[1].typ == EnumToken.StringTokenType) {
429
373
  tokens.shift();
430
- if (tokens[1].typ == EnumToken.UrlTokenTokenType) {
374
+ if (tokens[0]?.typ == EnumToken.UrlTokenTokenType) {
431
375
  // @ts-ignore
432
376
  tokens[0].typ = EnumToken.StringTokenType;
433
377
  // @ts-ignore
434
378
  tokens[0].val = `"${tokens[0].val}"`;
435
379
  }
380
+ // @ts-ignore
381
+ while (tokens[1]?.typ == EnumToken.WhitespaceTokenType || tokens[1]?.typ == EnumToken.CommentTokenType) {
382
+ tokens.splice(1, 1);
383
+ }
384
+ // @ts-ignore
385
+ if (tokens[1]?.typ == EnumToken.EndParensTokenType) {
386
+ tokens.splice(1, 1);
387
+ }
436
388
  }
437
389
  }
438
390
  // @ts-ignore
@@ -470,8 +422,43 @@ async function parseNode(results, context, stats, options, errors, src, map) {
470
422
  // https://www.w3.org/TR/css-nesting-1/#conditionals
471
423
  // allowed nesting at-rules
472
424
  // there must be a top level rule in the stack
473
- if (atRule.val == 'charset' && options.removeCharset) {
474
- return null;
425
+ if (atRule.val == 'charset') {
426
+ let spaces = 0;
427
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/@charset
428
+ for (let k = 1; k < rawTokens.length; k++) {
429
+ if (rawTokens[k].hint == EnumToken.WhitespaceTokenType) {
430
+ spaces += rawTokens[k].len;
431
+ continue;
432
+ }
433
+ if (rawTokens[k].hint == EnumToken.CommentTokenType) {
434
+ continue;
435
+ }
436
+ if (rawTokens[k].hint == EnumToken.CDOCOMMTokenType) {
437
+ continue;
438
+ }
439
+ if (spaces > 1) {
440
+ errors.push({
441
+ action: 'drop',
442
+ message: '@charset must have only one space',
443
+ // @ts-ignore
444
+ location: { src, ...(map.get(atRule) ?? position) }
445
+ });
446
+ return null;
447
+ }
448
+ if (rawTokens[k].hint != EnumToken.StringTokenType || rawTokens[k].token[0] != '"') {
449
+ errors.push({
450
+ action: 'drop',
451
+ message: '@charset expects a "<charset>"',
452
+ // @ts-ignore
453
+ location: { src, ...(map.get(atRule) ?? position) }
454
+ });
455
+ return null;
456
+ }
457
+ break;
458
+ }
459
+ if (options.removeCharset) {
460
+ return null;
461
+ }
475
462
  }
476
463
  const t = parseAtRulePrelude(parseTokens(tokens, { minify: options.minify }), atRule);
477
464
  const raw = t.reduce((acc, curr) => {
@@ -481,11 +468,11 @@ async function parseNode(results, context, stats, options, errors, src, map) {
481
468
  const node = {
482
469
  typ: EnumToken.AtRuleNodeType,
483
470
  nam: renderToken(atRule, { removeComments: true }),
484
- tokens: t,
471
+ // tokens: t,
485
472
  val: raw.join('')
486
473
  };
487
474
  Object.defineProperties(node, {
488
- tokens: { ...definedPropertySettings, enumerable: true, value: tokens.slice() },
475
+ tokens: { ...definedPropertySettings, enumerable: false, value: tokens.slice() },
489
476
  raw: { ...definedPropertySettings, value: raw }
490
477
  });
491
478
  if (delim.typ == EnumToken.BlockStartTokenType) {
@@ -499,11 +486,36 @@ async function parseNode(results, context, stats, options, errors, src, map) {
499
486
  node.loc = loc;
500
487
  }
501
488
  if (options.validation) {
502
- const valid = validateAtRule(node, options, context);
489
+ let isValid = true;
490
+ if (node.nam == 'else') {
491
+ const prev = getLastNode(context);
492
+ if (prev != null && prev.typ == EnumToken.AtRuleNodeType && ['when', 'else'].includes(prev.nam)) {
493
+ if (prev.nam == 'else') {
494
+ isValid = Array.isArray(prev.tokens) && prev.tokens.length > 0;
495
+ }
496
+ }
497
+ else {
498
+ isValid = false;
499
+ }
500
+ }
501
+ const valid = isValid ? validateAtRule(node, options, context) : {
502
+ valid: ValidationLevel.Drop,
503
+ node,
504
+ syntax: '@' + node.nam,
505
+ error: '@' + node.nam + ' not allowed here'};
503
506
  if (valid.valid == ValidationLevel.Drop) {
507
+ errors.push({
508
+ action: 'drop',
509
+ message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
510
+ // @ts-ignore
511
+ location: { src, ...(map.get(valid.node) ?? position) }
512
+ });
504
513
  // @ts-ignore
505
514
  node.typ = EnumToken.InvalidAtRuleTokenType;
506
515
  }
516
+ else {
517
+ node.val = node.tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false, removeComments: true }), '');
518
+ }
507
519
  }
508
520
  // @ts-ignore
509
521
  context.chi.push(node);
@@ -585,7 +597,7 @@ async function parseNode(results, context, stats, options, errors, src, map) {
585
597
  };
586
598
  Object.defineProperty(node, 'tokens', {
587
599
  ...definedPropertySettings,
588
- enumerable: true,
600
+ enumerable: false,
589
601
  value: tokens.slice()
590
602
  });
591
603
  let raw = [...uniq.values()];
@@ -682,7 +694,24 @@ async function parseNode(results, context, stats, options, errors, src, map) {
682
694
  };
683
695
  const result = parseDeclarationNode(node, errors, src, position);
684
696
  if (result != null) {
685
- if (options.validation) ;
697
+ // if (options.validation) {
698
+ //
699
+ // const valid: ValidationResult = validateDeclaration(result, options, context);
700
+ //
701
+ // // console.error({valid});
702
+ //
703
+ // if (valid.valid == ValidationLevel.Drop) {
704
+ //
705
+ // errors.push({
706
+ // action: 'drop',
707
+ // message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, {minify: false}), '') + '"',
708
+ // // @ts-ignore
709
+ // location: {src, ...(map.get(valid.node) ?? position)}
710
+ // });
711
+ //
712
+ // return null;
713
+ // }
714
+ // }
686
715
  // @ts-ignore
687
716
  context.chi.push(result);
688
717
  Object.defineProperty(result, 'parent', { ...definedPropertySettings, value: context });
@@ -691,6 +720,11 @@ async function parseNode(results, context, stats, options, errors, src, map) {
691
720
  }
692
721
  }
693
722
  }
723
+ /**
724
+ * parse at-rule prelude
725
+ * @param tokens
726
+ * @param atRule
727
+ */
694
728
  function parseAtRulePrelude(tokens, atRule) {
695
729
  // @ts-ignore
696
730
  for (const { value, parent } of walkValues(tokens, null, null, true)) {
@@ -759,17 +793,18 @@ function parseAtRulePrelude(tokens, atRule) {
759
793
  continue;
760
794
  }
761
795
  }
762
- if (value.typ == EnumToken.ParensTokenType) {
796
+ if (value.typ == EnumToken.ParensTokenType || (value.typ == EnumToken.FunctionTokenType && ['media', 'supports', 'style', 'scroll-state'].includes(value.val))) {
763
797
  // @todo parse range and declarations
764
798
  // parseDeclaration(parent.chi);
765
799
  let i;
766
800
  let nameIndex = -1;
767
801
  let valueIndex = -1;
802
+ const dashedIdent = value.typ == EnumToken.FunctionTokenType && value.val == 'style';
768
803
  for (let i = 0; i < value.chi.length; i++) {
769
804
  if (value.chi[i].typ == EnumToken.CommentTokenType || value.chi[i].typ == EnumToken.WhitespaceTokenType) {
770
805
  continue;
771
806
  }
772
- if (value.chi[i].typ == EnumToken.IdenTokenType) {
807
+ if ((dashedIdent && value.chi[i].typ == EnumToken.DashedIdenTokenType) || value.chi[i].typ == EnumToken.IdenTokenType || value.chi[i].typ == EnumToken.FunctionTokenType || value.chi[i].typ == EnumToken.ColorTokenType) {
773
808
  nameIndex = i;
774
809
  }
775
810
  break;
@@ -798,6 +833,13 @@ function parseAtRulePrelude(tokens, atRule) {
798
833
  ].includes(value.chi[valueIndex].typ)) {
799
834
  const val = value.chi.splice(valueIndex, 1)[0];
800
835
  const node = value.chi.splice(nameIndex, 1)[0];
836
+ // 'background'
837
+ // @ts-ignore
838
+ if (node.typ == EnumToken.ColorTokenType && node.kin == 'dpsys') {
839
+ // @ts-ignore
840
+ delete node.kin;
841
+ node.typ = EnumToken.IdenTokenType;
842
+ }
801
843
  while (value.chi[0]?.typ == EnumToken.WhitespaceTokenType) {
802
844
  value.chi.shift();
803
845
  }
@@ -815,6 +857,10 @@ function parseAtRulePrelude(tokens, atRule) {
815
857
  }
816
858
  return tokens;
817
859
  }
860
+ /**
861
+ * parse selector
862
+ * @param tokens
863
+ */
818
864
  function parseSelector(tokens) {
819
865
  for (const { value, previousValue, nextValue, parent } of walkValues(tokens)) {
820
866
  if (value.typ == EnumToken.CommentTokenType ||
@@ -958,6 +1004,11 @@ function parseSelector(tokens) {
958
1004
  //
959
1005
  // return doParse(`.x{${src}`, options).then((result: ParseResult) => <AstDeclaration[]>(<AstRule>result.ast.chi[0]).chi.filter(t => t.typ == EnumToken.DeclarationNodeType));
960
1006
  // }
1007
+ /**
1008
+ * parse string
1009
+ * @param src
1010
+ * @param options
1011
+ */
961
1012
  function parseString(src, options = { location: false }) {
962
1013
  return parseTokens([...tokenize(src)].map(t => {
963
1014
  const token = getTokenType(t.token, t.hint);
@@ -1019,10 +1070,16 @@ function getTokenType(val, hint) {
1019
1070
  val: val.slice(0, -1),
1020
1071
  chi: []
1021
1072
  }
1022
- : {
1023
- typ: EnumToken.PseudoClassTokenType,
1073
+ : (
1074
+ // https://www.w3.org/TR/selectors-4/#single-colon-pseudos
1075
+ val.startsWith('::') || pseudoElements.includes(val) ? {
1076
+ typ: EnumToken.PseudoElementTokenType,
1024
1077
  val
1025
- };
1078
+ } :
1079
+ {
1080
+ typ: EnumToken.PseudoClassTokenType,
1081
+ val
1082
+ });
1026
1083
  }
1027
1084
  if (isAtKeyword(val)) {
1028
1085
  return {
@@ -1039,7 +1096,7 @@ function getTokenType(val, hint) {
1039
1096
  chi: []
1040
1097
  };
1041
1098
  }
1042
- if (['linear-gradient', 'radial-gradient', 'repeating-linear-gradient', 'repeating-radial-gradient', 'conic-gradient', 'image', 'image-set', 'element', 'cross-fade'].includes(val)) {
1099
+ if (['linear-gradient', 'radial-gradient', 'repeating-linear-gradient', 'repeating-radial-gradient', 'conic-gradient', 'image', 'image-set', 'element', 'cross-fade', 'paint'].includes(val)) {
1043
1100
  return {
1044
1101
  typ: EnumToken.ImageFunctionTokenType,
1045
1102
  val,
@@ -1139,6 +1196,11 @@ function getTokenType(val, hint) {
1139
1196
  val
1140
1197
  };
1141
1198
  }
1199
+ /**
1200
+ * parse token list
1201
+ * @param tokens
1202
+ * @param options
1203
+ */
1142
1204
  function parseTokens(tokens, options = {}) {
1143
1205
  for (let i = 0; i < tokens.length; i++) {
1144
1206
  const t = tokens[i];
@@ -1194,25 +1256,25 @@ function parseTokens(tokens, options = {}) {
1194
1256
  break;
1195
1257
  }
1196
1258
  }
1197
- Object.assign(t, {
1259
+ const attr = Object.assign(t, {
1198
1260
  typ: inAttr == 0 ? EnumToken.AttrTokenType : EnumToken.InvalidAttrTokenType,
1199
1261
  chi: tokens.splice(i + 1, k - i)
1200
1262
  });
1201
1263
  // @ts-ignore
1202
- if (t.chi.at(-1).typ == EnumToken.AttrEndTokenType) {
1264
+ if (attr.chi.at(-1).typ == EnumToken.AttrEndTokenType) {
1203
1265
  // @ts-ignore
1204
- t.chi.pop();
1266
+ attr.chi.pop();
1205
1267
  }
1206
1268
  // @ts-ignore
1207
- if (t.chi.length > 1) {
1269
+ if (attr.chi.length > 1) {
1208
1270
  /*(<AttrToken>t).chi =*/
1209
1271
  // @ts-ignore
1210
- parseTokens(t.chi, t.typ);
1272
+ parseTokens(attr.chi, t.typ);
1211
1273
  }
1212
- let m = t.chi.length;
1274
+ let m = attr.chi.length;
1213
1275
  let val;
1214
- for (m = 0; m < t.chi.length; m++) {
1215
- val = t.chi[m];
1276
+ for (m = 0; m < attr.chi.length; m++) {
1277
+ val = attr.chi[m];
1216
1278
  if (val.typ == EnumToken.StringTokenType) {
1217
1279
  const slice = val.val.slice(1, -1);
1218
1280
  if ((slice.charAt(0) != '-' || (slice.charAt(0) == '-' && isIdentStart(slice.charCodeAt(1)))) && isIdent(slice)) {
@@ -1222,55 +1284,55 @@ function parseTokens(tokens, options = {}) {
1222
1284
  else if (val.typ == EnumToken.LiteralTokenType && val.val == '|') {
1223
1285
  let upper = m;
1224
1286
  let lower = m;
1225
- while (++upper < t.chi.length) {
1226
- if (t.chi[upper].typ == EnumToken.CommentTokenType) {
1287
+ while (++upper < attr.chi.length) {
1288
+ if (attr.chi[upper].typ == EnumToken.CommentTokenType) {
1227
1289
  continue;
1228
1290
  }
1229
1291
  break;
1230
1292
  }
1231
1293
  while (lower-- > 0) {
1232
- if (t.chi[lower].typ == EnumToken.CommentTokenType) {
1294
+ if (attr.chi[lower].typ == EnumToken.CommentTokenType) {
1233
1295
  continue;
1234
1296
  }
1235
1297
  break;
1236
1298
  }
1237
1299
  // @ts-ignore
1238
- t.chi[m] = {
1300
+ attr.chi[m] = {
1239
1301
  typ: EnumToken.NameSpaceAttributeTokenType,
1240
- l: t.chi[lower],
1241
- r: t.chi[upper]
1302
+ l: attr.chi[lower],
1303
+ r: attr.chi[upper]
1242
1304
  };
1243
- t.chi.splice(upper, 1);
1305
+ attr.chi.splice(upper, 1);
1244
1306
  if (lower >= 0) {
1245
- t.chi.splice(lower, 1);
1307
+ attr.chi.splice(lower, 1);
1246
1308
  m--;
1247
1309
  }
1248
1310
  }
1249
1311
  else if ([
1250
1312
  EnumToken.DashMatchTokenType, EnumToken.StartMatchTokenType, EnumToken.ContainMatchTokenType, EnumToken.EndMatchTokenType, EnumToken.IncludeMatchTokenType, EnumToken.DelimTokenType
1251
- ].includes(t.chi[m].typ)) {
1313
+ ].includes(attr.chi[m].typ)) {
1252
1314
  let upper = m;
1253
1315
  let lower = m;
1254
- while (++upper < t.chi.length) {
1255
- if (t.chi[upper].typ == EnumToken.CommentTokenType) {
1316
+ while (++upper < attr.chi.length) {
1317
+ if (attr.chi[upper].typ == EnumToken.CommentTokenType) {
1256
1318
  continue;
1257
1319
  }
1258
1320
  break;
1259
1321
  }
1260
1322
  while (lower-- > 0) {
1261
- if (t.chi[lower].typ == EnumToken.CommentTokenType) {
1323
+ if (attr.chi[lower].typ == EnumToken.CommentTokenType) {
1262
1324
  continue;
1263
1325
  }
1264
1326
  break;
1265
1327
  }
1266
- val = t.chi[lower];
1328
+ val = attr.chi[lower];
1267
1329
  if (val.typ == EnumToken.StringTokenType) {
1268
1330
  const slice = val.val.slice(1, -1);
1269
1331
  if ((slice.charAt(0) != '-' || (slice.charAt(0) == '-' && isIdentStart(slice.charCodeAt(1)))) && isIdent(slice)) {
1270
1332
  Object.assign(val, { typ: EnumToken.IdenTokenType, val: slice });
1271
1333
  }
1272
1334
  }
1273
- val = t.chi[upper];
1335
+ val = attr.chi[upper];
1274
1336
  if (val.typ == EnumToken.StringTokenType) {
1275
1337
  const slice = val.val.slice(1, -1);
1276
1338
  if ((slice.charAt(0) != '-' || (slice.charAt(0) == '-' && isIdentStart(slice.charCodeAt(1)))) && isIdent(slice)) {
@@ -1390,7 +1452,9 @@ function parseTokens(tokens, options = {}) {
1390
1452
  t.typ = EnumToken.ColorTokenType;
1391
1453
  // @ts-ignore
1392
1454
  t.kin = t.val;
1455
+ // @ts-ignore
1393
1456
  if (t.chi[0].typ == EnumToken.IdenTokenType) {
1457
+ // @ts-ignore
1394
1458
  if (t.chi[0].val == 'from') {
1395
1459
  // @ts-ignore
1396
1460
  t.cal = 'rel';
@@ -1400,10 +1464,11 @@ function parseTokens(tokens, options = {}) {
1400
1464
  // @ts-ignore
1401
1465
  t.cal = 'mix';
1402
1466
  }
1403
- else if (t.val == 'color') {
1404
- // @ts-ignore
1405
- t.cal = 'col';
1406
- // t.chi = t.chi.filter((t: Token) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(t.typ));
1467
+ else { // @ts-ignore
1468
+ if (t.val == 'color') {
1469
+ // @ts-ignore
1470
+ t.cal = 'col';
1471
+ }
1407
1472
  }
1408
1473
  }
1409
1474
  const filter = [EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType];
@@ -1437,7 +1502,7 @@ function parseTokens(tokens, options = {}) {
1437
1502
  if (t.chi.length > 0) {
1438
1503
  if (t.typ == EnumToken.PseudoClassFuncTokenType && t.val == ':is' && options.minify) {
1439
1504
  //
1440
- const count = t.chi.filter(t => t.typ != EnumToken.CommentTokenType).length;
1505
+ const count = t.chi.filter((t) => t.typ != EnumToken.CommentTokenType).length;
1441
1506
  if (count == 1 ||
1442
1507
  (i == 0 &&
1443
1508
  (tokens[i + 1]?.typ == EnumToken.CommaTokenType || tokens.length == i + 1)) ||
@@ -1474,4 +1539,4 @@ function parseTokens(tokens, options = {}) {
1474
1539
  return tokens;
1475
1540
  }
1476
1541
 
1477
- export { doParse, parseAtRulePrelude, parseSelector, parseString, parseTokens, urlTokenMatcher };
1542
+ export { doParse, parseSelector, parseString, parseTokens, urlTokenMatcher };