@jesscss/core 2.0.0-alpha.2 → 2.0.0-alpha.5

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 (106) hide show
  1. package/README.md +3 -1
  2. package/lib/tree/ampersand.d.ts +4 -0
  3. package/lib/tree/ampersand.d.ts.map +1 -1
  4. package/lib/tree/ampersand.js +51 -1
  5. package/lib/tree/ampersand.js.map +1 -1
  6. package/lib/tree/any.d.ts +1 -1
  7. package/lib/tree/any.d.ts.map +1 -1
  8. package/lib/tree/any.js +5 -2
  9. package/lib/tree/any.js.map +1 -1
  10. package/lib/tree/bool.d.ts +1 -0
  11. package/lib/tree/bool.d.ts.map +1 -1
  12. package/lib/tree/bool.js +5 -1
  13. package/lib/tree/bool.js.map +1 -1
  14. package/lib/tree/color.d.ts.map +1 -1
  15. package/lib/tree/color.js +2 -2
  16. package/lib/tree/color.js.map +1 -1
  17. package/lib/tree/combinator.d.ts +1 -0
  18. package/lib/tree/combinator.d.ts.map +1 -1
  19. package/lib/tree/combinator.js +5 -1
  20. package/lib/tree/combinator.js.map +1 -1
  21. package/lib/tree/comment.d.ts.map +1 -1
  22. package/lib/tree/comment.js +2 -1
  23. package/lib/tree/comment.js.map +1 -1
  24. package/lib/tree/declaration-var.d.ts +0 -1
  25. package/lib/tree/declaration-var.d.ts.map +1 -1
  26. package/lib/tree/declaration-var.js +1 -1
  27. package/lib/tree/declaration-var.js.map +1 -1
  28. package/lib/tree/declaration.d.ts.map +1 -1
  29. package/lib/tree/declaration.js +5 -8
  30. package/lib/tree/declaration.js.map +1 -1
  31. package/lib/tree/dimension.d.ts +1 -0
  32. package/lib/tree/dimension.d.ts.map +1 -1
  33. package/lib/tree/dimension.js +5 -2
  34. package/lib/tree/dimension.js.map +1 -1
  35. package/lib/tree/expression.d.ts +1 -0
  36. package/lib/tree/expression.d.ts.map +1 -1
  37. package/lib/tree/expression.js +5 -1
  38. package/lib/tree/expression.js.map +1 -1
  39. package/lib/tree/extend-list.d.ts +2 -2
  40. package/lib/tree/extend-list.d.ts.map +1 -1
  41. package/lib/tree/extend-list.js +5 -2
  42. package/lib/tree/extend-list.js.map +1 -1
  43. package/lib/tree/extend.d.ts +2 -2
  44. package/lib/tree/extend.d.ts.map +1 -1
  45. package/lib/tree/extend.js +6 -2
  46. package/lib/tree/extend.js.map +1 -1
  47. package/lib/tree/import-style.d.ts.map +1 -1
  48. package/lib/tree/import-style.js +11 -1
  49. package/lib/tree/import-style.js.map +1 -1
  50. package/lib/tree/nil.d.ts +0 -1
  51. package/lib/tree/nil.d.ts.map +1 -1
  52. package/lib/tree/nil.js +2 -3
  53. package/lib/tree/nil.js.map +1 -1
  54. package/lib/tree/node-base.d.ts +2 -0
  55. package/lib/tree/node-base.d.ts.map +1 -1
  56. package/lib/tree/node-base.js +46 -0
  57. package/lib/tree/node-base.js.map +1 -1
  58. package/lib/tree/paren.d.ts +2 -1
  59. package/lib/tree/paren.d.ts.map +1 -1
  60. package/lib/tree/paren.js +7 -1
  61. package/lib/tree/paren.js.map +1 -1
  62. package/lib/tree/quoted.d.ts +2 -1
  63. package/lib/tree/quoted.d.ts.map +1 -1
  64. package/lib/tree/quoted.js +10 -1
  65. package/lib/tree/quoted.js.map +1 -1
  66. package/lib/tree/reference.d.ts.map +1 -1
  67. package/lib/tree/reference.js.map +1 -1
  68. package/lib/tree/rules.d.ts +7 -0
  69. package/lib/tree/rules.d.ts.map +1 -1
  70. package/lib/tree/rules.js +147 -81
  71. package/lib/tree/rules.js.map +1 -1
  72. package/lib/tree/ruleset.d.ts +1 -0
  73. package/lib/tree/ruleset.d.ts.map +1 -1
  74. package/lib/tree/ruleset.js +27 -5
  75. package/lib/tree/ruleset.js.map +1 -1
  76. package/lib/tree/selector-basic.d.ts +1 -0
  77. package/lib/tree/selector-basic.d.ts.map +1 -1
  78. package/lib/tree/selector-basic.js +5 -1
  79. package/lib/tree/selector-basic.js.map +1 -1
  80. package/lib/tree/selector-list.d.ts.map +1 -1
  81. package/lib/tree/selector-list.js +3 -13
  82. package/lib/tree/selector-list.js.map +1 -1
  83. package/lib/tree/selector-pseudo.d.ts.map +1 -1
  84. package/lib/tree/selector-pseudo.js +13 -0
  85. package/lib/tree/selector-pseudo.js.map +1 -1
  86. package/lib/tree/sequence.d.ts.map +1 -1
  87. package/lib/tree/sequence.js +4 -1
  88. package/lib/tree/sequence.js.map +1 -1
  89. package/lib/tree/util/extend-roots.d.ts.map +1 -1
  90. package/lib/tree/util/extend-roots.js +25 -7
  91. package/lib/tree/util/extend-roots.js.map +1 -1
  92. package/lib/tree/util/extend-walk.d.ts +53 -0
  93. package/lib/tree/util/extend-walk.d.ts.map +1 -0
  94. package/lib/tree/util/extend-walk.js +881 -0
  95. package/lib/tree/util/extend-walk.js.map +1 -0
  96. package/lib/tree/util/extend.d.ts.map +1 -1
  97. package/lib/tree/util/extend.js +160 -11
  98. package/lib/tree/util/extend.js.map +1 -1
  99. package/lib/tree/util/registry-utils.d.ts.map +1 -1
  100. package/lib/tree/util/registry-utils.js +13 -41
  101. package/lib/tree/util/registry-utils.js.map +1 -1
  102. package/lib/tree/util/selector-match-core.d.ts +13 -0
  103. package/lib/tree/util/selector-match-core.d.ts.map +1 -1
  104. package/lib/tree/util/selector-match-core.js +55 -30
  105. package/lib/tree/util/selector-match-core.js.map +1 -1
  106. package/package.json +4 -2
package/lib/tree/rules.js CHANGED
@@ -637,6 +637,12 @@ export class Rules extends Node {
637
637
  rules.value[index] = node.preEval(context);
638
638
  return;
639
639
  }
640
+ // Nodes that don't register by name (Call, Expression, etc.) skip
641
+ // both preEval and dynamic resolution — they're handled by the eval queue.
642
+ if (!this._isRegisterableType(node)) {
643
+ node.index = index;
644
+ return;
645
+ }
640
646
  if (this._hasStaticName(node)) {
641
647
  // Pre-evaluate nodes with static names before registration
642
648
  // This ensures selectors are evaluated and keySets are available for rulesets
@@ -696,6 +702,15 @@ export class Rules extends Node {
696
702
  // Primitive values (strings, numbers, etc.) are considered static
697
703
  return true;
698
704
  }
705
+ /**
706
+ * Check if a node type participates in name-based registration.
707
+ * Only these node types have names/selectors that _resolveDynamicNodes
708
+ * needs to resolve. Everything else (Call, Expression, Comment, etc.)
709
+ * goes straight to the eval queue without preEval.
710
+ */
711
+ _isRegisterableType(node) {
712
+ return isNode(node, ['VarDeclaration', 'Declaration', 'Mixin', 'Ruleset', 'StyleImport']);
713
+ }
699
714
  /**
700
715
  * Check if a node has a static name that can be registered immediately
701
716
  */
@@ -708,6 +723,10 @@ export class Rules extends Node {
708
723
  const name = node.value.name;
709
724
  return this._isStatic(name);
710
725
  }
726
+ if (isNode(node, 'Declaration')) {
727
+ const name = node.value.name;
728
+ return this._isStatic(name);
729
+ }
711
730
  if (isNode(node, 'StyleImport')) {
712
731
  const path = node.value.path;
713
732
  return this._isStatic(path);
@@ -729,7 +748,7 @@ export class Rules extends Node {
729
748
  }
730
749
  return false;
731
750
  }
732
- // For other node types, assume they can be registered if they have static names
751
+ // For other registerable node types, check the F_STATIC flag
733
752
  return node.hasFlag(F_STATIC);
734
753
  }
735
754
  /**
@@ -751,105 +770,132 @@ export class Rules extends Node {
751
770
  * Multi-pass resolution of dynamic nodes with interpolated names
752
771
  */
753
772
  _resolveDynamicNodes(rules, context, saved, dynamicNodes) {
754
- const unresolvedNodes = [...dynamicNodes];
755
773
  const resolvedNodes = [];
756
- let firstError;
757
- let resolutionAttempts = 0;
758
- const MAX_RESOLUTION_ATTEMPTS = 5;
759
- const attemptResolution = () => {
760
- resolutionAttempts++;
761
- if (resolutionAttempts > MAX_RESOLUTION_ATTEMPTS) {
762
- throw new Error(`Could not resolve node.`);
774
+ const handleResolvedNode = (resolvedNode, node, stillUnresolved) => {
775
+ if (resolvedNode.index === undefined) {
776
+ resolvedNode.index = node.index;
777
+ }
778
+ if (!resolvedNode.sourceNode) {
779
+ resolvedNode.sourceNode = node.sourceNode ?? node;
780
+ }
781
+ if (resolvedNode.type === 'Ruleset') {
782
+ rules.registerNode(resolvedNode);
783
+ }
784
+ if (isNode(resolvedNode, 'Nil') || this._hasStaticName(resolvedNode)) {
785
+ resolvedNodes.push(resolvedNode);
786
+ this._registerNodeIfEligible(rules, resolvedNode, context);
787
+ return true; // made progress
788
+ }
789
+ else {
790
+ stillUnresolved.push(resolvedNode);
791
+ return false;
792
+ }
793
+ };
794
+ const applyResolvedNodes = () => {
795
+ for (let i = 0; i < rules.value.length; i++) {
796
+ const node = rules.value[i];
797
+ const resolvedNode = resolvedNodes.find(n => n.index === node.index);
798
+ if (resolvedNode && resolvedNode !== node) {
799
+ rules.value[i] = resolvedNode.inherit(node);
800
+ rules.adopt(resolvedNode);
801
+ }
802
+ }
803
+ };
804
+ const finishResolution = () => {
805
+ applyResolvedNodes();
806
+ context.rulesContext = saved.rulesContext;
807
+ context.treeRoot = saved.treeRoot;
808
+ if (saved.root !== undefined) {
809
+ context.root = saved.root;
810
+ }
811
+ return rules;
812
+ };
813
+ // Separate declarations (whose dynamic names might depend on each other)
814
+ // from non-declarations (which depend on declaration VALUES, not names,
815
+ // so retrying during preEval won't help).
816
+ const isDeclarationType = (n) => isNode(n, 'VarDeclaration') || isNode(n, 'Declaration');
817
+ const dynamicDeclarations = [];
818
+ const otherDynamic = [];
819
+ for (const node of dynamicNodes) {
820
+ if (isDeclarationType(node)) {
821
+ dynamicDeclarations.push(node);
822
+ }
823
+ else {
824
+ otherDynamic.push(node);
825
+ }
826
+ }
827
+ // Phase 1: Resolve declarations with dynamic names.
828
+ // Retry because one declaration's name might depend on another's being registered.
829
+ const MAX_DECL_RETRIES = 5;
830
+ let declRetries = 0;
831
+ const unresolvedDecls = [...dynamicDeclarations];
832
+ const resolveDeclarations = () => {
833
+ declRetries++;
834
+ if (declRetries > MAX_DECL_RETRIES || unresolvedDecls.length === 0) {
835
+ return;
763
836
  }
764
837
  const stillUnresolved = [];
765
838
  let madeProgress = false;
766
- for (const node of unresolvedNodes) {
839
+ for (let i = 0; i < unresolvedDecls.length; i++) {
840
+ const node = unresolvedDecls[i];
767
841
  try {
768
- // Try to preEval the node
769
842
  const result = node.preEval(context);
770
843
  if (isThenable(result)) {
771
- // Handle async preEval
844
+ const remaining = unresolvedDecls.slice(i + 1);
772
845
  return result.then((resolvedNode) => {
773
- if (resolvedNode.index === undefined) {
774
- resolvedNode.index = node.index;
775
- }
776
- if (!resolvedNode.sourceNode) {
777
- resolvedNode.sourceNode = node.sourceNode ?? node;
778
- }
779
- // Register rulesets after preEval regardless of static name
780
- if (resolvedNode.type === 'Ruleset') {
781
- // registerNode handles both 'mixin' and 'ruleset' registries
782
- rules.registerNode(resolvedNode);
783
- }
784
- if (isNode(resolvedNode, 'Nil') || this._hasStaticName(resolvedNode)) {
785
- resolvedNodes.push(resolvedNode);
786
- this._registerNodeIfEligible(rules, resolvedNode, context);
846
+ if (handleResolvedNode(resolvedNode, node, stillUnresolved)) {
787
847
  madeProgress = true;
788
848
  }
789
- else {
790
- stillUnresolved.push(resolvedNode);
849
+ unresolvedDecls.length = 0;
850
+ unresolvedDecls.push(...stillUnresolved, ...remaining);
851
+ if (madeProgress && unresolvedDecls.length > 0) {
852
+ return resolveDeclarations();
791
853
  }
792
- return attemptResolution();
793
854
  });
794
855
  }
795
- // Register rulesets after preEval regardless of static name
796
- if (result.index === undefined) {
797
- result.index = node.index;
798
- }
799
- if (!result.sourceNode) {
800
- result.sourceNode = node.sourceNode ?? node;
801
- }
802
- if (result.type === 'Ruleset') {
803
- // registerNode handles both 'mixin' and 'ruleset' registries
804
- rules.registerNode(result);
805
- }
806
- // Check if the node now has a static name
807
- if (isNode(result, 'Nil') || this._hasStaticName(result)) {
808
- resolvedNodes.push(result);
809
- this._registerNodeIfEligible(rules, result, context);
856
+ if (handleResolvedNode(result, node, stillUnresolved)) {
810
857
  madeProgress = true;
811
858
  }
812
- else {
813
- stillUnresolved.push(result);
814
- }
815
859
  }
816
- catch (error) {
817
- if (!firstError) {
818
- firstError = error;
819
- }
860
+ catch {
820
861
  stillUnresolved.push(node);
821
862
  }
822
863
  }
823
- // Update the rules with resolved nodes
824
- for (let i = 0; i < rules.value.length; i++) {
825
- const node = rules.value[i];
826
- const resolvedNode = resolvedNodes.find(n => n.index === node.index);
827
- if (resolvedNode && resolvedNode !== node) {
828
- rules.value[i] = resolvedNode.inherit(node);
829
- rules.adopt(resolvedNode);
830
- }
831
- }
832
- // If we made progress, try again
833
864
  if (madeProgress && stillUnresolved.length > 0) {
834
- unresolvedNodes.length = 0;
835
- unresolvedNodes.push(...stillUnresolved);
836
- return attemptResolution();
865
+ unresolvedDecls.length = 0;
866
+ unresolvedDecls.push(...stillUnresolved);
867
+ return resolveDeclarations();
837
868
  }
838
- // If we still have unresolved nodes and we're done with rules evaluation, throw the first error
839
- if (stillUnresolved.length > 0 && firstError) {
840
- throw firstError;
841
- }
842
- // Restore context after preEval is complete
843
- context.rulesContext = saved.rulesContext;
844
- context.treeRoot = saved.treeRoot;
845
- // Only restore context.root if saved.root is defined (not the outermost root)
846
- // If saved.root is undefined, it means we're at the outermost level, so keep context.root as is
847
- if (saved.root !== undefined) {
848
- context.root = saved.root;
869
+ };
870
+ // Phase 2: Try non-declarations once. Their interpolated names typically
871
+ // depend on declaration VALUES (e.g. @infix from breakpoint-infix()),
872
+ // which aren't evaluated until the eval phase. Retrying won't help.
873
+ const resolveOtherOnce = () => {
874
+ for (let i = 0; i < otherDynamic.length; i++) {
875
+ const node = otherDynamic[i];
876
+ try {
877
+ const result = node.preEval(context);
878
+ if (isThenable(result)) {
879
+ const remaining = otherDynamic.slice(i + 1);
880
+ return result.then((resolvedNode) => {
881
+ handleResolvedNode(resolvedNode, node, []);
882
+ // Continue with remaining nodes
883
+ otherDynamic.length = 0;
884
+ otherDynamic.push(...remaining);
885
+ return resolveOtherOnce();
886
+ });
887
+ }
888
+ handleResolvedNode(result, node, []);
889
+ }
890
+ catch {
891
+ // Can't resolve during preEval — leave in place for eval phase
892
+ }
849
893
  }
850
- return rules;
851
894
  };
852
- return attemptResolution();
895
+ return pipe(() => resolveDeclarations(), () => {
896
+ applyResolvedNodes();
897
+ return resolveOtherOnce();
898
+ }, () => finishResolution());
853
899
  }
854
900
  /**
855
901
  * Helper method to continue preEval'ing remaining children after an async preEval.
@@ -1025,6 +1071,15 @@ export class Rules extends Node {
1025
1071
  if (p === 0 /* Priority.None */) {
1026
1072
  throw error;
1027
1073
  }
1074
+ // Only retry when the import path itself couldn't be resolved
1075
+ // (e.g. @import "@{theme}/file" where @theme isn't available yet).
1076
+ // Path resolution is cheap (no cloning). Content evaluation errors
1077
+ // (after cloning the import tree) are never retried — each retry
1078
+ // would re-clone the entire tree, causing memory blowup.
1079
+ const isPathError = error instanceof Error && error._isPathResolutionError;
1080
+ if (!isPathError) {
1081
+ throw error;
1082
+ }
1028
1083
  // Retry policy:
1029
1084
  // 1) first failure at a priority -> retry once at same priority
1030
1085
  // 2) second+ failure at that priority -> step down one level
@@ -2038,7 +2093,7 @@ export function getFunctionFromMixins(mixins) {
2038
2093
  Mixin: 'public'
2039
2094
  }
2040
2095
  });
2041
- candidate.parent.adopt(outerRules);
2096
+ (thisContext.rulesContext ?? candidate.parent).adopt(outerRules);
2042
2097
  outerRules.index = candidate.index;
2043
2098
  for (let i = 0; i < params.value.length; i++) {
2044
2099
  let param = params.value[i];
@@ -2108,9 +2163,20 @@ export function getFunctionFromMixins(mixins) {
2108
2163
  .map(p => p.value.value);
2109
2164
  const argumentNodes = (paramValues && paramValues.length > 0) ? paramValues : nodeArgs;
2110
2165
  for (const argNode of argumentNodes) {
2111
- const cloned = argNode.copy(true, freezeChildren);
2112
- cloned.frozen = true;
2113
- argumentsArgs.push(cloned);
2166
+ // If a Rest param collected args into a Sequence, spread its items
2167
+ // so @arguments reflects the actual argument count
2168
+ if (isNode(argNode, 'Sequence')) {
2169
+ for (const item of argNode.value) {
2170
+ const cloned = item.copy(true, freezeChildren);
2171
+ cloned.frozen = true;
2172
+ argumentsArgs.push(cloned);
2173
+ }
2174
+ }
2175
+ else {
2176
+ const cloned = argNode.copy(true, freezeChildren);
2177
+ cloned.frozen = true;
2178
+ argumentsArgs.push(cloned);
2179
+ }
2114
2180
  }
2115
2181
  }
2116
2182
  }