@tbela99/css-parser 1.3.1 → 1.3.3

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.
@@ -1,7 +1,7 @@
1
1
  import { isIdentStart, isIdent, isIdentColor, mathFuncs, isColor, parseColor, isPseudo, pseudoElements, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isDimension, parseDimension, isHexColor, isHash, mediaTypes } from '../syntax/syntax.js';
2
2
  import { EnumToken, ColorType, ValidationLevel, SyntaxValidationResult } from '../ast/types.js';
3
3
  import { minify, definedPropertySettings, combinators } from '../ast/minify.js';
4
- import { walkValues, walk, WalkerOptionEnum } from '../ast/walk.js';
4
+ import { walkValues, WalkerValueEvent, walk, WalkerOptionEnum } from '../ast/walk.js';
5
5
  import { expand } from '../ast/expand.js';
6
6
  import './utils/config.js';
7
7
  import { parseDeclarationNode } from './utils/declaration.js';
@@ -39,6 +39,37 @@ const enumTokenHints = new Set([
39
39
  function reject(reason) {
40
40
  throw new Error(reason ?? 'Parsing aborted');
41
41
  }
42
+ function normalizeVisitorKeyName(keyName) {
43
+ return keyName.replace(/-([a-z])/g, (all, one) => one.toUpperCase());
44
+ }
45
+ function replaceToken(parent, value, replacement) {
46
+ // @ts-ignore
47
+ if ('parent' in value && value.parent != replacement.parent) {
48
+ Object.defineProperty(replacement, 'parent', {
49
+ ...definedPropertySettings,
50
+ value: value.parent
51
+ });
52
+ }
53
+ if (parent.typ == EnumToken.BinaryExpressionTokenType) {
54
+ if (parent.l == value) {
55
+ parent.l = replacement;
56
+ }
57
+ else {
58
+ parent.r = replacement;
59
+ }
60
+ }
61
+ else {
62
+ // @ts-ignore
63
+ const target = 'val' in parent && Array.isArray(parent.val) ? parent.val : parent.chi;
64
+ // @ts-ignore
65
+ const index = target.indexOf(value);
66
+ if (index == -1) {
67
+ return;
68
+ }
69
+ // @ts-ignore
70
+ target.splice(index, 1, ...(Array.isArray(replacement) ? replacement : [replacement]));
71
+ }
72
+ }
42
73
  /**
43
74
  * parse css string
44
75
  * @param iter
@@ -121,7 +152,7 @@ async function doParse(iter, options = {}) {
121
152
  const rawTokens = [];
122
153
  const imports = [];
123
154
  // @ts-ignore ignore error
124
- const isAsync = typeof iter[Symbol.asyncIterator] === 'function';
155
+ let isAsync = typeof iter[Symbol.asyncIterator] === 'function';
125
156
  while (item = isAsync ? (await iter.next()).value : iter.next().value) {
126
157
  stats.bytesIn = item.bytesIn;
127
158
  rawTokens.push(item);
@@ -240,21 +271,18 @@ async function doParse(iter, options = {}) {
240
271
  const token = node.tokens[0];
241
272
  const url = token.typ == EnumToken.StringTokenType ? token.val.slice(1, -1) : token.val;
242
273
  try {
243
- const root = await options.getStream(url, options.src).then(async (stream) => {
244
- return doParse(tokenizeStream(stream), Object.assign({}, options, {
245
- minify: false,
246
- setParent: false,
247
- src: options.resolve(url, options.src).absolute
248
- })); // )
249
- });
250
- // const root: ParseResult = await options.load!(url, <string>options.src).then((src: string) => {
251
- //
252
- // return doParse(src, Object.assign({}, options, {
253
- // minify: false,
254
- // setParent: false,
255
- // src: options.resolve!(url, options.src as string).absolute
256
- // }))
257
- // });
274
+ const result = options.load(url, options.src);
275
+ const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == 'AsyncFunction' ? await result : result;
276
+ const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream) : tokenize({
277
+ stream,
278
+ buffer: '',
279
+ position: { ind: 0, lin: 1, col: 1 },
280
+ currentPosition: { ind: -1, lin: 1, col: 0 }
281
+ }), Object.assign({}, options, {
282
+ minify: false,
283
+ setParent: false,
284
+ src: options.resolve(url, options.src).absolute
285
+ }));
258
286
  stats.importedBytesIn += root.stats.bytesIn;
259
287
  stats.imports.push(root.stats);
260
288
  node.parent.chi.splice(node.parent.chi.indexOf(node), 1, ...root.ast.chi);
@@ -282,39 +310,230 @@ async function doParse(iter, options = {}) {
282
310
  if (options.expandNestingRules) {
283
311
  ast = expand(ast);
284
312
  }
313
+ const valuesHandlers = new Map;
314
+ const preValuesHandlers = new Map;
315
+ const postValuesHandlers = new Map;
316
+ const preVisitorsHandlersMap = new Map;
317
+ const visitorsHandlersMap = new Map;
318
+ const postVisitorsHandlersMap = new Map;
319
+ const allValuesHandlers = [];
285
320
  if (options.visitor != null) {
286
- for (const result of walk(ast)) {
287
- if (result.node.typ == EnumToken.DeclarationNodeType &&
288
- (typeof options.visitor.Declaration == 'function' || options.visitor.Declaration?.[result.node.nam] != null)) {
289
- const callable = typeof options.visitor.Declaration == 'function' ? options.visitor.Declaration : options.visitor.Declaration[result.node.nam];
290
- const isAsync = Object.getPrototypeOf(callable).constructor.name == 'AsyncFunction';
291
- const results = isAsync ? await callable(result.node) : callable(result.node);
292
- if (results == null || (Array.isArray(results) && results.length == 0)) {
293
- continue;
321
+ for (const [key, value] of Object.entries(options.visitor)) {
322
+ if (key in EnumToken) {
323
+ if (typeof value == 'function') {
324
+ valuesHandlers.set(EnumToken[key], value);
325
+ }
326
+ else if (typeof value == 'object' && 'type' in value && 'handler' in value && value.type in WalkerValueEvent) {
327
+ if (WalkerValueEvent[value.type] == WalkerValueEvent.Enter) {
328
+ preValuesHandlers.set(EnumToken[key], value.handler);
329
+ }
330
+ else if (WalkerValueEvent[value.type] == WalkerValueEvent.Leave) {
331
+ postValuesHandlers.set(EnumToken[key], value.handler);
332
+ }
333
+ }
334
+ else {
335
+ console.warn(`doParse: visitor.${key} is not a valid key name`);
294
336
  }
295
- // @ts-ignore
296
- result.parent.chi.splice(result.parent.chi.indexOf(result.node), 1, ...(Array.isArray(results) ? results : [results]));
297
337
  }
298
- else if (options.visitor.Rule != null && result.node.typ == EnumToken.RuleNodeType) {
299
- const isAsync = Object.getPrototypeOf(options.visitor.Rule).constructor.name == 'AsyncFunction';
300
- const results = isAsync ? await options.visitor.Rule(result.node) : options.visitor.Rule(result.node);
301
- if (results == null || (Array.isArray(results) && results.length == 0)) {
302
- continue;
338
+ else if (['Declaration', 'Rule', 'AtRule', 'KeyframesRule', 'KeyframesAtRule'].includes(key)) {
339
+ if (typeof value == 'function') {
340
+ visitorsHandlersMap.set(key, value);
341
+ }
342
+ else if (typeof value == 'object') {
343
+ if ('type' in value && 'handler' in value && value.type in WalkerValueEvent) {
344
+ if (WalkerValueEvent[value.type] == WalkerValueEvent.Enter) {
345
+ preVisitorsHandlersMap.set(key, value.handler);
346
+ }
347
+ else if (WalkerValueEvent[value.type] == WalkerValueEvent.Leave) {
348
+ postVisitorsHandlersMap.set(key, value.handler);
349
+ }
350
+ }
351
+ else {
352
+ visitorsHandlersMap.set(key, value);
353
+ }
354
+ }
355
+ else {
356
+ console.warn(`doParse: visitor.${key} is not a valid key name`);
357
+ }
358
+ }
359
+ else {
360
+ console.warn(`doParse: visitor.${key} is not a valid key name`);
361
+ }
362
+ }
363
+ if (preValuesHandlers.size > 0) {
364
+ allValuesHandlers.push(preValuesHandlers);
365
+ }
366
+ if (valuesHandlers.size > 0) {
367
+ allValuesHandlers.push(valuesHandlers);
368
+ }
369
+ if (postValuesHandlers.size > 0) {
370
+ allValuesHandlers.push(postValuesHandlers);
371
+ }
372
+ }
373
+ for (const result of walk(ast)) {
374
+ // if (result.parent != null && !isNodeAllowedInContext(result.node, result.parent as AstNode)) {
375
+ //
376
+ // errors.push({
377
+ // action: 'drop',
378
+ // message: `${EnumToken[result.parent.typ]}: child ${EnumToken[result.node.typ]}${result.node.typ == EnumToken.DeclarationNodeType ? ` '${(result.node as AstDeclaration).nam}'` : result.node.typ == EnumToken.AtRuleNodeType || result.node.typ == EnumToken.KeyframesAtRuleNodeType ? ` '@${(result.node as AstAtRule).nam}'` : ''} not allowed in context${result.parent.typ == EnumToken.AtRuleNodeType ? ` '@${(result.parent as AstAtRule).nam}'` : result.parent.typ == EnumToken.StyleSheetNodeType ? ` 'stylesheet'` : ''}`,
379
+ // // @ts-ignore
380
+ // location: result.node.loc ?? map.get(result.node ) ?? null
381
+ // });
382
+ //
383
+ // // @ts-ignore
384
+ // removeNode(result.node, result.parent as AstNode);
385
+ // continue;
386
+ // }
387
+ if (allValuesHandlers.length > 0 || preVisitorsHandlersMap.size > 0 || visitorsHandlersMap.size > 0 || postVisitorsHandlersMap.size > 0) {
388
+ if ((result.node.typ == EnumToken.DeclarationNodeType &&
389
+ (preVisitorsHandlersMap.has('Declaration') || visitorsHandlersMap.has('Declaration') || postVisitorsHandlersMap.has('Declaration'))) ||
390
+ (result.node.typ == EnumToken.AtRuleNodeType && (preVisitorsHandlersMap.has('AtRule') || visitorsHandlersMap.has('AtRule') || postVisitorsHandlersMap.has('AtRule'))) ||
391
+ (result.node.typ == EnumToken.KeyframesAtRuleNodeType && (preVisitorsHandlersMap.has('KeyframesAtRule') || visitorsHandlersMap.has('KeyframesAtRule') || postVisitorsHandlersMap.has('KeyframesAtRule')))) {
392
+ const handlers = [];
393
+ const key = result.node.typ == EnumToken.DeclarationNodeType ? 'Declaration' : result.node.typ == EnumToken.AtRuleNodeType ? 'AtRule' : 'KeyframesAtRule';
394
+ if (preVisitorsHandlersMap.has(key)) {
395
+ // @ts-ignore
396
+ handlers.push(preVisitorsHandlersMap.get(key));
397
+ }
398
+ if (visitorsHandlersMap.has(key)) {
399
+ // @ts-ignore
400
+ handlers.push(visitorsHandlersMap.get(key));
401
+ }
402
+ if (postVisitorsHandlersMap.has(key)) {
403
+ // @ts-ignore
404
+ handlers.push(postVisitorsHandlersMap.get(key));
405
+ }
406
+ let callable;
407
+ let node = result.node;
408
+ for (const handler of handlers) {
409
+ callable = typeof handler == 'function' ? handler : handler[normalizeVisitorKeyName(node.typ == EnumToken.DeclarationNodeType || node.typ == EnumToken.AtRuleNodeType ? node.nam : node.val)];
410
+ if (callable == null) {
411
+ continue;
412
+ }
413
+ let replacement = callable(node, result.parent);
414
+ if (replacement == null) {
415
+ continue;
416
+ }
417
+ isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
418
+ if (replacement) {
419
+ replacement = await replacement;
420
+ }
421
+ if (replacement == null || replacement == node) {
422
+ continue;
423
+ }
424
+ // @ts-ignore
425
+ node = replacement;
426
+ //
427
+ if (Array.isArray(node)) {
428
+ break;
429
+ }
430
+ }
431
+ if (node != result.node) {
432
+ // @ts-ignore
433
+ replaceToken(result.parent, result.node, node);
434
+ }
435
+ }
436
+ else if ((result.node.typ == EnumToken.RuleNodeType && (preVisitorsHandlersMap.has('Rule') || visitorsHandlersMap.has('Rule') || postVisitorsHandlersMap.has('Rule'))) ||
437
+ (result.node.typ == EnumToken.KeyFramesRuleNodeType && (preVisitorsHandlersMap.has('KeyframesRule') || visitorsHandlersMap.has('KeyframesRule') || postVisitorsHandlersMap.has('KeyframesRule')))) {
438
+ const handlers = [];
439
+ const key = result.node.typ == EnumToken.RuleNodeType ? 'Rule' : 'KeyframesRule';
440
+ if (preVisitorsHandlersMap.has(key)) {
441
+ // @ts-ignore
442
+ handlers.push(preVisitorsHandlersMap.get(key));
443
+ }
444
+ if (visitorsHandlersMap.has(key)) {
445
+ // @ts-ignore
446
+ handlers.push(visitorsHandlersMap.get(key));
447
+ }
448
+ if (postVisitorsHandlersMap.has(key)) {
449
+ // @ts-ignore
450
+ handlers.push(postVisitorsHandlersMap.get(key));
451
+ }
452
+ let node = result.node;
453
+ for (const callable of handlers) {
454
+ // @ts-ignore
455
+ let replacement = callable(node, result.parent);
456
+ if (replacement == null) {
457
+ continue;
458
+ }
459
+ isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
460
+ if (replacement) {
461
+ replacement = await replacement;
462
+ }
463
+ if (replacement == null || replacement == node) {
464
+ continue;
465
+ }
466
+ // @ts-ignore
467
+ node = replacement;
468
+ //
469
+ if (Array.isArray(node)) {
470
+ break;
471
+ }
303
472
  }
304
473
  // @ts-ignore
305
- result.parent.chi.splice(result.parent.chi.indexOf(result.node), 1, ...(Array.isArray(results) ? results : [results]));
306
- }
307
- else if (options.visitor.AtRule != null &&
308
- result.node.typ == EnumToken.AtRuleNodeType &&
309
- (typeof options.visitor.AtRule == 'function' || options.visitor.AtRule?.[result.node.nam] != null)) {
310
- const callable = typeof options.visitor.AtRule == 'function' ? options.visitor.AtRule : options.visitor.AtRule[result.node.nam];
311
- const isAsync = Object.getPrototypeOf(callable).constructor.name == 'AsyncFunction';
312
- const results = isAsync ? await callable(result.node) : callable(result.node);
313
- if (results == null || (Array.isArray(results) && results.length == 0)) {
474
+ if (node != result.node) {
475
+ // @ts-ignore
476
+ replaceToken(result.parent, result.node, node);
477
+ }
478
+ }
479
+ else if (allValuesHandlers.length > 0) {
480
+ let callable;
481
+ let node = null;
482
+ node = result.node;
483
+ for (const valueHandler of allValuesHandlers) {
484
+ if (valueHandler.has(node.typ)) {
485
+ callable = valueHandler.get(node.typ);
486
+ let replacement = callable(node, result.parent);
487
+ if (replacement == null) {
488
+ continue;
489
+ }
490
+ isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
491
+ if (isAsync) {
492
+ replacement = await replacement;
493
+ }
494
+ if (replacement != null && replacement != node) {
495
+ node = replacement;
496
+ }
497
+ }
498
+ }
499
+ if (node != result.node) {
500
+ // @ts-ignore
501
+ replaceToken(result.parent, value, node);
502
+ }
503
+ const tokens = 'tokens' in result.node ? result.node.tokens : [];
504
+ if ('val' in result.node && Array.isArray(result.node.val)) {
505
+ tokens.push(...result.node.val);
506
+ }
507
+ if (tokens.length == 0) {
314
508
  continue;
315
509
  }
316
- // @ts-ignore
317
- result.parent.chi.splice(result.parent.chi.indexOf(result.node), 1, ...(Array.isArray(results) ? results : [results]));
510
+ for (const { value, parent, root } of walkValues(tokens, result.node)) {
511
+ node = value;
512
+ for (const valueHandler of allValuesHandlers) {
513
+ if (valueHandler.has(node.typ)) {
514
+ callable = valueHandler.get(node.typ);
515
+ let result = callable(node, parent, root);
516
+ if (result == null) {
517
+ continue;
518
+ }
519
+ isAsync = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == 'AsyncFunction';
520
+ if (isAsync) {
521
+ result = await result;
522
+ }
523
+ if (result != null && result != node) {
524
+ node = result;
525
+ }
526
+ //
527
+ if (Array.isArray(node)) {
528
+ break;
529
+ }
530
+ }
531
+ }
532
+ if (node != value) {
533
+ // @ts-ignore
534
+ replaceToken(parent, value, node);
535
+ }
536
+ }
318
537
  }
319
538
  }
320
539
  }
@@ -374,7 +593,6 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
374
593
  continue;
375
594
  }
376
595
  loc = location;
377
- // @ts-ignore
378
596
  context.chi.push(tokens[i]);
379
597
  if (options.sourcemap) {
380
598
  tokens[i].loc = loc;
@@ -408,9 +626,7 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
408
626
  rawTokens.shift();
409
627
  if (atRule.val == 'import') {
410
628
  // only @charset and @layer are accepted before @import
411
- // @ts-ignore
412
629
  if (context.chi.length > 0) {
413
- // @ts-ignore
414
630
  let i = context.chi.length;
415
631
  while (i--) {
416
632
  // @ts-ignore
@@ -419,11 +635,8 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
419
635
  continue;
420
636
  }
421
637
  if (type != EnumToken.AtRuleNodeType) {
422
- // @ts-ignore
423
638
  if (!(type == EnumToken.InvalidAtRuleTokenType &&
424
- // @ts-ignore
425
639
  ['charset', 'layer', 'import'].includes(context.chi[i].nam))) {
426
- // @ts-ignore
427
640
  errors.push({
428
641
  action: 'drop',
429
642
  message: 'invalid @import',
@@ -434,7 +647,6 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
434
647
  return null;
435
648
  }
436
649
  }
437
- // @ts-ignore
438
650
  const name = context.chi[i].nam;
439
651
  if (name != 'charset' && name != 'import' && name != 'layer') {
440
652
  errors.push({ action: 'drop', message: 'invalid @import', location });
@@ -532,7 +744,7 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
532
744
  const nam = renderToken(atRule, { removeComments: true });
533
745
  // @ts-ignore
534
746
  const node = {
535
- typ: /^(-[a-z]+-)?keyframes$/.test(nam) ? EnumToken.KeyframeAtRuleNodeType : EnumToken.AtRuleNodeType,
747
+ typ: /^(-[a-z]+-)?keyframes$/.test(nam) ? EnumToken.KeyframesAtRuleNodeType : EnumToken.AtRuleNodeType,
536
748
  nam,
537
749
  val: raw.join('')
538
750
  };
@@ -566,7 +778,7 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
566
778
  error: '',
567
779
  node,
568
780
  syntax: '@' + node.nam
569
- } : isValid ? (node.typ == EnumToken.KeyframeAtRuleNodeType ? validateAtRuleKeyframes(node) : validateAtRule(node, options, context)) : {
781
+ } : isValid ? (node.typ == EnumToken.KeyframesAtRuleNodeType ? validateAtRuleKeyframes(node) : validateAtRule(node, options, context)) : {
570
782
  valid: SyntaxValidationResult.Drop,
571
783
  node,
572
784
  syntax: '@' + node.nam,
@@ -602,7 +814,7 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
602
814
  const location = map.get(tokens[0]);
603
815
  const uniq = new Map;
604
816
  parseTokens(tokens, { minify: true });
605
- const ruleType = context.typ == EnumToken.KeyframeAtRuleNodeType ? EnumToken.KeyFrameRuleNodeType : EnumToken.RuleNodeType;
817
+ const ruleType = context.typ == EnumToken.KeyframesAtRuleNodeType ? EnumToken.KeyFramesRuleNodeType : EnumToken.RuleNodeType;
606
818
  if (ruleType == EnumToken.RuleNodeType) {
607
819
  parseSelector(tokens);
608
820
  }
@@ -612,6 +824,34 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
612
824
  if (curr.typ == EnumToken.CommentTokenType) {
613
825
  return acc;
614
826
  }
827
+ if (options.minify) {
828
+ if (curr.typ == EnumToken.PseudoClassFuncTokenType && curr.val == ':nth-child') {
829
+ let i = 0;
830
+ for (; i < curr.chi.length; i++) {
831
+ if (curr.chi[i].typ == EnumToken.IdenTokenType && curr.chi[i].val == 'even') {
832
+ Object.assign(curr.chi[i], {
833
+ typ: EnumToken.Dimension,
834
+ val: 2,
835
+ unit: 'n'
836
+ });
837
+ }
838
+ else if (curr.chi[i].typ == EnumToken.Dimension &&
839
+ curr.chi[i].val == 2 &&
840
+ curr.chi[i].unit == 'n' &&
841
+ curr.chi[i + 1]?.typ == EnumToken.WhitespaceTokenType &&
842
+ curr.chi[i + 2]?.typ == EnumToken.LiteralTokenType &&
843
+ curr.chi[i + 2].val == '+' &&
844
+ curr.chi[i + 3]?.typ == EnumToken.WhitespaceTokenType &&
845
+ curr.chi[i + 4]?.typ == EnumToken.NumberTokenType &&
846
+ curr.chi[i + 4].val == 1) {
847
+ curr.chi.splice(i, 5, Object.assign(curr.chi[i], {
848
+ typ: EnumToken.IdenTokenType,
849
+ val: 'odd'
850
+ }));
851
+ }
852
+ }
853
+ }
854
+ }
615
855
  if (curr.typ == EnumToken.WhitespaceTokenType) {
616
856
  if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
617
857
  trimWhiteSpace.includes(array[index + 1]?.typ) ||
@@ -620,7 +860,7 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
620
860
  return acc;
621
861
  }
622
862
  }
623
- if (ruleType == EnumToken.KeyFrameRuleNodeType) {
863
+ if (ruleType == EnumToken.KeyFramesRuleNodeType && options.minify) {
624
864
  if (curr.typ == EnumToken.IdenTokenType && curr.val == 'from') {
625
865
  Object.assign(curr, { typ: EnumToken.PercentageTokenType, val: '0' });
626
866
  }
@@ -669,7 +909,7 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
669
909
  const valid = options.validation == ValidationLevel.None ? {
670
910
  valid: SyntaxValidationResult.Valid,
671
911
  error: null
672
- } : ruleType == EnumToken.KeyFrameRuleNodeType ? validateKeyframeSelector(tokens) : validateSelector(tokens, options, context);
912
+ } : ruleType == EnumToken.KeyFramesRuleNodeType ? validateKeyframeSelector(tokens) : validateSelector(tokens, options, context);
673
913
  if (valid.valid != SyntaxValidationResult.Valid) {
674
914
  // @ts-ignore
675
915
  node.typ = EnumToken.InvalidRuleTokenType;
@@ -1045,7 +1285,7 @@ async function parseDeclarations(declaration) {
1045
1285
  position: { ind: 0, lin: 1, col: 1 },
1046
1286
  currentPosition: { ind: -1, lin: 1, col: 0 }
1047
1287
  }), { setParent: false, minify: false, validation: false }).then(result => {
1048
- return result.ast.chi[0].chi.filter(t => t.typ == EnumToken.DeclarationNodeType);
1288
+ return result.ast.chi[0].chi.filter(t => t.typ == EnumToken.DeclarationNodeType || t.typ == EnumToken.CommentNodeType);
1049
1289
  });
1050
1290
  }
1051
1291
  /**
@@ -1662,4 +1902,4 @@ function parseTokens(tokens, options = {}) {
1662
1902
  return tokens;
1663
1903
  }
1664
1904
 
1665
- export { doParse, getTokenType, parseAtRulePrelude, parseDeclarations, parseSelector, parseString, parseTokens, urlTokenMatcher };
1905
+ export { doParse, getTokenType, parseAtRulePrelude, parseDeclarations, parseSelector, parseString, parseTokens, replaceToken, urlTokenMatcher };
@@ -579,7 +579,7 @@ async function* tokenizeStream(input) {
579
579
  const reader = input.getReader();
580
580
  while (true) {
581
581
  const { done, value } = await reader.read();
582
- parseInfo.stream += decoder.decode(value, { stream: true });
582
+ parseInfo.stream += ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value;
583
583
  yield* tokenize(parseInfo, done);
584
584
  if (done) {
585
585
  break;
@@ -111,7 +111,7 @@ function doRender(data, options = {}) {
111
111
  function updateSourceMap(node, options, cache, sourcemap, position, str) {
112
112
  if ([
113
113
  EnumToken.RuleNodeType, EnumToken.AtRuleNodeType,
114
- EnumToken.KeyFrameRuleNodeType, EnumToken.KeyframeAtRuleNodeType
114
+ EnumToken.KeyFramesRuleNodeType, EnumToken.KeyframesAtRuleNodeType
115
115
  ].includes(node.typ)) {
116
116
  let src = node.loc?.src ?? '';
117
117
  let output = options.output ?? '';
@@ -185,9 +185,9 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
185
185
  }, '');
186
186
  case EnumToken.AtRuleNodeType:
187
187
  case EnumToken.RuleNodeType:
188
- case EnumToken.KeyFrameRuleNodeType:
189
- case EnumToken.KeyframeAtRuleNodeType:
190
- if ([EnumToken.AtRuleNodeType, EnumToken.KeyframeAtRuleNodeType].includes(data.typ) && !('chi' in data)) {
188
+ case EnumToken.KeyFramesRuleNodeType:
189
+ case EnumToken.KeyframesAtRuleNodeType:
190
+ if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ) && !('chi' in data)) {
191
191
  return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
192
192
  }
193
193
  // @ts-ignore
@@ -228,7 +228,7 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
228
228
  if (children.endsWith(';')) {
229
229
  children = children.slice(0, -1);
230
230
  }
231
- if ([EnumToken.AtRuleNodeType, EnumToken.KeyframeAtRuleNodeType].includes(data.typ)) {
231
+ if ([EnumToken.AtRuleNodeType, EnumToken.KeyframesAtRuleNodeType].includes(data.typ)) {
232
232
  return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
233
233
  }
234
234
  return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
@@ -4,7 +4,7 @@ import '../../ast/walk.js';
4
4
  import '../../parser/parse.js';
5
5
  import '../../parser/tokenize.js';
6
6
  import '../../parser/utils/config.js';
7
- import { color2srgbvalues } from './color.js';
7
+ import { color2srgbvalues, toPrecisionValue } from './color.js';
8
8
  import { hsl2srgbvalues } from './rgb.js';
9
9
  import './utils/constants.js';
10
10
  import { lch2srgbvalues, lab2srgbvalues, oklch2srgbvalues, oklab2srgbvalues, hwb2srgbvalues, rgb2srgbvalues } from './srgb.js';
@@ -92,11 +92,14 @@ function cmyktoken(values) {
92
92
  chi: values.reduce((acc, curr, index) => index < 4 ? [...acc, {
93
93
  typ: EnumToken.PercentageTokenType,
94
94
  // @ts-ignore
95
- val: curr * 100
95
+ val: toPrecisionValue(curr) * 100
96
96
  }] : [...acc, {
97
97
  typ: EnumToken.LiteralTokenType,
98
98
  val: '/'
99
- }, { typ: EnumToken.PercentageTokenType, val: curr * 100 }], []),
99
+ }, {
100
+ typ: EnumToken.PercentageTokenType,
101
+ val: toPrecisionValue(curr) * 100
102
+ }], []),
100
103
  kin: ColorType.DEVICE_CMYK
101
104
  };
102
105
  }
@@ -129,11 +129,10 @@ function colorMix(colorSpace, hueInterpolationMethod, color1, percentage1, color
129
129
  // @ts-ignore
130
130
  const calculate = () => [colorSpace].concat(values1.map((v1, i) => {
131
131
  return {
132
- // @ts-ignore
133
- typ: EnumToken.NumberTokenType, val: String((mul1 * v1 * p1 + mul2 * values2[i] * p2) / mul)
132
+ typ: EnumToken.NumberTokenType, val: (mul1 * v1 * p1 + mul2 * values2[i] * p2) / mul
134
133
  };
135
134
  }).concat(mul == 1 ? [] : [{
136
- typ: EnumToken.NumberTokenType, val: String(mul)
135
+ typ: EnumToken.NumberTokenType, val: mul
137
136
  }]));
138
137
  switch (colorSpace.val) {
139
138
  case 'srgb':
@@ -25,6 +25,7 @@ import { reduceHexValue, rgb2HexToken, color2HexToken, lch2HexToken, lab2HexToke
25
25
  import { parseRelativeColor } from './relativecolor.js';
26
26
  import { color2cmykToken, lch2cmykToken, lab2cmykToken, oklch2cmykToken, oklab2cmyk, hwb2cmykToken, hsl2cmykToken, rgb2cmykToken } from './cmyk.js';
27
27
  import { a98rgb2srgbvalues, srgb2a98values } from './a98rgb.js';
28
+ import { epsilon } from '../../ast/transform/utils.js';
28
29
  import '../../renderer/sourcemap/lib/encode.js';
29
30
 
30
31
  /**
@@ -32,6 +33,8 @@ import '../../renderer/sourcemap/lib/encode.js';
32
33
  * @param token
33
34
  * @param to
34
35
  *
36
+ * @private
37
+ *
35
38
  * <code>
36
39
  *
37
40
  * const token = {typ: EnumToken.ColorTokenType, kin: ColorType.HEX, val: '#F00'}
@@ -73,7 +76,8 @@ function convertColor(token, to) {
73
76
  if (components != null) {
74
77
  token = {
75
78
  ...token,
76
- chi: [...(token.val == 'color' ? [chi[offset]] : []), ...Object.values(components)]
79
+ chi: [...(token.val == 'color' ? [chi[offset]] : []), ...Object.values(components)],
80
+ kin: ColorType[token.val.toUpperCase().replaceAll('-', '_')]
77
81
  };
78
82
  delete token.cal;
79
83
  }
@@ -89,6 +93,11 @@ function convertColor(token, to) {
89
93
  if (token.kin == ColorType.COLOR) {
90
94
  const colorSpace = token.chi.find(t => ![EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ));
91
95
  if (colorSpace.val == ColorType[to].toLowerCase().replaceAll('_', '-')) {
96
+ for (const chi of token.chi) {
97
+ if (chi.typ == EnumToken.NumberTokenType && typeof chi.val == 'number') {
98
+ chi.val = toPrecisionValue(getNumber(chi));
99
+ }
100
+ }
92
101
  return token;
93
102
  }
94
103
  }
@@ -512,9 +521,9 @@ function color2srgbvalues(token) {
512
521
  function values2colortoken(values, to) {
513
522
  values = srgb2srgbcolorspace(values, to);
514
523
  const chi = [
515
- { typ: EnumToken.NumberTokenType, val: values[0] },
516
- { typ: EnumToken.NumberTokenType, val: values[1] },
517
- { typ: EnumToken.NumberTokenType, val: values[2] },
524
+ { typ: EnumToken.NumberTokenType, val: toPrecisionValue(values[0]) },
525
+ { typ: EnumToken.NumberTokenType, val: toPrecisionValue(values[1]) },
526
+ { typ: EnumToken.NumberTokenType, val: toPrecisionValue(values[2]) },
518
527
  ];
519
528
  if (values.length == 4) {
520
529
  chi.push({ typ: EnumToken.LiteralTokenType, val: "/" }, {
@@ -539,8 +548,17 @@ function getNumber(token) {
539
548
  if (token.typ == EnumToken.IdenTokenType && token.val == 'none') {
540
549
  return 0;
541
550
  }
551
+ let val;
542
552
  // @ts-ignore
543
- return token.typ == EnumToken.PercentageTokenType ? token.val / 100 : token.val;
553
+ if (typeof token.val != 'number' && token.val?.typ == EnumToken.FractionTokenType) {
554
+ // @ts-ignore
555
+ val = token.val.l.val / token.val.r.val;
556
+ }
557
+ else {
558
+ val = token.val;
559
+ }
560
+ // @ts-ignore
561
+ return token.typ == EnumToken.PercentageTokenType ? val / 100 : val;
544
562
  }
545
563
  /**
546
564
  * convert angle to turn
@@ -571,6 +589,10 @@ function getAngle(token) {
571
589
  // @ts-ignore
572
590
  return token.val / 360;
573
591
  }
592
+ function toPrecisionValue(value) {
593
+ value = +value.toFixed(colorPrecision);
594
+ return Math.abs(value) < epsilon ? 0 : value;
595
+ }
574
596
  function toPrecisionAngle(angle) {
575
597
  angle = +angle.toPrecision(colorPrecision);
576
598
  if (Math.abs(angle) >= 360) {
@@ -585,4 +607,4 @@ function toPrecisionAngle(angle) {
585
607
  return angle;
586
608
  }
587
609
 
588
- export { cmyk2colorToken, color2colorToken, color2srgbvalues, convertColor, getAngle, getNumber, hex2colorToken, hsl2colorToken, hwb2colorToken, lab2colorToken, lch2colorToken, minmax, oklab2colorToken, oklch2colorToken, rgb2colorToken, toPrecisionAngle };
610
+ export { cmyk2colorToken, color2colorToken, color2srgbvalues, convertColor, getAngle, getNumber, hex2colorToken, hsl2colorToken, hwb2colorToken, lab2colorToken, lch2colorToken, minmax, oklab2colorToken, oklch2colorToken, rgb2colorToken, toPrecisionAngle, toPrecisionValue };
@@ -33,6 +33,9 @@ function reduceHexValue(value) {
33
33
  value[7] == value[8]) {
34
34
  value = `#${value[1]}${value[3]}${value[5]}${value[7] == 'f' ? '' : value[7]}`;
35
35
  }
36
+ if (value.endsWith('ff')) {
37
+ value = value.slice(0, -2);
38
+ }
36
39
  }
37
40
  return named_color != null && named_color.length <= value.length ? named_color : value;
38
41
  }