@tbela99/css-parser 0.9.1 → 1.1.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +265 -0
  2. package/LICENSE.md +1 -1
  3. package/README.md +29 -17
  4. package/dist/index-umd-web.js +7461 -4360
  5. package/dist/index.cjs +8608 -5507
  6. package/dist/index.d.ts +203 -61
  7. package/dist/lib/ast/expand.js +2 -1
  8. package/dist/lib/ast/features/calc.js +19 -11
  9. package/dist/lib/ast/features/index.js +1 -0
  10. package/dist/lib/ast/features/inlinecssvariables.js +47 -29
  11. package/dist/lib/ast/features/prefix.js +117 -91
  12. package/dist/lib/ast/features/shorthand.js +34 -14
  13. package/dist/lib/ast/features/transform.js +67 -0
  14. package/dist/lib/ast/features/type.js +7 -0
  15. package/dist/lib/ast/math/expression.js +20 -10
  16. package/dist/lib/ast/math/math.js +20 -2
  17. package/dist/lib/ast/minify.js +209 -80
  18. package/dist/lib/ast/transform/compute.js +337 -0
  19. package/dist/lib/ast/transform/convert.js +33 -0
  20. package/dist/lib/ast/transform/matrix.js +112 -0
  21. package/dist/lib/ast/transform/minify.js +296 -0
  22. package/dist/lib/ast/transform/perspective.js +10 -0
  23. package/dist/lib/ast/transform/rotate.js +40 -0
  24. package/dist/lib/ast/transform/scale.js +32 -0
  25. package/dist/lib/ast/transform/skew.js +23 -0
  26. package/dist/lib/ast/transform/translate.js +32 -0
  27. package/dist/lib/ast/transform/utils.js +198 -0
  28. package/dist/lib/ast/types.js +18 -15
  29. package/dist/lib/ast/walk.js +54 -22
  30. package/dist/lib/fs/resolve.js +10 -0
  31. package/dist/lib/parser/declaration/list.js +48 -45
  32. package/dist/lib/parser/declaration/map.js +1 -0
  33. package/dist/lib/parser/declaration/set.js +2 -1
  34. package/dist/lib/parser/parse.js +449 -340
  35. package/dist/lib/parser/tokenize.js +147 -72
  36. package/dist/lib/parser/utils/declaration.js +5 -4
  37. package/dist/lib/parser/utils/type.js +2 -1
  38. package/dist/lib/renderer/color/a98rgb.js +2 -1
  39. package/dist/lib/renderer/color/{colormix.js → color-mix.js} +16 -7
  40. package/dist/lib/renderer/color/color.js +264 -170
  41. package/dist/lib/renderer/color/hex.js +19 -8
  42. package/dist/lib/renderer/color/hsl.js +9 -3
  43. package/dist/lib/renderer/color/hwb.js +2 -1
  44. package/dist/lib/renderer/color/lab.js +10 -1
  45. package/dist/lib/renderer/color/lch.js +10 -1
  46. package/dist/lib/renderer/color/oklab.js +10 -1
  47. package/dist/lib/renderer/color/oklch.js +10 -1
  48. package/dist/lib/renderer/color/p3.js +2 -1
  49. package/dist/lib/renderer/color/rec2020.js +2 -1
  50. package/dist/lib/renderer/color/relativecolor.js +27 -32
  51. package/dist/lib/renderer/color/rgb.js +14 -10
  52. package/dist/lib/renderer/color/srgb.js +48 -23
  53. package/dist/lib/renderer/color/utils/components.js +18 -6
  54. package/dist/lib/renderer/color/utils/constants.js +47 -3
  55. package/dist/lib/renderer/color/xyz.js +2 -1
  56. package/dist/lib/renderer/color/xyzd50.js +2 -1
  57. package/dist/lib/renderer/render.js +108 -43
  58. package/dist/lib/syntax/syntax.js +267 -136
  59. package/dist/lib/validation/at-rules/container.js +81 -103
  60. package/dist/lib/validation/at-rules/counter-style.js +9 -8
  61. package/dist/lib/validation/at-rules/custom-media.js +13 -15
  62. package/dist/lib/validation/at-rules/document.js +22 -27
  63. package/dist/lib/validation/at-rules/font-feature-values.js +8 -8
  64. package/dist/lib/validation/at-rules/import.js +30 -81
  65. package/dist/lib/validation/at-rules/keyframes.js +19 -23
  66. package/dist/lib/validation/at-rules/layer.js +5 -5
  67. package/dist/lib/validation/at-rules/media.js +42 -53
  68. package/dist/lib/validation/at-rules/namespace.js +19 -23
  69. package/dist/lib/validation/at-rules/page-margin-box.js +15 -18
  70. package/dist/lib/validation/at-rules/page.js +8 -7
  71. package/dist/lib/validation/at-rules/supports.js +73 -82
  72. package/dist/lib/validation/at-rules/when.js +32 -36
  73. package/dist/lib/validation/atrule.js +15 -18
  74. package/dist/lib/validation/config.js +24 -1
  75. package/dist/lib/validation/config.json.js +563 -63
  76. package/dist/lib/validation/parser/parse.js +196 -185
  77. package/dist/lib/validation/parser/types.js +1 -1
  78. package/dist/lib/validation/selector.js +8 -5
  79. package/dist/lib/validation/syntax.js +724 -1405
  80. package/dist/lib/validation/syntaxes/complex-selector-list.js +10 -11
  81. package/dist/lib/validation/syntaxes/complex-selector.js +10 -11
  82. package/dist/lib/validation/syntaxes/compound-selector.js +40 -50
  83. package/dist/lib/validation/syntaxes/family-name.js +9 -8
  84. package/dist/lib/validation/syntaxes/keyframe-block-list.js +6 -5
  85. package/dist/lib/validation/syntaxes/keyframe-selector.js +23 -105
  86. package/dist/lib/validation/syntaxes/layer-name.js +6 -5
  87. package/dist/lib/validation/syntaxes/relative-selector-list.js +7 -6
  88. package/dist/lib/validation/syntaxes/relative-selector.js +17 -15
  89. package/dist/lib/validation/syntaxes/url.js +18 -22
  90. package/dist/lib/validation/utils/list.js +20 -2
  91. package/dist/lib/validation/utils/whitespace.js +2 -1
  92. package/dist/node/index.js +4 -2
  93. package/dist/node/load.js +6 -1
  94. package/dist/web/index.js +4 -2
  95. package/dist/web/load.js +5 -0
  96. package/package.json +16 -15
  97. package/dist/lib/renderer/color/prophotoRgb.js +0 -56
  98. package/dist/lib/validation/declaration.js +0 -94
  99. package/dist/lib/validation/syntaxes/image.js +0 -29
@@ -1,19 +1,24 @@
1
- import { webkitPseudoAliasMap, isIdentStart, isIdent, mathFuncs, isColor, isHexColor, isPseudo, pseudoElements, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isDimension, parseDimension, isHash, mediaTypes } from '../syntax/syntax.js';
1
+ import { isColor, parseColor, isIdent, mediaTypes, isDimension, parseDimension, isPseudo, pseudoElements, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isHexColor, isHash, isIdentStart, isIdentColor, mathFuncs } from '../syntax/syntax.js';
2
2
  import './utils/config.js';
3
- import { EnumToken, funcLike, ValidationLevel } from '../ast/types.js';
3
+ import { EnumToken, ValidationLevel, SyntaxValidationResult } from '../ast/types.js';
4
4
  import { minify, definedPropertySettings, combinators } from '../ast/minify.js';
5
- import { walkValues, walk } from '../ast/walk.js';
5
+ import { walkValues, walk, WalkerOptionEnum } from '../ast/walk.js';
6
6
  import { expand } from '../ast/expand.js';
7
7
  import { parseDeclarationNode } from './utils/declaration.js';
8
8
  import { renderToken } from '../renderer/render.js';
9
- import { COLORS_NAMES, systemColors, deprecatedSystemColors } from '../renderer/color/utils/constants.js';
9
+ import { funcLike, ColorKind, COLORS_NAMES, systemColors, deprecatedSystemColors, colorsFunc } from '../renderer/color/utils/constants.js';
10
+ import { buildExpression } from '../ast/math/expression.js';
10
11
  import { tokenize } from './tokenize.js';
11
12
  import '../validation/config.js';
12
13
  import '../validation/parser/types.js';
13
14
  import '../validation/parser/parse.js';
14
15
  import { validateSelector } from '../validation/selector.js';
15
16
  import { validateAtRule } from '../validation/atrule.js';
17
+ import { splitTokenList } from '../validation/utils/list.js';
16
18
  import '../validation/syntaxes/complex-selector.js';
19
+ import { validateKeyframeSelector } from '../validation/syntaxes/keyframe-selector.js';
20
+ import { evaluateSyntax } from '../validation/syntax.js';
21
+ import { validateAtRuleKeyframes } from '../validation/at-rules/keyframes.js';
17
22
 
18
23
  const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
19
24
  const trimWhiteSpace = [EnumToken.CommentTokenType, EnumToken.GtTokenType, EnumToken.GteTokenType, EnumToken.LtTokenType, EnumToken.LteTokenType, EnumToken.ColumnCombinatorTokenType];
@@ -48,21 +53,25 @@ async function doParse(iterator, options = {}) {
48
53
  minify: true,
49
54
  pass: 1,
50
55
  parseColor: true,
51
- nestingRules: false,
56
+ nestingRules: true,
52
57
  resolveImport: false,
53
58
  resolveUrls: false,
54
59
  removeCharset: true,
55
60
  removeEmpty: true,
56
61
  removeDuplicateDeclarations: true,
62
+ computeTransform: true,
57
63
  computeShorthand: true,
58
64
  computeCalcExpression: true,
59
65
  inlineCssVariables: false,
60
66
  setParent: true,
61
67
  removePrefix: false,
62
- validation: true,
68
+ validation: ValidationLevel.Default,
63
69
  lenient: true,
64
70
  ...options
65
71
  };
72
+ if (typeof options.validation == 'boolean') {
73
+ options.validation = options.validation ? ValidationLevel.All : ValidationLevel.None;
74
+ }
66
75
  if (options.expandNestingRules) {
67
76
  options.nestingRules = false;
68
77
  }
@@ -94,16 +103,22 @@ async function doParse(iterator, options = {}) {
94
103
  lin: 1,
95
104
  col: 1
96
105
  },
106
+ end: {
107
+ ind: 0,
108
+ lin: 1,
109
+ col: 1
110
+ },
97
111
  src: ''
98
112
  };
99
113
  }
100
114
  const iter = tokenize(iterator);
101
115
  let item;
116
+ let node;
102
117
  const rawTokens = [];
118
+ const imports = [];
103
119
  while (item = iter.next().value) {
104
120
  stats.bytesIn = item.bytesIn;
105
121
  rawTokens.push(item);
106
- // doParse error
107
122
  if (item.hint != null && BadTokensTypes.includes(item.hint)) {
108
123
  // bad token
109
124
  continue;
@@ -111,22 +126,33 @@ async function doParse(iterator, options = {}) {
111
126
  if (item.hint != EnumToken.EOFTokenType) {
112
127
  tokens.push(item);
113
128
  }
129
+ else if (ast.loc != null) {
130
+ for (let i = stack.length - 1; i >= 0; i--) {
131
+ stack[i].loc.end = { ...item.end };
132
+ }
133
+ ast.loc.end = item.end;
134
+ }
114
135
  if (item.token == ';' || item.token == '{') {
115
- let node = await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
136
+ node = parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
116
137
  rawTokens.length = 0;
117
138
  if (node != null) {
118
- // @ts-ignore
119
- stack.push(node);
120
- // @ts-ignore
121
- context = node;
139
+ if ('chi' in node) {
140
+ stack.push(node);
141
+ context = node;
142
+ }
143
+ else if (node.typ == EnumToken.AtRuleNodeType && node.nam == 'import') {
144
+ imports.push(node);
145
+ }
122
146
  }
123
147
  else if (item.token == '{') {
124
148
  let inBlock = 1;
149
+ tokens = [item];
125
150
  do {
126
151
  item = iter.next().value;
127
152
  if (item == null) {
128
153
  break;
129
154
  }
155
+ tokens.push(item);
130
156
  if (item.token == '{') {
131
157
  inBlock++;
132
158
  }
@@ -134,28 +160,32 @@ async function doParse(iterator, options = {}) {
134
160
  inBlock--;
135
161
  }
136
162
  } while (inBlock != 0);
163
+ if (tokens.length > 0) {
164
+ errors.push({
165
+ action: 'drop',
166
+ message: 'invalid block',
167
+ rawTokens: tokens.slice()
168
+ });
169
+ }
137
170
  }
138
171
  tokens = [];
139
172
  map = new Map;
140
173
  }
141
174
  else if (item.token == '}') {
142
- await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
175
+ parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
143
176
  rawTokens.length = 0;
177
+ if (context.loc != null) {
178
+ context.loc.end = item.end;
179
+ }
144
180
  const previousNode = stack.pop();
145
- // @ts-ignore
146
- context = stack[stack.length - 1] ?? ast;
147
- // @ts-ignore
181
+ context = (stack[stack.length - 1] ?? ast);
148
182
  if (previousNode != null && previousNode.typ == EnumToken.InvalidRuleTokenType) {
149
- // @ts-ignore
150
183
  const index = context.chi.findIndex(node => node == previousNode);
151
184
  if (index > -1) {
152
- // @ts-ignore
153
185
  context.chi.splice(index, 1);
154
186
  }
155
187
  }
156
- // @ts-ignore
157
188
  if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
158
- // @ts-ignore
159
189
  context.chi.pop();
160
190
  }
161
191
  tokens = [];
@@ -163,23 +193,56 @@ async function doParse(iterator, options = {}) {
163
193
  }
164
194
  }
165
195
  if (tokens.length > 0) {
166
- await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
196
+ node = parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
167
197
  rawTokens.length = 0;
198
+ if (node != null) {
199
+ if (node.typ == EnumToken.AtRuleNodeType && node.nam == 'import') {
200
+ imports.push(node);
201
+ }
202
+ else if ('chi' in node && node.typ != EnumToken.InvalidRuleTokenType) {
203
+ stack.push(node);
204
+ context = node;
205
+ }
206
+ }
168
207
  if (context != null && context.typ == EnumToken.InvalidRuleTokenType) {
208
+ // @ts-ignore
169
209
  const index = context.chi.findIndex((node) => node == context);
170
210
  if (index > -1) {
171
211
  context.chi.splice(index, 1);
172
212
  }
173
213
  }
174
214
  }
215
+ if (imports.length > 0 && options.resolveImport) {
216
+ await Promise.all(imports.map(async (node) => {
217
+ const token = node.tokens[0];
218
+ const url = token.typ == EnumToken.StringTokenType ? token.val.slice(1, -1) : token.val;
219
+ try {
220
+ const root = await options.load(url, options.src).then((src) => {
221
+ // console.error({url, src: options.src, resolved: options.resolve!(url, options.src as string)})
222
+ return doParse(src, Object.assign({}, options, {
223
+ minify: false,
224
+ setParent: false,
225
+ src: options.resolve(url, options.src).absolute
226
+ }));
227
+ });
228
+ stats.importedBytesIn += root.stats.bytesIn;
229
+ node.parent.chi.splice(node.parent.chi.indexOf(node), 1, ...root.ast.chi);
230
+ if (root.errors.length > 0) {
231
+ errors.push(...root.errors);
232
+ }
233
+ }
234
+ catch (error) {
235
+ // console.error(error);
236
+ // @ts-ignore
237
+ errors.push({ action: 'ignore', message: 'doParse: ' + error.message, error });
238
+ }
239
+ }));
240
+ }
175
241
  while (stack.length > 0 && context != ast) {
176
242
  const previousNode = stack.pop();
177
- // @ts-ignore
178
- context = stack[stack.length - 1] ?? ast;
243
+ context = (stack[stack.length - 1] ?? ast);
179
244
  // remove empty nodes
180
- // @ts-ignore
181
245
  if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
182
- // @ts-ignore
183
246
  context.chi.pop();
184
247
  continue;
185
248
  }
@@ -192,14 +255,12 @@ async function doParse(iterator, options = {}) {
192
255
  if (options.visitor != null) {
193
256
  for (const result of walk(ast)) {
194
257
  if (result.node.typ == EnumToken.DeclarationNodeType &&
195
- // @ts-ignore
196
258
  (typeof options.visitor.Declaration == 'function' || options.visitor.Declaration?.[result.node.nam] != null)) {
197
259
  const callable = typeof options.visitor.Declaration == 'function' ? options.visitor.Declaration : options.visitor.Declaration[result.node.nam];
198
260
  const results = await callable(result.node);
199
261
  if (results == null || (Array.isArray(results) && results.length == 0)) {
200
262
  continue;
201
263
  }
202
- // @ts-ignore
203
264
  result.parent.chi.splice(result.parent.chi.indexOf(result.node), 1, ...(Array.isArray(results) ? results : [results]));
204
265
  }
205
266
  else if (options.visitor.Rule != null && result.node.typ == EnumToken.RuleNodeType) {
@@ -207,7 +268,6 @@ async function doParse(iterator, options = {}) {
207
268
  if (results == null || (Array.isArray(results) && results.length == 0)) {
208
269
  continue;
209
270
  }
210
- // @ts-ignore
211
271
  result.parent.chi.splice(result.parent.chi.indexOf(result.node), 1, ...(Array.isArray(results) ? results : [results]));
212
272
  }
213
273
  else if (options.visitor.AtRule != null &&
@@ -219,7 +279,6 @@ async function doParse(iterator, options = {}) {
219
279
  if (results == null || (Array.isArray(results) && results.length == 0)) {
220
280
  continue;
221
281
  }
222
- // @ts-ignore
223
282
  result.parent.chi.splice(result.parent.chi.indexOf(result.node), 1, ...(Array.isArray(results) ? results : [results]));
224
283
  }
225
284
  }
@@ -258,30 +317,27 @@ function getLastNode(context) {
258
317
  }
259
318
  return null;
260
319
  }
261
- async function parseNode(results, context, stats, options, errors, src, map, rawTokens) {
320
+ function parseNode(results, context, stats, options, errors, src, map, rawTokens) {
262
321
  let tokens = [];
263
322
  for (const t of results) {
264
323
  const node = getTokenType(t.token, t.hint);
265
- map.set(node, t.position);
324
+ map.set(node, { sta: t.sta, end: t.end, src });
266
325
  tokens.push(node);
267
326
  }
268
327
  let i;
269
328
  let loc;
270
329
  for (i = 0; i < tokens.length; i++) {
271
330
  if (tokens[i].typ == EnumToken.CommentTokenType || tokens[i].typ == EnumToken.CDOCOMMTokenType) {
272
- const position = map.get(tokens[i]);
331
+ const location = map.get(tokens[i]);
273
332
  if (tokens[i].typ == EnumToken.CDOCOMMTokenType && context.typ != EnumToken.StyleSheetNodeType) {
274
333
  errors.push({
275
334
  action: 'drop',
276
335
  message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`,
277
- location: { src, ...position }
336
+ location
278
337
  });
279
338
  continue;
280
339
  }
281
- loc = {
282
- sta: position,
283
- src
284
- };
340
+ loc = location;
285
341
  // @ts-ignore
286
342
  context.chi.push(tokens[i]);
287
343
  if (options.sourcemap) {
@@ -300,10 +356,6 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
300
356
  if (delim.typ == EnumToken.SemiColonTokenType || delim.typ == EnumToken.BlockStartTokenType || delim.typ == EnumToken.BlockEndTokenType) {
301
357
  tokens.pop();
302
358
  }
303
- else {
304
- delim = { typ: EnumToken.SemiColonTokenType };
305
- }
306
- // @ts-ignore
307
359
  while ([EnumToken.WhitespaceTokenType, EnumToken.BadStringTokenType, EnumToken.BadCommentTokenType].includes(tokens.at(-1)?.typ)) {
308
360
  tokens.pop();
309
361
  }
@@ -312,11 +364,12 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
312
364
  }
313
365
  if (tokens[0]?.typ == EnumToken.AtRuleTokenType) {
314
366
  const atRule = tokens.shift();
315
- const position = map.get(atRule);
367
+ const location = map.get(atRule);
316
368
  // @ts-ignore
317
369
  while ([EnumToken.WhitespaceTokenType].includes(tokens[0]?.typ)) {
318
370
  tokens.shift();
319
371
  }
372
+ rawTokens.shift();
320
373
  if (atRule.val == 'import') {
321
374
  // only @charset and @layer are accepted before @import
322
375
  // @ts-ignore
@@ -334,14 +387,14 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
334
387
  if (!(type == EnumToken.InvalidAtRuleTokenType &&
335
388
  // @ts-ignore
336
389
  ['charset', 'layer', 'import'].includes(context.chi[i].nam))) {
337
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
390
+ errors.push({ action: 'drop', message: 'invalid @import', location });
338
391
  return null;
339
392
  }
340
393
  }
341
394
  // @ts-ignore
342
395
  const name = context.chi[i].nam;
343
396
  if (name != 'charset' && name != 'import' && name != 'layer') {
344
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
397
+ errors.push({ action: 'drop', message: 'invalid @import', location });
345
398
  return null;
346
399
  }
347
400
  break;
@@ -352,7 +405,7 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
352
405
  errors.push({
353
406
  action: 'drop',
354
407
  message: 'doParse: invalid @import',
355
- location: { src, ...position }
408
+ location
356
409
  });
357
410
  return null;
358
411
  }
@@ -361,7 +414,7 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
361
414
  errors.push({
362
415
  action: 'drop',
363
416
  message: 'doParse: invalid @import',
364
- location: { src, ...position }
417
+ location
365
418
  });
366
419
  return null;
367
420
  }
@@ -387,37 +440,6 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
387
440
  }
388
441
  }
389
442
  }
390
- // @ts-ignore
391
- if (tokens[0].typ == EnumToken.StringTokenType) {
392
- if (options.resolveImport) {
393
- const url = tokens[0].val.slice(1, -1);
394
- try {
395
- // @ts-ignore
396
- const root = await options.load(url, options.src).then((src) => {
397
- return doParse(src, Object.assign({}, options, {
398
- minify: false,
399
- setParent: false,
400
- // @ts-ignore
401
- src: options.resolve(url, options.src).absolute
402
- }));
403
- });
404
- stats.importedBytesIn += root.stats.bytesIn;
405
- if (root.ast.chi.length > 0) {
406
- // @todo - filter charset, layer and scope
407
- // @ts-ignore
408
- context.chi.push(...root.ast.chi);
409
- }
410
- if (root.errors.length > 0) {
411
- errors.push(...root.errors);
412
- }
413
- return null;
414
- }
415
- catch (error) {
416
- // @ts-ignore
417
- errors.push({ action: 'ignore', message: 'doParse: ' + error.message, error });
418
- }
419
- }
420
- }
421
443
  }
422
444
  // https://www.w3.org/TR/css-nesting-1/#conditionals
423
445
  // allowed nesting at-rules
@@ -425,7 +447,7 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
425
447
  if (atRule.val == 'charset') {
426
448
  let spaces = 0;
427
449
  // https://developer.mozilla.org/en-US/docs/Web/CSS/@charset
428
- for (let k = 1; k < rawTokens.length; k++) {
450
+ for (let k = 0; k < rawTokens.length; k++) {
429
451
  if (rawTokens[k].hint == EnumToken.WhitespaceTokenType) {
430
452
  spaces += rawTokens[k].len;
431
453
  continue;
@@ -441,7 +463,7 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
441
463
  action: 'drop',
442
464
  message: '@charset must have only one space',
443
465
  // @ts-ignore
444
- location: { src, ...(map.get(atRule) ?? position) }
466
+ location
445
467
  });
446
468
  return null;
447
469
  }
@@ -449,8 +471,7 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
449
471
  errors.push({
450
472
  action: 'drop',
451
473
  message: '@charset expects a "<charset>"',
452
- // @ts-ignore
453
- location: { src, ...(map.get(atRule) ?? position) }
474
+ location
454
475
  });
455
476
  return null;
456
477
  }
@@ -465,134 +486,127 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
465
486
  acc.push(renderToken(curr, { removeComments: true }));
466
487
  return acc;
467
488
  }, []);
489
+ const nam = renderToken(atRule, { removeComments: true });
490
+ // @ts-ignore
468
491
  const node = {
469
- typ: EnumToken.AtRuleNodeType,
470
- nam: renderToken(atRule, { removeComments: true }),
471
- // tokens: t,
492
+ typ: /^(-[a-z]+-)?keyframes$/.test(nam) ? EnumToken.KeyframeAtRuleNodeType : EnumToken.AtRuleNodeType,
493
+ nam,
472
494
  val: raw.join('')
473
495
  };
474
496
  Object.defineProperties(node, {
475
- tokens: { ...definedPropertySettings, enumerable: false, value: tokens.slice() },
497
+ tokens: { ...definedPropertySettings, enumerable: false, value: t.slice() },
476
498
  raw: { ...definedPropertySettings, value: raw }
477
499
  });
478
500
  if (delim.typ == EnumToken.BlockStartTokenType) {
479
501
  node.chi = [];
480
502
  }
481
- loc = {
482
- sta: position,
483
- src
484
- };
503
+ loc = map.get(atRule);
485
504
  if (options.sourcemap) {
486
505
  node.loc = loc;
506
+ node.loc.end = { ...map.get(delim).end };
487
507
  }
488
- if (options.validation) {
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;
508
+ // if (options.validation) {
509
+ let isValid = true;
510
+ if (node.nam == 'else') {
511
+ const prev = getLastNode(context);
512
+ if (prev != null && prev.typ == EnumToken.AtRuleNodeType && ['when', 'else'].includes(prev.nam)) {
513
+ if (prev.nam == 'else') {
514
+ isValid = Array.isArray(prev.tokens) && prev.tokens.length > 0;
499
515
  }
500
516
  }
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'};
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
- });
513
- // @ts-ignore
514
- node.typ = EnumToken.InvalidAtRuleTokenType;
515
- }
516
517
  else {
517
- node.val = node.tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false, removeComments: true }), '');
518
+ isValid = false;
518
519
  }
519
520
  }
520
521
  // @ts-ignore
522
+ const valid = options.validation == ValidationLevel.None ? {
523
+ valid: SyntaxValidationResult.Valid,
524
+ error: '',
525
+ node,
526
+ syntax: '@' + node.nam
527
+ } : isValid ? (node.typ == EnumToken.KeyframeAtRuleNodeType ? validateAtRuleKeyframes(node) : validateAtRule(node, options, context)) : {
528
+ valid: SyntaxValidationResult.Drop,
529
+ node,
530
+ syntax: '@' + node.nam,
531
+ error: '@' + node.nam + ' not allowed here'};
532
+ if (valid.valid == SyntaxValidationResult.Drop) {
533
+ errors.push({
534
+ action: 'drop',
535
+ message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
536
+ // @ts-ignore
537
+ location: { src, ...(map.get(valid.node) ?? location) }
538
+ });
539
+ // @ts-ignore
540
+ node.typ = EnumToken.InvalidAtRuleTokenType;
541
+ }
542
+ else {
543
+ node.val = node.tokens.reduce((acc, curr) => acc + renderToken(curr, {
544
+ minify: false,
545
+ removeComments: true
546
+ }), '');
547
+ }
548
+ // }
521
549
  context.chi.push(node);
522
- Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: context });
523
- return delim.typ == EnumToken.BlockStartTokenType ? node : null;
550
+ Object.defineProperties(node, { parent: { ...definedPropertySettings, value: context }, validSyntax: { ...definedPropertySettings, value: valid.valid == SyntaxValidationResult.Valid } });
551
+ return node;
524
552
  }
525
553
  else {
526
554
  // rule
527
555
  if (delim.typ == EnumToken.BlockStartTokenType) {
528
- const position = map.get(tokens[0]);
556
+ const location = map.get(tokens[0]);
529
557
  const uniq = new Map;
530
- parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
531
- if (curr.typ == EnumToken.CommentTokenType) {
532
- return acc;
533
- }
534
- if (curr.typ == EnumToken.WhitespaceTokenType) {
535
- if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
536
- trimWhiteSpace.includes(array[index + 1]?.typ) ||
537
- combinators.includes(array[index - 1]?.val) ||
538
- combinators.includes(array[index + 1]?.val)) {
539
- return acc;
540
- }
541
- }
542
- let t = renderToken(curr, { minify: false });
543
- if (t == ',') {
544
- acc.push([]);
545
- // uniqTokens.push([]);
546
- }
547
- else {
548
- acc[acc.length - 1].push(t);
549
- // uniqTokens[uniqTokens.length - 1].push(curr);
550
- }
551
- return acc;
552
- }, [[]]).reduce((acc, curr) => {
553
- let i = 0;
554
- for (; i < curr.length; i++) {
555
- if (i + 1 < curr.length && curr[i] == '*') {
556
- if (curr[i] == '*') {
557
- let index = curr[i + 1] == ' ' ? 2 : 1;
558
- if (!['>', '~', '+'].includes(curr[index])) {
559
- curr.splice(i, index);
560
- }
561
- }
562
- }
563
- }
564
- acc.set(curr.join(''), curr);
565
- return acc;
566
- }, uniq);
567
- const ruleType = context.typ == EnumToken.AtRuleNodeType && context.nam == 'keyframes' ? EnumToken.KeyFrameRuleNodeType : EnumToken.RuleNodeType;
558
+ parseTokens(tokens, { minify: true });
559
+ const ruleType = context.typ == EnumToken.KeyframeAtRuleNodeType ? EnumToken.KeyFrameRuleNodeType : EnumToken.RuleNodeType;
568
560
  if (ruleType == EnumToken.RuleNodeType) {
569
561
  parseSelector(tokens);
570
- if (options.validation) {
571
- // @ts-ignore
572
- const valid = validateSelector(tokens, options, context);
573
- if (valid.valid != ValidationLevel.Valid) {
574
- const node = {
575
- typ: EnumToken.InvalidRuleTokenType,
576
- // @ts-ignore
577
- sel: tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), ''),
578
- chi: []
579
- };
580
- errors.push({
581
- action: 'drop',
582
- message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
583
- // @ts-ignore
584
- location: { src, ...(map.get(valid.node) ?? position) }
585
- });
586
- // @ts-ignore
587
- context.chi.push(node);
588
- Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: context });
589
- return node;
590
- }
591
- }
592
562
  }
593
563
  const node = {
594
564
  typ: ruleType,
595
- sel: [...uniq.keys()].join(','),
565
+ sel: [...tokens.reduce((acc, curr, index, array) => {
566
+ if (curr.typ == EnumToken.CommentTokenType) {
567
+ return acc;
568
+ }
569
+ if (curr.typ == EnumToken.WhitespaceTokenType) {
570
+ if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
571
+ trimWhiteSpace.includes(array[index + 1]?.typ) ||
572
+ combinators.includes(array[index - 1]?.val) ||
573
+ combinators.includes(array[index + 1]?.val)) {
574
+ return acc;
575
+ }
576
+ }
577
+ if (ruleType == EnumToken.KeyFrameRuleNodeType) {
578
+ if (curr.typ == EnumToken.IdenTokenType && curr.val == 'from') {
579
+ Object.assign(curr, { typ: EnumToken.PercentageTokenType, val: '0' });
580
+ }
581
+ else if (curr.typ == EnumToken.PercentageTokenType && curr.val == '100') {
582
+ Object.assign(curr, { typ: EnumToken.IdenTokenType, val: 'to' });
583
+ }
584
+ }
585
+ let t = renderToken(curr, { minify: false });
586
+ if (t == ',') {
587
+ acc.push([]);
588
+ // uniqTokens.push([]);
589
+ }
590
+ else {
591
+ acc[acc.length - 1].push(t);
592
+ // uniqTokens[uniqTokens.length - 1].push(curr);
593
+ }
594
+ return acc;
595
+ }, [[]]).reduce((acc, curr) => {
596
+ let i = 0;
597
+ for (; i < curr.length; i++) {
598
+ if (i + 1 < curr.length && curr[i] == '*') {
599
+ if (curr[i] == '*') {
600
+ let index = curr[i + 1] == ' ' ? 2 : 1;
601
+ if (!['>', '~', '+'].includes(curr[index])) {
602
+ curr.splice(i, index);
603
+ }
604
+ }
605
+ }
606
+ }
607
+ acc.set(curr.join(''), curr);
608
+ return acc;
609
+ }, uniq).keys()].join(','),
596
610
  chi: []
597
611
  };
598
612
  Object.defineProperty(node, 'tokens', {
@@ -600,121 +614,203 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
600
614
  enumerable: false,
601
615
  value: tokens.slice()
602
616
  });
603
- let raw = [...uniq.values()];
604
- Object.defineProperty(node, 'raw', {
605
- enumerable: false,
606
- configurable: true,
607
- writable: true,
608
- value: raw
609
- });
610
- loc = {
611
- sta: position,
612
- src
613
- };
617
+ loc = location;
614
618
  if (options.sourcemap) {
615
619
  node.loc = loc;
616
620
  }
617
621
  // @ts-ignore
618
622
  context.chi.push(node);
619
623
  Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: context });
624
+ // if (options.validation) {
625
+ // @ts-ignore
626
+ const valid = options.validation == ValidationLevel.None ? {
627
+ valid: SyntaxValidationResult.Valid,
628
+ error: null
629
+ } : ruleType == EnumToken.KeyFrameRuleNodeType ? validateKeyframeSelector(tokens) : validateSelector(tokens, options, context);
630
+ if (valid.valid != SyntaxValidationResult.Valid) {
631
+ // @ts-ignore
632
+ node.typ = EnumToken.InvalidRuleTokenType;
633
+ node.sel = tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '');
634
+ errors.push({
635
+ action: 'drop',
636
+ message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
637
+ // @ts-ignore
638
+ location
639
+ });
640
+ }
641
+ // } else {
642
+ //
643
+ // Object.defineProperty(node, 'tokens', {
644
+ // ...definedPropertySettings,
645
+ // enumerable: false,
646
+ // value: tokens.slice()
647
+ // });
648
+ //
649
+ // let raw: string[][] = [...uniq.values()];
650
+ //
651
+ // Object.defineProperty(node, 'raw', {
652
+ // enumerable: false,
653
+ // configurable: true,
654
+ // writable: true,
655
+ // value: raw
656
+ // });
657
+ // }
658
+ Object.defineProperty(node, 'validSyntax', { ...definedPropertySettings, value: valid.valid == SyntaxValidationResult.Valid });
620
659
  return node;
621
660
  }
622
661
  else {
623
662
  let name = null;
624
663
  let value = null;
625
- for (let i = 0; i < tokens.length; i++) {
664
+ let i = 0;
665
+ for (; i < tokens.length; i++) {
666
+ if (tokens[i].typ == EnumToken.LiteralTokenType && tokens[i].val.length > 1) {
667
+ const start = tokens[i].val.charAt(0);
668
+ const val = tokens[i].val.slice(1);
669
+ if (['/', '*'].includes(start) && isNumber(val)) {
670
+ tokens.splice(i, 1, {
671
+ typ: EnumToken.LiteralTokenType,
672
+ val: tokens[i].val.charAt(0)
673
+ }, {
674
+ typ: EnumToken.NumberTokenType,
675
+ val: tokens[i].val.slice(1)
676
+ });
677
+ }
678
+ else if (start == '/' && isFunction(val)) {
679
+ tokens.splice(i, 1, { typ: EnumToken.LiteralTokenType, val: '/' }, getTokenType(val));
680
+ }
681
+ }
682
+ }
683
+ parseTokens(tokens, { ...options, parseColor: true });
684
+ for (i = 0; i < tokens.length; i++) {
626
685
  if (tokens[i].typ == EnumToken.CommentTokenType) {
627
686
  continue;
628
687
  }
629
688
  if (name == null && [EnumToken.IdenTokenType, EnumToken.DashedIdenTokenType].includes(tokens[i].typ)) {
630
689
  name = tokens.slice(0, i + 1);
631
690
  }
691
+ else if (name == null && tokens[i].typ == EnumToken.ColorTokenType && [ColorKind.SYS, ColorKind.DPSYS].includes(tokens[i].kin)) {
692
+ name = tokens.slice(0, i + 1);
693
+ tokens[i].typ = EnumToken.IdenTokenType;
694
+ }
632
695
  else if (name != null && funcLike.concat([
633
696
  EnumToken.LiteralTokenType,
634
697
  EnumToken.IdenTokenType, EnumToken.DashedIdenTokenType,
635
698
  EnumToken.PseudoClassTokenType, EnumToken.PseudoClassFuncTokenType
636
699
  ]).includes(tokens[i].typ)) {
637
- if (tokens[i].val.charAt(0) == ':') {
700
+ if (tokens[i].val?.charAt?.(0) == ':') {
638
701
  Object.assign(tokens[i], getTokenType(tokens[i].val.slice(1)));
702
+ if ('chi' in tokens[i]) {
703
+ tokens[i].typ = EnumToken.FunctionTokenType;
704
+ if (colorsFunc.includes(tokens[i].val) && isColor(tokens[i])) {
705
+ parseColor(tokens[i]);
706
+ }
707
+ }
708
+ tokens.splice(i, 0, { typ: EnumToken.ColonTokenType });
709
+ // i++;
710
+ i--;
711
+ continue;
639
712
  }
640
713
  if ('chi' in tokens[i]) {
641
714
  tokens[i].typ = EnumToken.FunctionTokenType;
642
715
  }
643
- value = parseTokens(tokens.slice(i), {
644
- parseColor: options.parseColor,
645
- src: options.src,
646
- resolveUrls: options.resolveUrls,
647
- resolve: options.resolve,
648
- cwd: options.cwd
649
- });
650
- break;
716
+ value = tokens.slice(i);
651
717
  }
652
718
  if (tokens[i].typ == EnumToken.ColonTokenType) {
653
719
  name = tokens.slice(0, i);
654
- value = parseTokens(tokens.slice(i + 1), {
655
- parseColor: options.parseColor,
656
- src: options.src,
657
- resolveUrls: options.resolveUrls,
658
- resolve: options.resolve,
659
- cwd: options.cwd
660
- });
720
+ value = tokens.slice(i + 1);
661
721
  break;
662
722
  }
663
723
  }
664
724
  if (name == null) {
665
725
  name = tokens;
666
726
  }
667
- const position = map.get(name[0]);
727
+ const location = map.get(name[0]);
668
728
  if (name.length > 0) {
669
729
  for (let i = 1; i < name.length; i++) {
670
730
  if (name[i].typ != EnumToken.WhitespaceTokenType && name[i].typ != EnumToken.CommentTokenType) {
671
731
  errors.push({
672
732
  action: 'drop',
673
733
  message: 'doParse: invalid declaration',
674
- location: { src, ...position }
734
+ location
675
735
  });
676
736
  return null;
677
737
  }
678
738
  }
679
739
  }
680
- if (value == null || value.length == 0) {
740
+ const nam = renderToken(name.shift(), { removeComments: true });
741
+ if (value == null || (!nam.startsWith('--') && value.length == 0)) {
681
742
  errors.push({
682
743
  action: 'drop',
683
744
  message: 'doParse: invalid declaration',
684
- location: { src, ...position }
745
+ location
685
746
  });
747
+ if (options.lenient) {
748
+ const node = {
749
+ typ: EnumToken.InvalidDeclarationNodeType,
750
+ nam,
751
+ val: []
752
+ };
753
+ if (options.sourcemap) {
754
+ node.loc = location;
755
+ node.loc.end = { ...map.get(delim).end };
756
+ }
757
+ context.chi.push(node);
758
+ }
686
759
  return null;
687
760
  }
761
+ for (const { value: token } of walkValues(value, null, {
762
+ fn: (node) => node.typ == EnumToken.FunctionTokenType && node.val == 'calc' ? WalkerOptionEnum.IgnoreChildren : null,
763
+ type: EnumToken.FunctionTokenType
764
+ })) {
765
+ if (token.typ == EnumToken.FunctionTokenType && token.val == 'calc') {
766
+ for (const { value: node, parent } of walkValues(token.chi, token)) {
767
+ // fix expressions starting with '/' or '*' such as '/4' in (1 + 1)/4
768
+ if (node.typ == EnumToken.LiteralTokenType && node.val.length > 0) {
769
+ if (node.val[0] == '/' || node.val[0] == '*') {
770
+ parent.chi.splice(parent.chi.indexOf(node), 1, { typ: node.val[0] == '/' ? EnumToken.Div : EnumToken.Mul }, ...parseString(node.val.slice(1)));
771
+ }
772
+ }
773
+ }
774
+ }
775
+ }
688
776
  const node = {
689
777
  typ: EnumToken.DeclarationNodeType,
690
- // @ts-ignore
691
- nam: renderToken(name.shift(), { removeComments: true }),
692
- // @ts-ignore
778
+ nam,
693
779
  val: value
694
780
  };
695
- const result = parseDeclarationNode(node, errors, src, position);
696
- if (result != null) {
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
- // }
781
+ if (options.sourcemap) {
782
+ node.loc = location;
783
+ node.loc.end = { ...map.get(delim).end };
784
+ }
785
+ // do not allow declarations in style sheets
786
+ if (context.typ == EnumToken.StyleSheetNodeType && options.lenient) {
715
787
  // @ts-ignore
788
+ node.typ = EnumToken.InvalidDeclarationNodeType;
789
+ context.chi.push(node);
790
+ return null;
791
+ }
792
+ const result = parseDeclarationNode(node, errors, location);
793
+ Object.defineProperty(result, 'parent', { ...definedPropertySettings, value: context });
794
+ if (result != null) {
795
+ // console.error(doRender(result), result.val, location);
796
+ if (options.validation == ValidationLevel.All) {
797
+ const valid = evaluateSyntax(result, options);
798
+ Object.defineProperty(result, 'validSyntax', { ...definedPropertySettings, value: valid.valid == SyntaxValidationResult.Valid });
799
+ if (valid.valid == SyntaxValidationResult.Drop) {
800
+ errors.push({
801
+ action: 'drop',
802
+ message: valid.error,
803
+ syntax: valid.syntax,
804
+ location: map.get(valid.node) ?? valid.node?.loc ?? result.loc ?? location
805
+ });
806
+ if (!options.lenient) {
807
+ return null;
808
+ }
809
+ // @ts-ignore
810
+ node.typ = EnumToken.InvalidDeclarationNodeType;
811
+ }
812
+ }
716
813
  context.chi.push(result);
717
- Object.defineProperty(result, 'parent', { ...definedPropertySettings, value: context });
718
814
  }
719
815
  return null;
720
816
  }
@@ -733,6 +829,33 @@ function parseAtRulePrelude(tokens, atRule) {
733
829
  value.typ == EnumToken.CommaTokenType) {
734
830
  continue;
735
831
  }
832
+ if (value.typ == EnumToken.PseudoClassFuncTokenType || value.typ == EnumToken.PseudoClassTokenType) {
833
+ if (parent?.typ == EnumToken.ParensTokenType) {
834
+ const index = parent.chi.indexOf(value);
835
+ let i = index;
836
+ while (i--) {
837
+ if (parent.chi[i].typ == EnumToken.IdenTokenType || parent.chi[i].typ == EnumToken.DashedIdenTokenType) {
838
+ break;
839
+ }
840
+ }
841
+ if (i >= 0) {
842
+ const token = getTokenType(parent.chi[index].val.slice(1) + (funcLike.includes(parent.chi[index].typ) ? '(' : ''));
843
+ parent.chi[index].val = token.val;
844
+ parent.chi[index].typ = token.typ;
845
+ if (parent.chi[index].typ == EnumToken.FunctionTokenType && isColor(parent.chi[index])) {
846
+ parseColor(parent.chi[index]);
847
+ }
848
+ parent.chi.splice(i, index - i + 1, {
849
+ typ: EnumToken.MediaQueryConditionTokenType,
850
+ l: parent.chi[i],
851
+ r: parent.chi.slice(index),
852
+ op: {
853
+ typ: EnumToken.ColonTokenType
854
+ }
855
+ });
856
+ }
857
+ }
858
+ }
736
859
  if (atRule.val == 'page' && value.typ == EnumToken.PseudoClassTokenType) {
737
860
  if ([':left', ':right', ':first', ':blank'].includes(value.val)) {
738
861
  // @ts-ignore
@@ -793,9 +916,10 @@ function parseAtRulePrelude(tokens, atRule) {
793
916
  continue;
794
917
  }
795
918
  }
919
+ if (value.typ == EnumToken.FunctionTokenType && value.val == 'selector') {
920
+ parseSelector(value.chi);
921
+ }
796
922
  if (value.typ == EnumToken.ParensTokenType || (value.typ == EnumToken.FunctionTokenType && ['media', 'supports', 'style', 'scroll-state'].includes(value.val))) {
797
- // @todo parse range and declarations
798
- // parseDeclaration(parent.chi);
799
923
  let i;
800
924
  let nameIndex = -1;
801
925
  let valueIndex = -1;
@@ -816,6 +940,23 @@ function parseAtRulePrelude(tokens, atRule) {
816
940
  if (value.chi[i].typ == EnumToken.CommentTokenType || value.chi[i].typ == EnumToken.WhitespaceTokenType) {
817
941
  continue;
818
942
  }
943
+ if (value.chi[i].typ == EnumToken.LiteralTokenType && value.chi[i].val.startsWith(':') && isDimension(value.chi[i].val.slice(1))) {
944
+ value.chi.splice(i, 1, {
945
+ typ: EnumToken.ColonTokenType,
946
+ }, Object.assign(value.chi[i], parseDimension(value.chi[i].val.slice(1))));
947
+ i--;
948
+ continue;
949
+ }
950
+ if (nameIndex != -1 && value.chi[i].typ == EnumToken.PseudoClassTokenType) {
951
+ value.chi.splice(i, 1, {
952
+ typ: EnumToken.ColonTokenType,
953
+ }, Object.assign(value.chi[i], {
954
+ typ: EnumToken.IdenTokenType,
955
+ val: value.chi[i].val.slice(1)
956
+ }));
957
+ i--;
958
+ continue;
959
+ }
819
960
  valueIndex = i;
820
961
  break;
821
962
  }
@@ -835,7 +976,7 @@ function parseAtRulePrelude(tokens, atRule) {
835
976
  const node = value.chi.splice(nameIndex, 1)[0];
836
977
  // 'background'
837
978
  // @ts-ignore
838
- if (node.typ == EnumToken.ColorTokenType && node.kin == 'dpsys') {
979
+ if (node.typ == EnumToken.ColorTokenType && node.kin == ColorKind.DPSYS) {
839
980
  // @ts-ignore
840
981
  delete node.kin;
841
982
  node.typ = EnumToken.IdenTokenType;
@@ -959,8 +1100,8 @@ function parseSelector(tokens) {
959
1100
  }
960
1101
  }
961
1102
  else if (value.typ == EnumToken.ColorTokenType) {
962
- if (value.kin == 'lit' || value.kin == 'hex' || value.kin == 'sys' || value.kin == 'dpsys') {
963
- if (value.kin == 'hex') {
1103
+ if (value.kin == ColorKind.LIT || value.kin == ColorKind.HEX || value.kin == ColorKind.SYS || value.kin == ColorKind.DPSYS) {
1104
+ if (value.kin == ColorKind.HEX) {
964
1105
  if (!isIdent(value.val.slice(1))) {
965
1106
  continue;
966
1107
  }
@@ -1005,64 +1146,54 @@ function parseSelector(tokens) {
1005
1146
  // return doParse(`.x{${src}`, options).then((result: ParseResult) => <AstDeclaration[]>(<AstRule>result.ast.chi[0]).chi.filter(t => t.typ == EnumToken.DeclarationNodeType));
1006
1147
  // }
1007
1148
  /**
1008
- * parse string
1149
+ * parse css string
1009
1150
  * @param src
1010
1151
  * @param options
1011
1152
  */
1012
1153
  function parseString(src, options = { location: false }) {
1013
- return parseTokens([...tokenize(src)].map(t => {
1154
+ return parseTokens([...tokenize(src)].reduce((acc, t) => {
1155
+ if (t.hint == EnumToken.EOFTokenType) {
1156
+ return acc;
1157
+ }
1014
1158
  const token = getTokenType(t.token, t.hint);
1015
1159
  if (options.location) {
1016
- Object.assign(token, { loc: t.position });
1160
+ Object.assign(token, { loc: t.sta });
1017
1161
  }
1018
- return token;
1019
- }));
1162
+ acc.push(token);
1163
+ return acc;
1164
+ }, []));
1020
1165
  }
1021
1166
  function getTokenType(val, hint) {
1022
1167
  if (hint != null) {
1023
1168
  return enumTokenHints.has(hint) ? { typ: hint } : { typ: hint, val };
1024
1169
  }
1025
- if (val == ' ') {
1026
- return { typ: EnumToken.WhitespaceTokenType };
1027
- }
1028
- if (val == ';') {
1029
- return { typ: EnumToken.SemiColonTokenType };
1030
- }
1031
- if (val == '{') {
1032
- return { typ: EnumToken.BlockStartTokenType };
1033
- }
1034
- if (val == '}') {
1035
- return { typ: EnumToken.BlockEndTokenType };
1036
- }
1037
- if (val == '[') {
1038
- return { typ: EnumToken.AttrStartTokenType };
1039
- }
1040
- if (val == ']') {
1041
- return { typ: EnumToken.AttrEndTokenType };
1042
- }
1043
- if (val == ':') {
1044
- return { typ: EnumToken.ColonTokenType };
1045
- }
1046
- if (val == ')') {
1047
- return { typ: EnumToken.EndParensTokenType };
1048
- }
1049
- if (val == '(') {
1050
- return { typ: EnumToken.StartParensTokenType };
1051
- }
1052
- if (val == '=') {
1053
- return { typ: EnumToken.DelimTokenType };
1054
- }
1055
- if (val == ';') {
1056
- return { typ: EnumToken.SemiColonTokenType };
1057
- }
1058
- if (val == ',') {
1059
- return { typ: EnumToken.CommaTokenType };
1060
- }
1061
- if (val == '<') {
1062
- return { typ: EnumToken.LtTokenType };
1063
- }
1064
- if (val == '>') {
1065
- return { typ: EnumToken.GtTokenType };
1170
+ switch (val) {
1171
+ case ' ':
1172
+ return { typ: EnumToken.WhitespaceTokenType };
1173
+ case ';':
1174
+ return { typ: EnumToken.SemiColonTokenType };
1175
+ case '{':
1176
+ return { typ: EnumToken.BlockStartTokenType };
1177
+ case '}':
1178
+ return { typ: EnumToken.BlockEndTokenType };
1179
+ case '[':
1180
+ return { typ: EnumToken.AttrStartTokenType };
1181
+ case ']':
1182
+ return { typ: EnumToken.AttrEndTokenType };
1183
+ case ':':
1184
+ return { typ: EnumToken.ColonTokenType };
1185
+ case ')':
1186
+ return { typ: EnumToken.EndParensTokenType };
1187
+ case '(':
1188
+ return { typ: EnumToken.StartParensTokenType };
1189
+ case '=':
1190
+ return { typ: EnumToken.DelimTokenType };
1191
+ case ',':
1192
+ return { typ: EnumToken.CommaTokenType };
1193
+ case '<':
1194
+ return { typ: EnumToken.LtTokenType };
1195
+ case '>':
1196
+ return { typ: EnumToken.GtTokenType };
1066
1197
  }
1067
1198
  if (isPseudo(val)) {
1068
1199
  return val.endsWith('(') ? {
@@ -1149,22 +1280,22 @@ function getTokenType(val, hint) {
1149
1280
  return {
1150
1281
  typ: EnumToken.ColorTokenType,
1151
1282
  val: v,
1152
- kin: 'lit'
1283
+ kin: ColorKind.LIT
1153
1284
  };
1154
1285
  }
1155
1286
  if (isIdent(val)) {
1156
- if (systemColors.has(val.toLowerCase())) {
1287
+ if (systemColors.has(v)) {
1157
1288
  return {
1158
1289
  typ: EnumToken.ColorTokenType,
1159
1290
  val,
1160
- kin: 'sys'
1291
+ kin: ColorKind.SYS
1161
1292
  };
1162
1293
  }
1163
- if (deprecatedSystemColors.has(val.toLowerCase())) {
1294
+ if (deprecatedSystemColors.has(v)) {
1164
1295
  return {
1165
1296
  typ: EnumToken.ColorTokenType,
1166
1297
  val,
1167
- kin: 'dpsys'
1298
+ kin: ColorKind.DPSYS
1168
1299
  };
1169
1300
  }
1170
1301
  return {
@@ -1176,7 +1307,7 @@ function getTokenType(val, hint) {
1176
1307
  return {
1177
1308
  typ: EnumToken.ColorTokenType,
1178
1309
  val,
1179
- kin: 'hex'
1310
+ kin: ColorKind.HEX
1180
1311
  };
1181
1312
  }
1182
1313
  if (val.charAt(0) == '#' && isHash(val)) {
@@ -1197,23 +1328,13 @@ function getTokenType(val, hint) {
1197
1328
  };
1198
1329
  }
1199
1330
  /**
1200
- * parse token list
1331
+ * parse token array into a tree structure
1201
1332
  * @param tokens
1202
1333
  * @param options
1203
1334
  */
1204
1335
  function parseTokens(tokens, options = {}) {
1205
1336
  for (let i = 0; i < tokens.length; i++) {
1206
1337
  const t = tokens[i];
1207
- if (t.typ == EnumToken.PseudoClassFuncTokenType) {
1208
- if (t.val.slice(1) in webkitPseudoAliasMap) {
1209
- t.val = ':' + webkitPseudoAliasMap[t.val.slice(1)];
1210
- }
1211
- }
1212
- else if (t.typ == EnumToken.PseudoClassTokenType) {
1213
- if (t.val.slice(1) in webkitPseudoAliasMap) {
1214
- t.val = ':' + webkitPseudoAliasMap[t.val.slice(1)];
1215
- }
1216
- }
1217
1338
  if (t.typ == EnumToken.WhitespaceTokenType && ((i == 0 ||
1218
1339
  i + 1 == tokens.length ||
1219
1340
  [EnumToken.CommaTokenType, EnumToken.GteTokenType, EnumToken.LteTokenType, EnumToken.ColumnCombinatorTokenType].includes(tokens[i + 1].typ)) ||
@@ -1225,13 +1346,9 @@ function parseTokens(tokens, options = {}) {
1225
1346
  const typ = tokens[i + 1]?.typ;
1226
1347
  if (typ != null) {
1227
1348
  if (typ == EnumToken.FunctionTokenType) {
1228
- tokens[i + 1].val = ':' + (tokens[i + 1].val in webkitPseudoAliasMap ? webkitPseudoAliasMap[tokens[i + 1].val] : tokens[i + 1].val);
1229
1349
  tokens[i + 1].typ = EnumToken.PseudoClassFuncTokenType;
1230
1350
  }
1231
1351
  else if (typ == EnumToken.IdenTokenType) {
1232
- if (tokens[i + 1].val in webkitPseudoAliasMap) {
1233
- tokens[i + 1].val = webkitPseudoAliasMap[tokens[i + 1].val];
1234
- }
1235
1352
  tokens[i + 1].val = ':' + tokens[i + 1].val;
1236
1353
  tokens[i + 1].typ = EnumToken.PseudoClassTokenType;
1237
1354
  }
@@ -1351,6 +1468,12 @@ function parseTokens(tokens, options = {}) {
1351
1468
  l: t.chi[lower],
1352
1469
  r: t.chi[upper]
1353
1470
  };
1471
+ if (isIdentColor(t.chi[m].l)) {
1472
+ t.chi[m].l.typ = EnumToken.IdenTokenType;
1473
+ }
1474
+ if (isIdentColor(t.chi[m].r)) {
1475
+ t.chi[m].r.typ = EnumToken.IdenTokenType;
1476
+ }
1354
1477
  t.chi.splice(upper, 1);
1355
1478
  t.chi.splice(lower, 1);
1356
1479
  upper = m;
@@ -1359,7 +1482,7 @@ function parseTokens(tokens, options = {}) {
1359
1482
  upper++;
1360
1483
  }
1361
1484
  if (upper < t.chi.length &&
1362
- t.chi[upper].typ == EnumToken.Iden &&
1485
+ t.chi[upper].typ == EnumToken.IdenTokenType &&
1363
1486
  ['i', 's'].includes(t.chi[upper].val.toLowerCase())) {
1364
1487
  t.chi[m].attr = t.chi[upper].val;
1365
1488
  t.chi.splice(upper, 1);
@@ -1437,6 +1560,13 @@ function parseTokens(tokens, options = {}) {
1437
1560
  delete value.val;
1438
1561
  }
1439
1562
  }
1563
+ t.chi = splitTokenList(t.chi).reduce((acc, t) => {
1564
+ if (acc.length > 0) {
1565
+ acc.push({ typ: EnumToken.CommaTokenType });
1566
+ }
1567
+ acc.push(buildExpression(t));
1568
+ return acc;
1569
+ }, []);
1440
1570
  }
1441
1571
  else if (t.typ == EnumToken.FunctionTokenType && ['minmax', 'fit-content', 'repeat'].includes(t.val)) {
1442
1572
  // @ts-ignore
@@ -1448,34 +1578,7 @@ function parseTokens(tokens, options = {}) {
1448
1578
  }
1449
1579
  // @ts-ignore
1450
1580
  if (options.parseColor && t.typ == EnumToken.FunctionTokenType && isColor(t)) {
1451
- // @ts-ignore
1452
- t.typ = EnumToken.ColorTokenType;
1453
- // @ts-ignore
1454
- t.kin = t.val;
1455
- // @ts-ignore
1456
- if (t.chi[0].typ == EnumToken.IdenTokenType) {
1457
- // @ts-ignore
1458
- if (t.chi[0].val == 'from') {
1459
- // @ts-ignore
1460
- t.cal = 'rel';
1461
- }
1462
- // @ts-ignore
1463
- else if (t.val == 'color-mix' && t.chi[0].val == 'in') {
1464
- // @ts-ignore
1465
- t.cal = 'mix';
1466
- }
1467
- else { // @ts-ignore
1468
- if (t.val == 'color') {
1469
- // @ts-ignore
1470
- t.cal = 'col';
1471
- }
1472
- }
1473
- }
1474
- const filter = [EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType];
1475
- if (t.val != 'light-dark') {
1476
- filter.push(EnumToken.CommaTokenType);
1477
- }
1478
- t.chi = t.chi.filter((t) => !filter.includes(t.typ));
1581
+ parseColor(t);
1479
1582
  continue;
1480
1583
  }
1481
1584
  if (t.typ == EnumToken.UrlFunctionTokenType) {
@@ -1487,6 +1590,12 @@ function parseTokens(tokens, options = {}) {
1487
1590
  if (t.chi[0].val.slice(1, 5) != 'data:' && urlTokenMatcher.test(value)) {
1488
1591
  // @ts-ignore
1489
1592
  t.chi[0].typ = EnumToken.UrlTokenTokenType;
1593
+ // console.error({t, v: t.chi[0], value,
1594
+ // src: options.src,
1595
+ // resolved: options.src !== '' && options.resolveUrls ? options.resolve(value, options.src).absolute : null,
1596
+ // val2: options.src !== '' && options.resolveUrls ? options.resolve(value, options.src).absolute : value});
1597
+ //
1598
+ // console.error(new Error('resolved'));
1490
1599
  // @ts-ignore
1491
1600
  t.chi[0].val = options.src !== '' && options.resolveUrls ? options.resolve(value, options.src).absolute : value;
1492
1601
  }
@@ -1522,7 +1631,7 @@ function parseTokens(tokens, options = {}) {
1522
1631
  Object.assign(t, {
1523
1632
  typ: EnumToken.ColorTokenType,
1524
1633
  val: COLORS_NAMES[value].length < value.length ? COLORS_NAMES[value] : value,
1525
- kin: 'hex'
1634
+ kin: ColorKind.HEX
1526
1635
  });
1527
1636
  }
1528
1637
  continue;
@@ -1532,11 +1641,11 @@ function parseTokens(tokens, options = {}) {
1532
1641
  // @ts-ignore
1533
1642
  t.typ = EnumToken.ColorTokenType;
1534
1643
  // @ts-ignore
1535
- t.kin = 'hex';
1644
+ t.kin = ColorKind.HEX;
1536
1645
  }
1537
1646
  }
1538
1647
  }
1539
1648
  return tokens;
1540
1649
  }
1541
1650
 
1542
- export { doParse, parseSelector, parseString, parseTokens, urlTokenMatcher };
1651
+ export { doParse, parseAtRulePrelude, parseSelector, parseString, parseTokens, urlTokenMatcher };