zyzz 0.0.8 → 0.0.11

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 (126) hide show
  1. package/README.md +8 -8
  2. package/dist/Style.d.ts +2 -2
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js +6 -6
  5. package/dist/Style.js.map +1 -1
  6. package/dist/Vars.d.ts +2 -2
  7. package/dist/Vars.d.ts.map +1 -1
  8. package/dist/Vars.js.map +1 -1
  9. package/dist/compiler/Source.d.ts.map +1 -1
  10. package/dist/compiler/Source.js +10 -0
  11. package/dist/compiler/Source.js.map +1 -1
  12. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  13. package/dist/compiler/internal/Contract.js +11 -6
  14. package/dist/compiler/internal/Contract.js.map +1 -1
  15. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  16. package/dist/compiler/internal/Contributions.js +592 -556
  17. package/dist/compiler/internal/Contributions.js.map +1 -1
  18. package/dist/compiler/internal/Expression.d.ts +4 -0
  19. package/dist/compiler/internal/Expression.d.ts.map +1 -1
  20. package/dist/compiler/internal/Expression.js +45 -0
  21. package/dist/compiler/internal/Expression.js.map +1 -1
  22. package/dist/compiler/internal/RecipeConditions.js +2 -2
  23. package/dist/compiler/internal/RecipeConditions.js.map +1 -1
  24. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  25. package/dist/compiler/internal/Themes.js +2 -0
  26. package/dist/compiler/internal/Themes.js.map +1 -1
  27. package/dist/default.d.ts +75 -19
  28. package/dist/default.d.ts.map +1 -1
  29. package/dist/default.js +76 -11
  30. package/dist/default.js.map +2 -2
  31. package/dist/default.js.zyzz.json +1 -1
  32. package/dist/internal/Condition.d.ts +1 -1
  33. package/dist/internal/Condition.d.ts.map +1 -1
  34. package/dist/internal/Configuration.js +2 -2
  35. package/dist/internal/Configuration.js.map +1 -1
  36. package/dist/internal/Query.d.ts +2 -2
  37. package/dist/internal/Query.d.ts.map +1 -1
  38. package/dist/internal/Query.js +1 -1
  39. package/dist/internal/Query.js.map +1 -1
  40. package/dist/internal/Theme.d.ts +4 -4
  41. package/dist/internal/Theme.d.ts.map +1 -1
  42. package/dist/internal/Theme.js +6 -6
  43. package/dist/internal/Theme.js.map +1 -1
  44. package/dist/internal/Token.d.ts +185 -2
  45. package/dist/internal/Token.d.ts.map +1 -1
  46. package/dist/internal/Token.js +236 -23
  47. package/dist/internal/Token.js.map +1 -1
  48. package/dist/internal/Value.d.ts +5 -5
  49. package/dist/internal/Value.d.ts.map +1 -1
  50. package/dist/internal/Value.js +18 -64
  51. package/dist/internal/Value.js.map +1 -1
  52. package/dist/internal/VariableSets.d.ts.map +1 -1
  53. package/dist/internal/VariableSets.js +9 -6
  54. package/dist/internal/VariableSets.js.map +1 -1
  55. package/dist/unplugin/index.d.ts.map +1 -1
  56. package/dist/unplugin/index.js +17 -4
  57. package/dist/unplugin/index.js.map +1 -1
  58. package/dist/vite/index.d.ts.map +1 -1
  59. package/dist/vite/index.js +112 -24
  60. package/dist/vite/index.js.map +1 -1
  61. package/dist/web/colorProfile.d.ts +9 -5
  62. package/dist/web/colorProfile.d.ts.map +1 -1
  63. package/dist/web/colorProfile.js.map +1 -1
  64. package/dist/web/counterStyle.d.ts +13 -9
  65. package/dist/web/counterStyle.d.ts.map +1 -1
  66. package/dist/web/counterStyle.js.map +1 -1
  67. package/dist/web/cssFunction.d.ts +19 -14
  68. package/dist/web/cssFunction.d.ts.map +1 -1
  69. package/dist/web/cssFunction.js.map +1 -1
  70. package/dist/web/fontFace.d.ts +6 -1
  71. package/dist/web/fontFace.d.ts.map +1 -1
  72. package/dist/web/fontFace.js.map +1 -1
  73. package/dist/web/fontFeatureValues.d.ts +11 -7
  74. package/dist/web/fontFeatureValues.d.ts.map +1 -1
  75. package/dist/web/fontFeatureValues.js.map +1 -1
  76. package/dist/web/fontPaletteValues.d.ts +8 -3
  77. package/dist/web/fontPaletteValues.d.ts.map +1 -1
  78. package/dist/web/fontPaletteValues.js.map +1 -1
  79. package/dist/web/internal/Context.d.ts +3 -3
  80. package/dist/web/internal/Context.d.ts.map +1 -1
  81. package/dist/web/keyframes.d.ts +7 -3
  82. package/dist/web/keyframes.d.ts.map +1 -1
  83. package/dist/web/keyframes.js.map +1 -1
  84. package/dist/web/page.d.ts +11 -4
  85. package/dist/web/page.d.ts.map +1 -1
  86. package/dist/web/page.js.map +1 -1
  87. package/dist/web/positionTry.d.ts +6 -1
  88. package/dist/web/positionTry.d.ts.map +1 -1
  89. package/dist/web/positionTry.js.map +1 -1
  90. package/dist/web/property.d.ts +10 -5
  91. package/dist/web/property.d.ts.map +1 -1
  92. package/dist/web/property.js.map +1 -1
  93. package/dist/web/viewTransition.d.ts +10 -6
  94. package/dist/web/viewTransition.d.ts.map +1 -1
  95. package/dist/web/viewTransition.js.map +1 -1
  96. package/package.json +1 -1
  97. package/src/Style.ts +8 -8
  98. package/src/Vars.ts +2 -3
  99. package/src/compiler/Source.ts +19 -0
  100. package/src/compiler/internal/Contract.ts +19 -8
  101. package/src/compiler/internal/Contributions.ts +734 -685
  102. package/src/compiler/internal/Expression.ts +57 -0
  103. package/src/compiler/internal/RecipeConditions.ts +2 -2
  104. package/src/compiler/internal/Themes.ts +2 -0
  105. package/src/default.ts +175 -30
  106. package/src/internal/Condition.ts +3 -3
  107. package/src/internal/Configuration.ts +2 -2
  108. package/src/internal/Query.ts +3 -3
  109. package/src/internal/Theme.ts +12 -12
  110. package/src/internal/Token.ts +253 -33
  111. package/src/internal/Value.ts +40 -88
  112. package/src/internal/VariableSets.ts +28 -25
  113. package/src/unplugin/index.ts +16 -3
  114. package/src/vite/index.ts +138 -32
  115. package/src/web/colorProfile.ts +25 -10
  116. package/src/web/counterStyle.ts +29 -14
  117. package/src/web/cssFunction.ts +47 -24
  118. package/src/web/fontFace.ts +16 -3
  119. package/src/web/fontFeatureValues.ts +35 -18
  120. package/src/web/fontPaletteValues.ts +25 -11
  121. package/src/web/internal/Context.ts +9 -3
  122. package/src/web/keyframes.ts +24 -9
  123. package/src/web/page.ts +22 -5
  124. package/src/web/positionTry.ts +20 -5
  125. package/src/web/property.ts +23 -9
  126. package/src/web/viewTransition.ts +28 -13
@@ -445,585 +445,621 @@ export function extract(scanned, tokens, starts, resolve = Expression.unwrap) {
445
445
  })),
446
446
  };
447
447
  }
448
+ function definitions(input, headers = []) {
449
+ const entries = Object.entries(record(input));
450
+ const groups = new Set(entries
451
+ .filter(([key]) => key === '@layer' ||
452
+ /^@(media|supports|container|layer)(?=[\t\n\r\f (/])/.test(key))
453
+ .map(([key]) => key));
454
+ const descriptors = entries.filter(([key]) => !groups.has(key));
455
+ const body = Object.fromEntries(descriptors);
456
+ if (!entries.length)
457
+ return [{ input: body, headers }];
458
+ // A complete descriptor block occupies its first descriptor's authored position.
459
+ return entries.flatMap(([header, child]) => groups.has(header)
460
+ ? definitions(child, [...headers, Condition.normalize(header)])
461
+ : header === descriptors[0]?.[0]
462
+ ? [{ input: body, headers }]
463
+ : []);
464
+ }
448
465
  for (const call of scanned.calls) {
449
- const before = result.length;
450
466
  try {
451
- const input = value(call.argument);
467
+ const argument = value(call.argument);
452
468
  if (call.name && !call.exported && !scanned.used.has(call.name))
453
469
  continue;
454
- if (call.kind === 'importCss') {
455
- const options = record(input);
456
- if (Object.keys(options).some((key) => !['layer', 'media', 'supports', 'url'].includes(key)) ||
457
- typeof options.url !== 'string' ||
458
- Object.entries(options).some(([key, value]) => key !== 'url' &&
459
- value !== undefined &&
460
- typeof value !== 'string' &&
461
- !(key === 'layer' && value === true)))
462
- throw new Error('Expected a stylesheet URL and static import conditions.');
463
- result.push({
464
- kind: 'import',
465
- url: options.url,
466
- ...(options.layer !== undefined
467
- ? { layer: options.layer }
468
- : {}),
469
- ...(options.supports !== undefined
470
- ? { supports: options.supports }
471
- : {}),
472
- ...(options.media !== undefined
473
- ? { media: options.media }
474
- : {}),
475
- });
476
- }
477
- else if (call.kind === 'namespace') {
478
- const options = record(input);
479
- if (Object.keys(options).some((key) => !['prefix', 'uri'].includes(key)) ||
480
- typeof options.uri !== 'string' ||
481
- (options.prefix !== undefined &&
482
- (typeof options.prefix !== 'string' ||
483
- Identifiers.read(options.prefix) === undefined)))
484
- throw new Error('Expected a namespace URI and optional identifier prefix.');
485
- result.push({
486
- kind: 'namespace',
487
- uri: options.uri,
488
- ...(options.prefix !== undefined
489
- ? { prefix: options.prefix }
490
- : {}),
491
- name: `z-n${scanned.namespace}-${call.start.toString(36)}`,
492
- });
493
- }
494
- else if (call.kind === 'customMedia') {
495
- if (typeof input !== 'string' && typeof input !== 'boolean')
496
- throw new Error('Expected a media query or boolean.');
497
- if (typeof input === 'string')
498
- Descriptors.media(input);
499
- result.push({ kind: 'custom-media', name: call.name, query: input });
500
- }
501
- else if (call.kind === 'cssFunction') {
502
- const options = record(input);
503
- if (Object.keys(options).some((key) => !['body', 'parameters', 'returns'].includes(key)) ||
504
- !Array.isArray(options.parameters) ||
505
- (options.returns !== undefined &&
506
- (typeof options.returns !== 'string' ||
507
- !FunctionSyntax.accepts(options.returns))))
508
- throw new Error('Expected CSS function parameters, body, and optional return syntax.');
509
- const names = new Set();
510
- const parameters = options.parameters.map((input) => {
511
- const parameter = record(input);
512
- if (Object.keys(parameter).some((key) => !['default', 'name', 'syntax'].includes(key)) ||
513
- typeof parameter.name !== 'string' ||
514
- !dashed(parameter.name) ||
515
- names.has(Identifiers.read(parameter.name)) ||
516
- (parameter.syntax !== undefined &&
517
- (typeof parameter.syntax !== 'string' ||
518
- !FunctionSyntax.accepts(parameter.syntax))) ||
519
- (parameter.default !== undefined &&
520
- typeof parameter.default !== 'string' &&
521
- typeof parameter.default !== 'number'))
522
- throw new Error('Expected unique CSS parameters with supported syntaxes and scalar defaults.');
523
- names.add(Identifiers.read(parameter.name));
524
- if (parameter.default !== undefined)
525
- Functions.value(String(parameter.syntax ?? '*'), String(parameter.default));
526
- return `${parameter.name}${parameter.syntax ? ` ${parameter.syntax === '*' ? 'type(*)' : parameter.syntax}` : ''}${parameter.default !== undefined ? `: ${parameter.default}` : ''}`;
527
- });
528
- function body(input) {
529
- return Object.entries(record(input)).flatMap(([key, value]) => {
530
- if (value === undefined)
531
- return [];
532
- if (/^@(media|supports|container) /.test(key))
533
- return [
534
- {
535
- kind: 'block',
536
- header: Condition.normalize(key),
537
- entries: body(value),
538
- },
539
- ];
540
- if ((key !== 'result' && !dashed(key)) ||
541
- (typeof value !== 'string' && typeof value !== 'number'))
542
- throw new Error('CSS function bodies accept result, local variables, and conditional groups.');
543
- Functions.value('*', String(value));
544
- return [{ kind: 'descriptor', name: key, value }];
470
+ const contextValue = call.context ? value(call.context) : undefined;
471
+ if (contextValue !== undefined &&
472
+ Object.keys(record(contextValue)).some((key) => key !== 'id'))
473
+ throw new Error('Unknown contribution context option. Use nested at-rule keys for enclosing groups.');
474
+ const inputs = [
475
+ 'importCss',
476
+ 'namespace',
477
+ 'customMedia',
478
+ 'layers',
479
+ 'global',
480
+ ].includes(call.kind)
481
+ ? [{ input: argument, headers: [] }]
482
+ : definitions(argument);
483
+ let functionSignature;
484
+ for (const { input, headers } of inputs) {
485
+ const before = result.length;
486
+ if (call.kind === 'importCss') {
487
+ const options = record(input);
488
+ if (Object.keys(options).some((key) => !['layer', 'media', 'supports', 'url'].includes(key)) ||
489
+ typeof options.url !== 'string' ||
490
+ Object.entries(options).some(([key, value]) => key !== 'url' &&
491
+ value !== undefined &&
492
+ typeof value !== 'string' &&
493
+ !(key === 'layer' && value === true)))
494
+ throw new Error('Expected a stylesheet URL and static import conditions.');
495
+ result.push({
496
+ kind: 'import',
497
+ url: options.url,
498
+ ...(options.layer !== undefined
499
+ ? { layer: options.layer }
500
+ : {}),
501
+ ...(options.supports !== undefined
502
+ ? { supports: options.supports }
503
+ : {}),
504
+ ...(options.media !== undefined
505
+ ? { media: options.media }
506
+ : {}),
545
507
  });
546
508
  }
547
- call.function.parameters =
548
- options.parameters;
549
- call.function.returns = (options.returns ?? '*');
550
- result.push({
551
- kind: 'block',
552
- header: `@function ${call.name}(${parameters.join(',')})${options.returns ? ` returns ${options.returns === '*' ? 'type(*)' : options.returns}` : ''}`,
553
- entries: body(options.body),
554
- });
555
- }
556
- else if (call.kind === 'layers') {
557
- if (!Array.isArray(input) ||
558
- input.some((value) => typeof value !== 'string'))
559
- throw new Error('Layers require a literal string list.');
560
- result.push({ kind: 'layers', names: input });
561
- }
562
- else if (call.kind === 'global') {
563
- for (const [selector, child] of Object.entries(record(input)))
509
+ else if (call.kind === 'namespace') {
510
+ const options = record(input);
511
+ if (Object.keys(options).some((key) => !['prefix', 'uri'].includes(key)) ||
512
+ typeof options.uri !== 'string' ||
513
+ (options.prefix !== undefined &&
514
+ (typeof options.prefix !== 'string' ||
515
+ Identifiers.read(options.prefix) === undefined)))
516
+ throw new Error('Expected a namespace URI and optional identifier prefix.');
564
517
  result.push({
565
- kind: 'rule',
566
- selector,
567
- style: grouping(selector) ? global(child) : style(child),
518
+ kind: 'namespace',
519
+ uri: options.uri,
520
+ ...(options.prefix !== undefined
521
+ ? { prefix: options.prefix }
522
+ : {}),
523
+ name: `z-n${scanned.namespace}-${call.start.toString(36)}`,
568
524
  });
569
- }
570
- else if (call.kind === 'fontFace') {
571
- const declarations = record(input);
572
- for (const key of Object.keys(declarations))
573
- if (declarations[key] === undefined &&
574
- key !== 'fontFamily' &&
575
- key !== 'src')
576
- delete declarations[key];
577
- const keys = [
578
- 'fontFeatureSettings',
579
- 'fontVariationSettings',
580
- 'fontFamily',
581
- 'src',
582
- 'fontDisplay',
583
- 'fontStyle',
584
- 'fontWeight',
585
- 'fontStretch',
586
- 'unicodeRange',
587
- 'sizeAdjust',
588
- 'ascentOverride',
589
- 'descentOverride',
590
- 'lineGapOverride',
591
- ];
592
- if (typeof declarations.fontFamily !== 'string' ||
593
- typeof declarations.src !== 'string' ||
594
- Object.entries(declarations).some(([key, value]) => !keys.includes(key) ||
595
- (typeof value !== 'string' && typeof value !== 'number')))
596
- throw new Error('Font faces require family/source and scalar supported descriptors.');
597
- for (const [name, value] of Object.entries(declarations))
598
- Descriptors.check({ rule: 'font-face', name, value: String(value) });
599
- result.push({
600
- kind: 'font-face',
601
- declarations: declarations,
602
- });
603
- }
604
- else if (call.kind === 'property') {
605
- const options = record(input);
606
- const names = typeof options.name === 'string'
607
- ? Identifiers.list(options.name, 'space')
608
- : undefined;
609
- if (Object.keys(options).some((key) => !['name', 'syntax', 'inherits', 'initialValue'].includes(key)) ||
610
- names?.length !== 1 ||
611
- !names[0].startsWith('--') ||
612
- names[0].length <= 2 ||
613
- typeof options.syntax !== 'string' ||
614
- typeof options.inherits !== 'boolean' ||
615
- (options.initialValue !== undefined &&
616
- typeof options.initialValue !== 'string' &&
617
- typeof options.initialValue !== 'number'))
618
- throw new Error('Expected a native property registration with scalar descriptors.');
619
- if (options.initialValue !== undefined)
620
- Properties.initial(String(options.initialValue));
621
- result.push({
622
- kind: 'property',
623
- name: options.name,
624
- syntax: options.syntax,
625
- inherits: options.inherits,
626
- ...(options.initialValue !== undefined
627
- ? { initialValue: options.initialValue }
628
- : {}),
629
- });
630
- }
631
- else if (call.kind === 'page') {
632
- const options = record(input);
633
- if (Object.keys(options).some((key) => !['descriptors', 'selector'].includes(key)) ||
634
- (options.selector !== undefined &&
635
- typeof options.selector !== 'string'))
636
- throw new Error('Expected page descriptors and an optional selector.');
637
- const margins = [
638
- 'top-left-corner',
639
- 'top-left',
640
- 'top-center',
641
- 'top-right',
642
- 'top-right-corner',
643
- 'bottom-left-corner',
644
- 'bottom-left',
645
- 'bottom-center',
646
- 'bottom-right',
647
- 'bottom-right-corner',
648
- 'left-top',
649
- 'left-middle',
650
- 'left-bottom',
651
- 'right-top',
652
- 'right-middle',
653
- 'right-bottom',
654
- ].map((name) => `@${name}`);
655
- const properties = /^(?!border(?:Collapse|Spacing)$)(?:background|border|font|margin|padding|outline)/;
656
- const names = [
657
- 'color',
658
- 'counterIncrement',
659
- 'counterReset',
660
- 'direction',
661
- 'height',
662
- 'letterSpacing',
663
- 'lineHeight',
664
- 'maxHeight',
665
- 'maxWidth',
666
- 'minHeight',
667
- 'minWidth',
668
- 'quotes',
669
- 'textAlign',
670
- 'textDecoration',
671
- 'textIndent',
672
- 'textTransform',
673
- 'visibility',
674
- 'whiteSpace',
675
- 'width',
676
- 'wordSpacing',
677
- ];
678
- function body(input, margin = false) {
679
- return Object.entries(record(input)).flatMap(([key, value]) => {
680
- if (value === undefined)
681
- return [];
682
- if (!margin && margins.includes(key))
683
- return [
684
- { kind: 'block', header: key, entries: body(value, true) },
685
- ];
686
- if (!margin &&
687
- ['bleed', 'marks', 'pageOrientation', 'size'].includes(key)) {
688
- if (typeof value !== 'string' && typeof value !== 'number')
689
- throw new Error('Expected a scalar page descriptor.');
690
- Descriptors.check({
691
- rule: 'page',
692
- name: key,
693
- value: String(value),
694
- });
525
+ }
526
+ else if (call.kind === 'customMedia') {
527
+ if (typeof input !== 'string' && typeof input !== 'boolean')
528
+ throw new Error('Expected a media query or boolean.');
529
+ if (typeof input === 'string')
530
+ Descriptors.media(input);
531
+ result.push({ kind: 'custom-media', name: call.name, query: input });
532
+ }
533
+ else if (call.kind === 'cssFunction') {
534
+ const options = record(input);
535
+ if (Object.keys(options).some((key) => !['body', 'parameters', 'returns'].includes(key)) ||
536
+ !Array.isArray(options.parameters) ||
537
+ (options.returns !== undefined &&
538
+ (typeof options.returns !== 'string' ||
539
+ !FunctionSyntax.accepts(options.returns))))
540
+ throw new Error('Expected CSS function parameters, body, and optional return syntax.');
541
+ const names = new Set();
542
+ const parameters = options.parameters.map((input) => {
543
+ const parameter = record(input);
544
+ if (Object.keys(parameter).some((key) => !['default', 'name', 'syntax'].includes(key)) ||
545
+ typeof parameter.name !== 'string' ||
546
+ !dashed(parameter.name) ||
547
+ names.has(Identifiers.read(parameter.name)) ||
548
+ (parameter.syntax !== undefined &&
549
+ (typeof parameter.syntax !== 'string' ||
550
+ !FunctionSyntax.accepts(parameter.syntax))) ||
551
+ (parameter.default !== undefined &&
552
+ typeof parameter.default !== 'string' &&
553
+ typeof parameter.default !== 'number'))
554
+ throw new Error('Expected unique CSS parameters with supported syntaxes and scalar defaults.');
555
+ names.add(Identifiers.read(parameter.name));
556
+ if (parameter.default !== undefined)
557
+ Functions.value(String(parameter.syntax ?? '*'), String(parameter.default));
558
+ return `${parameter.name}${parameter.syntax ? ` ${parameter.syntax === '*' ? 'type(*)' : parameter.syntax}` : ''}${parameter.default !== undefined ? `: ${parameter.default}` : ''}`;
559
+ });
560
+ function body(input) {
561
+ return Object.entries(record(input)).flatMap(([key, value]) => {
562
+ if (value === undefined)
563
+ return [];
564
+ if (/^@(media|supports|container) /.test(key))
565
+ return [
566
+ {
567
+ kind: 'block',
568
+ header: Condition.normalize(key),
569
+ entries: body(value),
570
+ },
571
+ ];
572
+ if ((key !== 'result' && !dashed(key)) ||
573
+ (typeof value !== 'string' && typeof value !== 'number'))
574
+ throw new Error('CSS function bodies accept result, local variables, and conditional groups.');
575
+ Functions.value('*', String(value));
576
+ return [{ kind: 'descriptor', name: key, value }];
577
+ });
578
+ }
579
+ const signature = JSON.stringify({
580
+ parameters: options.parameters.map((input) => {
581
+ const parameter = record(input);
695
582
  return [
696
- {
697
- kind: 'descriptor',
698
- name: key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`),
699
- value,
700
- },
583
+ parameter.name,
584
+ parameter.syntax ?? '*',
585
+ parameter.default,
701
586
  ];
702
- }
703
- if (!properties.test(key) &&
704
- !names.includes(key) &&
705
- !(margin &&
706
- [
707
- 'content',
708
- 'overflow',
709
- 'unicodeBidi',
710
- 'verticalAlign',
711
- 'zIndex',
712
- ].includes(key)))
713
- throw new Error('Unsupported page or page-margin declaration.');
714
- return [{ kind: 'style', style: style({ [key]: value }) }];
587
+ }),
588
+ returns: options.returns ?? '*',
589
+ });
590
+ if (functionSignature !== undefined &&
591
+ functionSignature !== signature)
592
+ throw new Error('Grouped CSS function definitions must use the same parameters and return syntax.');
593
+ functionSignature = signature;
594
+ call.function.parameters =
595
+ options.parameters;
596
+ call.function.returns = (options.returns ??
597
+ '*');
598
+ result.push({
599
+ kind: 'block',
600
+ header: `@function ${call.name}(${parameters.join(',')})${options.returns ? ` returns ${options.returns === '*' ? 'type(*)' : options.returns}` : ''}`,
601
+ entries: body(options.body),
715
602
  });
716
603
  }
717
- result.push({
718
- kind: 'block',
719
- header: Condition.normalize(`@page${options.selector ? ` ${options.selector}` : ''}`),
720
- entries: body(options.descriptors),
721
- });
722
- }
723
- else if (call.kind === 'fontFeatureValues') {
724
- const options = record(input);
725
- if (Object.keys(options).some((key) => !['families', 'features', 'fontDisplay'].includes(key)))
726
- throw new Error('Unknown font-feature-values option.');
727
- const families = options.families;
728
- if (typeof families !== 'string' &&
729
- (!Array.isArray(families) ||
730
- !families.length ||
731
- families.some((value) => typeof value !== 'string')))
732
- throw new Error('Expected a font family list.');
733
- const familyList = Array.isArray(families)
734
- ? families
735
- .map((value) => '"' +
736
- Array.from(value, (char) => {
737
- const code = char.codePointAt(0);
738
- return code < 32 ||
739
- code === 127 ||
740
- char === '"' ||
741
- char === '\\'
742
- ? `\\${code.toString(16)} `
743
- : char;
744
- }).join('') +
745
- '"')
746
- .join(',')
747
- : families;
748
- // Validate the native prelude before shielding newer body descriptors.
749
- Lightning.transform({
750
- filename: 'font-feature-values.css',
751
- code: new TextEncoder().encode(`@font-feature-values ${familyList} {}`),
752
- });
753
- const entries = [];
754
- for (const key of Object.keys(options)) {
755
- if (key === 'fontDisplay' && options.fontDisplay !== undefined) {
756
- if (typeof options.fontDisplay !== 'string' ||
757
- !['auto', 'block', 'fallback', 'optional', 'swap'].includes(Identifiers.list(options.fontDisplay, 'space')
758
- ?.join(' ')
759
- .replace(/[A-Z]/g, (letter) => letter.toLowerCase()) ?? ''))
760
- throw new Error('Invalid font display descriptor.');
761
- entries.push({
762
- kind: 'descriptor',
763
- name: 'font-display',
764
- value: options.fontDisplay,
604
+ else if (call.kind === 'layers') {
605
+ if (!Array.isArray(input) ||
606
+ input.some((value) => typeof value !== 'string'))
607
+ throw new Error('Layers require a literal string list.');
608
+ result.push({ kind: 'layers', names: input });
609
+ }
610
+ else if (call.kind === 'global') {
611
+ for (const [selector, child] of Object.entries(record(input)))
612
+ result.push({
613
+ kind: 'rule',
614
+ selector,
615
+ style: grouping(selector) ? global(child) : style(child),
765
616
  });
766
- }
767
- if (key !== 'features')
768
- continue;
769
- for (const [header, input] of Object.entries(record(options.features))) {
770
- if (input === undefined)
771
- continue;
772
- if (![
773
- '@annotation',
774
- '@character-variant',
775
- '@ornaments',
776
- '@styleset',
777
- '@stylistic',
778
- '@swash',
779
- ].includes(header))
780
- throw new Error('Unknown font feature block.');
781
- const declarations = Object.entries(record(input)).map(([name, value]) => {
782
- const values = Array.isArray(value) ? value : [value];
783
- const maximum = header === '@styleset'
784
- ? Infinity
785
- : header === '@character-variant'
786
- ? 2
787
- : 1;
788
- if (!identifier(name) ||
789
- !values.length ||
790
- values.length > maximum ||
791
- values.some((value) => typeof value !== 'number' ||
792
- !Number.isSafeInteger(value) ||
793
- value < 0))
794
- throw new Error('Expected feature aliases with nonnegative integer indices.');
795
- return { kind: 'descriptor', name, value: values.join(' ') };
617
+ }
618
+ else if (call.kind === 'fontFace') {
619
+ const declarations = record(input);
620
+ for (const key of Object.keys(declarations))
621
+ if (declarations[key] === undefined &&
622
+ key !== 'fontFamily' &&
623
+ key !== 'src')
624
+ delete declarations[key];
625
+ const keys = [
626
+ 'fontFeatureSettings',
627
+ 'fontVariationSettings',
628
+ 'fontFamily',
629
+ 'src',
630
+ 'fontDisplay',
631
+ 'fontStyle',
632
+ 'fontWeight',
633
+ 'fontStretch',
634
+ 'unicodeRange',
635
+ 'sizeAdjust',
636
+ 'ascentOverride',
637
+ 'descentOverride',
638
+ 'lineGapOverride',
639
+ ];
640
+ if (typeof declarations.fontFamily !== 'string' ||
641
+ typeof declarations.src !== 'string' ||
642
+ Object.entries(declarations).some(([key, value]) => !keys.includes(key) ||
643
+ (typeof value !== 'string' && typeof value !== 'number')))
644
+ throw new Error('Font faces require family/source and scalar supported descriptors.');
645
+ for (const [name, value] of Object.entries(declarations))
646
+ Descriptors.check({ rule: 'font-face', name, value: String(value) });
647
+ result.push({
648
+ kind: 'font-face',
649
+ declarations: declarations,
650
+ });
651
+ }
652
+ else if (call.kind === 'property') {
653
+ const options = record(input);
654
+ const names = typeof options.name === 'string'
655
+ ? Identifiers.list(options.name, 'space')
656
+ : undefined;
657
+ if (Object.keys(options).some((key) => !['name', 'syntax', 'inherits', 'initialValue'].includes(key)) ||
658
+ names?.length !== 1 ||
659
+ !names[0].startsWith('--') ||
660
+ names[0].length <= 2 ||
661
+ typeof options.syntax !== 'string' ||
662
+ typeof options.inherits !== 'boolean' ||
663
+ (options.initialValue !== undefined &&
664
+ typeof options.initialValue !== 'string' &&
665
+ typeof options.initialValue !== 'number'))
666
+ throw new Error('Expected a native property registration with scalar descriptors.');
667
+ if (options.initialValue !== undefined)
668
+ Properties.initial(String(options.initialValue));
669
+ result.push({
670
+ kind: 'property',
671
+ name: options.name,
672
+ syntax: options.syntax,
673
+ inherits: options.inherits,
674
+ ...(options.initialValue !== undefined
675
+ ? { initialValue: options.initialValue }
676
+ : {}),
677
+ });
678
+ }
679
+ else if (call.kind === 'page') {
680
+ const options = record(input);
681
+ if (Object.keys(options).some((key) => !['descriptors', 'selector'].includes(key)) ||
682
+ (options.selector !== undefined &&
683
+ typeof options.selector !== 'string'))
684
+ throw new Error('Expected page descriptors and an optional selector.');
685
+ const margins = [
686
+ 'top-left-corner',
687
+ 'top-left',
688
+ 'top-center',
689
+ 'top-right',
690
+ 'top-right-corner',
691
+ 'bottom-left-corner',
692
+ 'bottom-left',
693
+ 'bottom-center',
694
+ 'bottom-right',
695
+ 'bottom-right-corner',
696
+ 'left-top',
697
+ 'left-middle',
698
+ 'left-bottom',
699
+ 'right-top',
700
+ 'right-middle',
701
+ 'right-bottom',
702
+ ].map((name) => `@${name}`);
703
+ const properties = /^(?!border(?:Collapse|Spacing)$)(?:background|border|font|margin|padding|outline)/;
704
+ const names = [
705
+ 'color',
706
+ 'counterIncrement',
707
+ 'counterReset',
708
+ 'direction',
709
+ 'height',
710
+ 'letterSpacing',
711
+ 'lineHeight',
712
+ 'maxHeight',
713
+ 'maxWidth',
714
+ 'minHeight',
715
+ 'minWidth',
716
+ 'quotes',
717
+ 'textAlign',
718
+ 'textDecoration',
719
+ 'textIndent',
720
+ 'textTransform',
721
+ 'visibility',
722
+ 'whiteSpace',
723
+ 'width',
724
+ 'wordSpacing',
725
+ ];
726
+ function body(input, margin = false) {
727
+ return Object.entries(record(input)).flatMap(([key, value]) => {
728
+ if (value === undefined)
729
+ return [];
730
+ if (!margin && margins.includes(key))
731
+ return [
732
+ { kind: 'block', header: key, entries: body(value, true) },
733
+ ];
734
+ if (!margin &&
735
+ ['bleed', 'marks', 'pageOrientation', 'size'].includes(key)) {
736
+ if (typeof value !== 'string' && typeof value !== 'number')
737
+ throw new Error('Expected a scalar page descriptor.');
738
+ Descriptors.check({
739
+ rule: 'page',
740
+ name: key,
741
+ value: String(value),
742
+ });
743
+ return [
744
+ {
745
+ kind: 'descriptor',
746
+ name: key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`),
747
+ value,
748
+ },
749
+ ];
750
+ }
751
+ if (!properties.test(key) &&
752
+ !names.includes(key) &&
753
+ !(margin &&
754
+ [
755
+ 'content',
756
+ 'overflow',
757
+ 'unicodeBidi',
758
+ 'verticalAlign',
759
+ 'zIndex',
760
+ ].includes(key)))
761
+ throw new Error('Unsupported page or page-margin declaration.');
762
+ return [{ kind: 'style', style: style({ [key]: value }) }];
796
763
  });
797
- entries.push({ kind: 'block', header, entries: declarations });
798
764
  }
765
+ result.push({
766
+ kind: 'block',
767
+ header: Condition.normalize(`@page${options.selector ? ` ${options.selector}` : ''}`),
768
+ entries: body(options.descriptors),
769
+ });
799
770
  }
800
- result.push({
801
- kind: 'block',
802
- header: `@font-feature-values ${familyList}`,
803
- entries,
804
- });
805
- }
806
- else if (call.kind === 'viewTransition') {
807
- const entries = Object.entries(record(input)).flatMap(([key, value]) => {
808
- if (value === undefined)
809
- return [];
810
- if ((key !== 'navigation' && key !== 'types') ||
811
- typeof value !== 'string' ||
812
- (key === 'navigation' &&
813
- !['auto', 'none'].includes(Identifiers.list(value, 'space')?.join(' ').toLowerCase() ??
814
- '')))
815
- throw new Error('Expected navigation or types view-transition descriptors.');
816
- if (key === 'types') {
817
- const names = Identifiers.list(value, 'space');
818
- if (!names ||
819
- names.some((name) => [
820
- 'default',
821
- 'inherit',
822
- 'initial',
823
- 'revert',
824
- 'revert-layer',
825
- 'unset',
826
- ].includes(name.toLowerCase()) ||
827
- (name.toLowerCase() === 'none' && names.length !== 1)))
828
- throw new Error('Expected none or a list of view-transition custom identifiers.');
829
- }
830
- Functions.value('*', String(value));
831
- return [{ kind: 'descriptor', name: key, value }];
832
- });
833
- result.push({ kind: 'block', header: '@view-transition', entries });
834
- }
835
- else if (call.kind === 'positionTry') {
836
- const declarations = record(input);
837
- const keys = [
838
- 'alignSelf',
839
- 'blockSize',
840
- 'bottom',
841
- 'height',
842
- 'inlineSize',
843
- 'inset',
844
- 'insetBlock',
845
- 'insetBlockEnd',
846
- 'insetBlockStart',
847
- 'insetInline',
848
- 'insetInlineEnd',
849
- 'insetInlineStart',
850
- 'justifySelf',
851
- 'left',
852
- 'margin',
853
- 'marginBlock',
854
- 'marginBlockEnd',
855
- 'marginBlockStart',
856
- 'marginBottom',
857
- 'marginInline',
858
- 'marginInlineEnd',
859
- 'marginInlineStart',
860
- 'marginLeft',
861
- 'marginRight',
862
- 'marginTop',
863
- 'maxBlockSize',
864
- 'maxHeight',
865
- 'maxInlineSize',
866
- 'maxWidth',
867
- 'minBlockSize',
868
- 'minHeight',
869
- 'minInlineSize',
870
- 'minWidth',
871
- 'placeSelf',
872
- 'positionAnchor',
873
- 'positionArea',
874
- 'right',
875
- 'top',
876
- 'width',
877
- ];
878
- if (Object.keys(declarations).some((key) => !keys.includes(key)))
879
- throw new Error('Unsupported position-try declaration.');
880
- const block = style(declarations);
881
- if (block.rules || block.declarations.some((value) => value.important))
882
- throw new Error('Position-try forbids nested rules and important declarations.');
883
- result.push({
884
- kind: 'rule',
885
- selector: `@position-try ${call.name}`,
886
- style: block,
887
- });
888
- }
889
- else if (call.kind === 'colorProfile' ||
890
- call.kind === 'counterStyle' ||
891
- call.kind === 'fontPaletteValues') {
892
- const descriptors = {
893
- colorProfile: {
894
- rule: 'color-profile',
895
- keys: ['components', 'renderingIntent', 'src'],
896
- required: ['src'],
897
- },
898
- counterStyle: {
899
- rule: 'counter-style',
900
- keys: [
901
- 'additiveSymbols',
902
- 'fallback',
903
- 'negative',
904
- 'pad',
905
- 'prefix',
906
- 'range',
907
- 'speakAs',
908
- 'suffix',
909
- 'symbols',
910
- 'system',
911
- ],
912
- required: [],
913
- },
914
- fontPaletteValues: {
915
- rule: 'font-palette-values',
916
- keys: ['basePalette', 'fontFamily', 'overrideColors'],
917
- required: ['fontFamily'],
918
- },
919
- };
920
- const definition = descriptors[call.kind];
921
- const declarations = record(input);
922
- for (const key of Object.keys(declarations))
923
- if (declarations[key] === undefined)
924
- delete declarations[key];
925
- if (definition.required.some((key) => typeof declarations[key] !== 'string') ||
926
- Object.entries(declarations).some(([key, value]) => !definition.keys.includes(key) ||
927
- (typeof value !== 'string' &&
928
- !(key === 'basePalette' &&
929
- typeof value === 'number' &&
930
- Number.isSafeInteger(value) &&
931
- value >= 0))))
932
- throw new Error('Expected supported scalar descriptors and required fields.');
933
- if (call.kind === 'colorProfile') {
934
- if (!Identifiers.url(declarations.src))
935
- throw new Error('Color-profile src requires one URL.');
936
- if (typeof declarations.components === 'string') {
937
- const names = Identifiers.list(declarations.components, 'comma');
938
- if (!names || names.some((name) => name.toLowerCase() === 'none'))
939
- throw new Error('Color-profile components require comma-separated identifiers other than none.');
940
- }
941
- if (typeof declarations.renderingIntent === 'string') {
942
- const names = Identifiers.list(declarations.renderingIntent, 'space');
943
- if (names?.length !== 1 ||
944
- ![
945
- 'absolute-colorimetric',
946
- 'relative-colorimetric',
947
- 'perceptual',
948
- 'saturation',
949
- ].includes(names[0].toLowerCase()))
950
- throw new Error('Invalid color-profile rendering intent.');
771
+ else if (call.kind === 'fontFeatureValues') {
772
+ const options = record(input);
773
+ if (Object.keys(options).some((key) => !['families', 'features', 'fontDisplay'].includes(key)))
774
+ throw new Error('Unknown font-feature-values option.');
775
+ const families = options.families;
776
+ if (typeof families !== 'string' &&
777
+ (!Array.isArray(families) ||
778
+ !families.length ||
779
+ families.some((value) => typeof value !== 'string')))
780
+ throw new Error('Expected a font family list.');
781
+ const familyList = Array.isArray(families)
782
+ ? families
783
+ .map((value) => '"' +
784
+ Array.from(value, (char) => {
785
+ const code = char.codePointAt(0);
786
+ return code < 32 ||
787
+ code === 127 ||
788
+ char === '"' ||
789
+ char === '\\'
790
+ ? `\\${code.toString(16)} `
791
+ : char;
792
+ }).join('') +
793
+ '"')
794
+ .join(',')
795
+ : families;
796
+ // Validate the native prelude before shielding newer body descriptors.
797
+ Lightning.transform({
798
+ filename: 'font-feature-values.css',
799
+ code: new TextEncoder().encode(`@font-feature-values ${familyList} {}`),
800
+ });
801
+ const entries = [];
802
+ for (const key of Object.keys(options)) {
803
+ if (key === 'fontDisplay' && options.fontDisplay !== undefined) {
804
+ if (typeof options.fontDisplay !== 'string' ||
805
+ !['auto', 'block', 'fallback', 'optional', 'swap'].includes(Identifiers.list(options.fontDisplay, 'space')
806
+ ?.join(' ')
807
+ .replace(/[A-Z]/g, (letter) => letter.toLowerCase()) ?? ''))
808
+ throw new Error('Invalid font display descriptor.');
809
+ entries.push({
810
+ kind: 'descriptor',
811
+ name: 'font-display',
812
+ value: options.fontDisplay,
813
+ });
814
+ }
815
+ if (key !== 'features')
816
+ continue;
817
+ for (const [header, input] of Object.entries(record(options.features))) {
818
+ if (input === undefined)
819
+ continue;
820
+ if (![
821
+ '@annotation',
822
+ '@character-variant',
823
+ '@ornaments',
824
+ '@styleset',
825
+ '@stylistic',
826
+ '@swash',
827
+ ].includes(header))
828
+ throw new Error('Unknown font feature block.');
829
+ const declarations = Object.entries(record(input)).map(([name, value]) => {
830
+ const values = Array.isArray(value) ? value : [value];
831
+ const maximum = header === '@styleset'
832
+ ? Infinity
833
+ : header === '@character-variant'
834
+ ? 2
835
+ : 1;
836
+ if (!identifier(name) ||
837
+ !values.length ||
838
+ values.length > maximum ||
839
+ values.some((value) => typeof value !== 'number' ||
840
+ !Number.isSafeInteger(value) ||
841
+ value < 0))
842
+ throw new Error('Expected feature aliases with nonnegative integer indices.');
843
+ return { kind: 'descriptor', name, value: values.join(' ') };
844
+ });
845
+ entries.push({ kind: 'block', header, entries: declarations });
846
+ }
951
847
  }
848
+ result.push({
849
+ kind: 'block',
850
+ header: `@font-feature-values ${familyList}`,
851
+ entries,
852
+ });
853
+ }
854
+ else if (call.kind === 'viewTransition') {
855
+ const entries = Object.entries(record(input)).flatMap(([key, value]) => {
856
+ if (value === undefined)
857
+ return [];
858
+ if ((key !== 'navigation' && key !== 'types') ||
859
+ typeof value !== 'string' ||
860
+ (key === 'navigation' &&
861
+ !['auto', 'none'].includes(Identifiers.list(value, 'space')?.join(' ').toLowerCase() ??
862
+ '')))
863
+ throw new Error('Expected navigation or types view-transition descriptors.');
864
+ if (key === 'types') {
865
+ const names = Identifiers.list(value, 'space');
866
+ if (!names ||
867
+ names.some((name) => [
868
+ 'default',
869
+ 'inherit',
870
+ 'initial',
871
+ 'revert',
872
+ 'revert-layer',
873
+ 'unset',
874
+ ].includes(name.toLowerCase()) ||
875
+ (name.toLowerCase() === 'none' && names.length !== 1)))
876
+ throw new Error('Expected none or a list of view-transition custom identifiers.');
877
+ }
878
+ Functions.value('*', String(value));
879
+ return [{ kind: 'descriptor', name: key, value }];
880
+ });
881
+ result.push({ kind: 'block', header: '@view-transition', entries });
882
+ }
883
+ else if (call.kind === 'positionTry') {
884
+ const declarations = record(input);
885
+ const keys = [
886
+ 'alignSelf',
887
+ 'blockSize',
888
+ 'bottom',
889
+ 'height',
890
+ 'inlineSize',
891
+ 'inset',
892
+ 'insetBlock',
893
+ 'insetBlockEnd',
894
+ 'insetBlockStart',
895
+ 'insetInline',
896
+ 'insetInlineEnd',
897
+ 'insetInlineStart',
898
+ 'justifySelf',
899
+ 'left',
900
+ 'margin',
901
+ 'marginBlock',
902
+ 'marginBlockEnd',
903
+ 'marginBlockStart',
904
+ 'marginBottom',
905
+ 'marginInline',
906
+ 'marginInlineEnd',
907
+ 'marginInlineStart',
908
+ 'marginLeft',
909
+ 'marginRight',
910
+ 'marginTop',
911
+ 'maxBlockSize',
912
+ 'maxHeight',
913
+ 'maxInlineSize',
914
+ 'maxWidth',
915
+ 'minBlockSize',
916
+ 'minHeight',
917
+ 'minInlineSize',
918
+ 'minWidth',
919
+ 'placeSelf',
920
+ 'positionAnchor',
921
+ 'positionArea',
922
+ 'right',
923
+ 'top',
924
+ 'width',
925
+ ];
926
+ if (Object.keys(declarations).some((key) => !keys.includes(key)))
927
+ throw new Error('Unsupported position-try declaration.');
928
+ const block = style(declarations);
929
+ if (block.rules ||
930
+ block.declarations.some((value) => value.important))
931
+ throw new Error('Position-try forbids nested rules and important declarations.');
932
+ result.push({
933
+ kind: 'rule',
934
+ selector: `@position-try ${call.name}`,
935
+ style: block,
936
+ });
952
937
  }
953
- if (call.kind === 'counterStyle') {
954
- const system = typeof declarations.system === 'string'
955
- ? Lexical.normalize(declarations.system)
956
- .trim()
957
- .replace(/[A-Z]/g, (letter) => letter.toLowerCase())
958
- : 'symbolic';
959
- try {
960
- Descriptors.check({
938
+ else if (call.kind === 'colorProfile' ||
939
+ call.kind === 'counterStyle' ||
940
+ call.kind === 'fontPaletteValues') {
941
+ const descriptors = {
942
+ colorProfile: {
943
+ rule: 'color-profile',
944
+ keys: ['components', 'renderingIntent', 'src'],
945
+ required: ['src'],
946
+ },
947
+ counterStyle: {
961
948
  rule: 'counter-style',
962
- name: 'system',
963
- value: typeof declarations.system === 'string'
964
- ? declarations.system
965
- : 'symbolic',
966
- });
949
+ keys: [
950
+ 'additiveSymbols',
951
+ 'fallback',
952
+ 'negative',
953
+ 'pad',
954
+ 'prefix',
955
+ 'range',
956
+ 'speakAs',
957
+ 'suffix',
958
+ 'symbols',
959
+ 'system',
960
+ ],
961
+ required: [],
962
+ },
963
+ fontPaletteValues: {
964
+ rule: 'font-palette-values',
965
+ keys: ['basePalette', 'fontFamily', 'overrideColors'],
966
+ required: ['fontFamily'],
967
+ },
968
+ };
969
+ const definition = descriptors[call.kind];
970
+ const declarations = record(input);
971
+ for (const key of Object.keys(declarations))
972
+ if (declarations[key] === undefined)
973
+ delete declarations[key];
974
+ if (definition.required.some((key) => typeof declarations[key] !== 'string') ||
975
+ Object.entries(declarations).some(([key, value]) => !definition.keys.includes(key) ||
976
+ (typeof value !== 'string' &&
977
+ !(key === 'basePalette' &&
978
+ typeof value === 'number' &&
979
+ Number.isSafeInteger(value) &&
980
+ value >= 0))))
981
+ throw new Error('Expected supported scalar descriptors and required fields.');
982
+ if (call.kind === 'colorProfile') {
983
+ if (!Identifiers.url(declarations.src))
984
+ throw new Error('Color-profile src requires one URL.');
985
+ if (typeof declarations.components === 'string') {
986
+ const names = Identifiers.list(declarations.components, 'comma');
987
+ if (!names || names.some((name) => name.toLowerCase() === 'none'))
988
+ throw new Error('Color-profile components require comma-separated identifiers other than none.');
989
+ }
990
+ if (typeof declarations.renderingIntent === 'string') {
991
+ const names = Identifiers.list(declarations.renderingIntent, 'space');
992
+ if (names?.length !== 1 ||
993
+ ![
994
+ 'absolute-colorimetric',
995
+ 'relative-colorimetric',
996
+ 'perceptual',
997
+ 'saturation',
998
+ ].includes(names[0].toLowerCase()))
999
+ throw new Error('Invalid color-profile rendering intent.');
1000
+ }
967
1001
  }
968
- catch {
969
- throw new Error('Expected a supported counter system, with an integer after fixed.');
1002
+ if (call.kind === 'counterStyle') {
1003
+ const system = typeof declarations.system === 'string'
1004
+ ? Lexical.normalize(declarations.system)
1005
+ .trim()
1006
+ .replace(/[A-Z]/g, (letter) => letter.toLowerCase())
1007
+ : 'symbolic';
1008
+ try {
1009
+ Descriptors.check({
1010
+ rule: 'counter-style',
1011
+ name: 'system',
1012
+ value: typeof declarations.system === 'string'
1013
+ ? declarations.system
1014
+ : 'symbolic',
1015
+ });
1016
+ }
1017
+ catch {
1018
+ throw new Error('Expected a supported counter system, with an integer after fixed.');
1019
+ }
1020
+ const required = system === 'additive'
1021
+ ? 'additiveSymbols'
1022
+ : system.startsWith('extends ')
1023
+ ? undefined
1024
+ : 'symbols';
1025
+ if (required &&
1026
+ (typeof declarations[required] !== 'string' ||
1027
+ !declarations[required].trim()))
1028
+ throw new Error('The counter system requires symbols or additiveSymbols.');
970
1029
  }
971
- const required = system === 'additive'
972
- ? 'additiveSymbols'
973
- : system.startsWith('extends ')
974
- ? undefined
975
- : 'symbols';
976
- if (required &&
977
- (typeof declarations[required] !== 'string' ||
978
- !declarations[required].trim()))
979
- throw new Error('The counter system requires symbols or additiveSymbols.');
1030
+ if (call.kind !== 'colorProfile')
1031
+ for (const [name, value] of Object.entries(declarations))
1032
+ Descriptors.check({
1033
+ rule: definition.rule,
1034
+ name,
1035
+ value: String(value),
1036
+ });
1037
+ result.push({
1038
+ kind: 'descriptor',
1039
+ rule: definition.rule,
1040
+ name: call.name,
1041
+ declarations: declarations,
1042
+ });
1043
+ }
1044
+ else {
1045
+ const frames = Object.entries(record(input)).map(([stop, input]) => {
1046
+ if (!Keyframes.accepts(stop))
1047
+ throw new Error('Keyframe stops require from, to, 0–100% offsets, or named timeline percentages.');
1048
+ const frame = style(input);
1049
+ if (frame.rules ||
1050
+ frame.declarations.some((value) => value.important))
1051
+ throw new Error('Keyframes forbid nested rules and important declarations.');
1052
+ return { stop, style: frame };
1053
+ });
1054
+ if (call.exported || scanned.used.has(call.name))
1055
+ result.push({ kind: 'keyframes', name: call.name, frames });
1056
+ }
1057
+ for (let index = before; index < result.length; index++) {
1058
+ if (headers.length)
1059
+ result[index] = { ...result[index], within: headers };
1060
+ starts?.push(call.start);
980
1061
  }
981
- if (call.kind !== 'colorProfile')
982
- for (const [name, value] of Object.entries(declarations))
983
- Descriptors.check({
984
- rule: definition.rule,
985
- name,
986
- value: String(value),
987
- });
988
- result.push({
989
- kind: 'descriptor',
990
- rule: definition.rule,
991
- name: call.name,
992
- declarations: declarations,
993
- });
994
- }
995
- else {
996
- const frames = Object.entries(record(input)).map(([stop, input]) => {
997
- if (!Keyframes.accepts(stop))
998
- throw new Error('Keyframe stops require from, to, 0–100% offsets, or named timeline percentages.');
999
- const frame = style(input);
1000
- if (frame.rules ||
1001
- frame.declarations.some((value) => value.important))
1002
- throw new Error('Keyframes forbid nested rules and important declarations.');
1003
- return { stop, style: frame };
1004
- });
1005
- if (call.exported || scanned.used.has(call.name))
1006
- result.push({ kind: 'keyframes', name: call.name, frames });
1007
- }
1008
- const contextValue = call.context ? value(call.context) : undefined;
1009
- if (contextValue !== undefined) {
1010
- const context = record(contextValue);
1011
- if (Object.keys(context).some((key) => !['id', 'within'].includes(key)))
1012
- throw new Error('Unknown contribution context option.');
1013
- if (call.kind === 'customMedia' && 'within' in context)
1014
- throw new Error('Custom media definitions do not accept a within context.');
1015
- const within = context.within ?? [];
1016
- if (!Array.isArray(within) ||
1017
- within.some((header) => typeof header !== 'string' ||
1018
- !(header === '@layer' ||
1019
- /^@(media|supports|container|layer)(?=[\t\n\r\f (/])/.test(header))))
1020
- throw new Error('Expected enclosing conditional or layer headers.');
1021
- const headers = within.map((header) => Condition.normalize(header));
1022
- for (let index = before; index < result.length; index++)
1023
- result[index] = { ...result[index], within: headers };
1024
1062
  }
1025
- for (let index = before; index < result.length; index++)
1026
- starts?.push(call.start);
1027
1063
  }
1028
1064
  catch (error) {
1029
1065
  throw new Themes.InvalidError(error.message, call);