clava 0.4.0 → 0.4.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # clava
2
2
 
3
+ ## 0.4.2
4
+
5
+ - Improved [`refine`](https://clava.style/docs/reference/refine) iteration warnings to show the latest changing variant values with a shorter component creation stack.
6
+ - Fixed [`setDefaultVariants`](https://clava.style/docs/reference/refine#setdefaultvariants) calls with stable values to avoid extra [`refine`](https://clava.style/docs/reference/refine) passes.
7
+
8
+ ## 0.4.1
9
+
10
+ ### Improved refine iteration warning with debugging context
11
+
12
+ When the [`refine`](https://clava.style/docs/reference/refine) iteration cap is hit, the development warning now lists the variant keys that did not stabilize and includes the stack trace of the [`cv`](https://clava.style/docs/reference/cv) call that defined the component. This makes it easier to find the offending component without searching through the codebase.
13
+
14
+ ```txt
15
+ Clava: Maximum refine iterations exceeded. This can happen when a refine callback calls setVariants or setDefaultVariants, but one of the variants changes on every run.
16
+ Variant(s) that did not stabilize: size.
17
+ Component created at:
18
+ at toolbarButton (src/components/toolbar.ts:42:18)
19
+ ...
20
+ ```
21
+
22
+ The frame is captured at component creation but formatted lazily, so component creation stays cheap unless the warning actually fires, and the capture is skipped entirely for components that cannot enter the refine loop. The whole warning is wrapped in a `process.env.NODE_ENV !== "production"` guard, so bundlers that inline that constant strip the warning machinery from production builds.
23
+
24
+ ### Other updates
25
+
26
+ - Removed refine warning-only code from production bundles when bundlers statically replace `process.env.NODE_ENV`.
27
+
3
28
  ## 0.4.0
4
29
 
5
30
  ### Removed `computedVariants` in favor of function values in `variants`
package/dist/index.d.ts CHANGED
@@ -30,11 +30,11 @@ type ComponentProps<V = {}> = VariantValues<V> & NullableComponentResult;
30
30
  type GetVariants<V> = (variants?: VariantValues<V>) => VariantValues<V>;
31
31
  type ComponentPropKey<R extends ComponentResult> = keyof R | (R extends StyleClassProps ? "className" : never);
32
32
  type KeySourceArray = readonly string[];
33
- type KeySourceComponent = {
33
+ interface KeySourceComponent {
34
34
  propKeys: readonly string[];
35
35
  variantKeys: readonly string[];
36
36
  getVariants: () => Record<string, unknown>;
37
- };
37
+ }
38
38
  type KeySource = KeySourceArray | KeySourceComponent;
39
39
  type IsComponent<S> = S extends {
40
40
  getVariants: () => unknown;
package/dist/index.js CHANGED
@@ -1,4 +1,92 @@
1
1
  import clsx from "clsx";
2
+ //#region src/refine-warning.ts
3
+ function captureCreationFrame(skipFn) {
4
+ if (process.env.NODE_ENV === "production") return void 0;
5
+ if (typeof Error.captureStackTrace === "function") {
6
+ const holder = {};
7
+ Error.captureStackTrace(holder, skipFn);
8
+ return holder;
9
+ }
10
+ return /* @__PURE__ */ new Error();
11
+ }
12
+ function formatCreationStack(frame) {
13
+ let stack = frame.stack;
14
+ if (!stack) return void 0;
15
+ const newlineIdx = stack.indexOf("\n");
16
+ if (newlineIdx > 0) {
17
+ const firstLine = stack.slice(0, newlineIdx);
18
+ if (firstLine === "Error" || firstLine.startsWith("Error:")) stack = stack.slice(newlineIdx + 1);
19
+ }
20
+ const frames = stack.split("\n");
21
+ for (let i = 0; i < frames.length; i++) {
22
+ const line = frames[i]?.trim();
23
+ if (!line) continue;
24
+ if (isInternalCreationFrame(line)) continue;
25
+ if (line.includes("/node_modules/")) continue;
26
+ if (line.includes("\\node_modules\\")) continue;
27
+ if (line.includes("node:internal")) continue;
28
+ return ` ${line}`;
29
+ }
30
+ }
31
+ function isInternalCreationFrame(line) {
32
+ if (line.includes("captureCreationFrame")) return true;
33
+ if (line.startsWith("at cv ")) return true;
34
+ if (line.startsWith("at cv(")) return true;
35
+ if (line.startsWith("cv@")) return true;
36
+ return false;
37
+ }
38
+ function formatVariantValue(value) {
39
+ if (typeof value === "string") return JSON.stringify(value);
40
+ if (typeof value === "number") {
41
+ if (Number.isNaN(value)) return "NaN";
42
+ return String(value);
43
+ }
44
+ if (typeof value === "bigint") return `${value}n`;
45
+ if (value === void 0) return "undefined";
46
+ if (value === null) return "null";
47
+ if (typeof value === "boolean") return String(value);
48
+ if (typeof value === "symbol") return String(value);
49
+ if (typeof value === "function") return "[function]";
50
+ return "[object]";
51
+ }
52
+ function setVariantChange(into, key, from, to) {
53
+ into.set(key, {
54
+ from,
55
+ to
56
+ });
57
+ }
58
+ function accumulateUnstableVariantChanges(into, prev, next) {
59
+ for (const key in next) {
60
+ if (!Object.hasOwn(next, key)) continue;
61
+ if (!Object.is(prev[key], next[key])) setVariantChange(into, key, prev[key], next[key]);
62
+ }
63
+ for (const key in prev) {
64
+ if (!Object.hasOwn(prev, key)) continue;
65
+ if (Object.hasOwn(next, key)) continue;
66
+ setVariantChange(into, key, prev[key], void 0);
67
+ }
68
+ }
69
+ function formatVariantChanges(changes) {
70
+ return Array.from(changes).map(([key, { from, to }]) => {
71
+ return `${key}: ${formatVariantValue(from)} -> ${formatVariantValue(to)}`;
72
+ }).join(", ");
73
+ }
74
+ function warnRefineLimit({ runState, creationFrame, unstableChanges }) {
75
+ if (process.env.NODE_ENV === "production") return;
76
+ if (runState.warned) return;
77
+ runState.warned = true;
78
+ let message = "Clava: Maximum refine iterations exceeded. This can happen when a refine callback calls setVariants or setDefaultVariants, but one of the variants changes on every run.";
79
+ if (unstableChanges && unstableChanges.size > 0) {
80
+ message += `\nVariant(s) that did not stabilize: ${Array.from(unstableChanges.keys()).join(", ")}.`;
81
+ message += `\nLatest variant changes before warning: ${formatVariantChanges(unstableChanges)}.`;
82
+ }
83
+ if (creationFrame) {
84
+ const creationStack = formatCreationStack(creationFrame);
85
+ if (creationStack) message += `\nComponent created at:\n${creationStack}`;
86
+ }
87
+ console.warn(message);
88
+ }
89
+ //#endregion
2
90
  //#region src/utils.ts
3
91
  const hasOwn = Object.prototype.hasOwnProperty;
4
92
  function isAsciiLetter(code) {
@@ -235,11 +323,6 @@ function areVariantsEqual(a, b) {
235
323
  }
236
324
  return true;
237
325
  }
238
- function warnRefineLimit(runState) {
239
- if (runState.warned) return;
240
- runState.warned = true;
241
- if (process.env.NODE_ENV !== "production") console.warn("Clava: Maximum refine iterations exceeded. This can happen when a refine callback calls setVariants or setDefaultVariants, but one of the variants changes on every run.");
242
- }
243
326
  function getExtUserVariantProps(userVariantProps, protectedVariants, changedVariants) {
244
327
  const extUserVariantProps = {};
245
328
  Object.assign(extUserVariantProps, userVariantProps);
@@ -578,6 +661,7 @@ function create({ transformClass = (className) => className } = {}) {
578
661
  }
579
662
  const extMetasWithRefineCount = extMetasWithRefine.length;
580
663
  const shouldCollectChangedVariants = extMetasWithRefineCount > 0;
664
+ const creationFrame = !!refine || extMetasWithRefineCount > 0 ? captureCreationFrame(cv) : void 0;
581
665
  const functionVariantKeys = /* @__PURE__ */ new Set();
582
666
  for (let i = 0; i < extCount; i++) {
583
667
  const fnKeys = extMetas[i].functionVariantKeys;
@@ -736,7 +820,7 @@ function create({ transformClass = (className) => className } = {}) {
736
820
  if (!Object.hasOwn(newVariants, key)) continue;
737
821
  const value = newVariants[key];
738
822
  setChangedVariant(key, value, true);
739
- if (getCurrentVariantValue(key) === value) continue;
823
+ if (Object.is(getCurrentVariantValue(key), value)) continue;
740
824
  ensureUpdated()[key] = value;
741
825
  }
742
826
  return;
@@ -750,7 +834,7 @@ function create({ transformClass = (className) => className } = {}) {
750
834
  if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) continue;
751
835
  }
752
836
  setChangedVariant(key, value, true);
753
- if (getCurrentVariantValue(key) === value) continue;
837
+ if (Object.is(getCurrentVariantValue(key), value)) continue;
754
838
  ensureUpdated()[key] = value;
755
839
  }
756
840
  },
@@ -765,9 +849,9 @@ function create({ transformClass = (className) => className } = {}) {
765
849
  const valueKey = getVariantValueKey(value);
766
850
  if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) continue;
767
851
  }
852
+ if (Object.is(getCurrentVariantValue(key), value)) continue;
768
853
  setChangedVariant(key, value);
769
854
  if (pendingProtectedVariants) pendingProtectedVariants[key] = value;
770
- if (getCurrentVariantValue(key) === value) continue;
771
855
  ensureUpdated()[key] = value;
772
856
  }
773
857
  },
@@ -898,16 +982,12 @@ function create({ transformClass = (className) => className } = {}) {
898
982
  if (cStyle) Object.assign(styleOut, cStyle);
899
983
  return workingResolved;
900
984
  };
901
- const compute = !refine && extMetasWithRefineCount === 0 ? (resolved, userVariantProps, skipKeys, skipValues, classesOut, styleOut, runState, protectedVariants, pendingProtectedVariants, protectedVariantKeys) => {
902
- return computeOnce(resolved, userVariantProps, skipKeys, skipValues, classesOut, styleOut, runState, protectedVariants, pendingProtectedVariants, protectedVariantKeys);
903
- } : (resolved, userVariantProps, skipKeys, skipValues, classesOut, styleOut, runState, protectedVariants, pendingProtectedVariants, protectedVariantKeys) => {
904
- runState ??= {
905
- remaining: MAX_REFINE_RUNS,
906
- warned: false
907
- };
985
+ const compute = !refine && extMetasWithRefineCount === 0 ? computeOnce : (resolved, userVariantProps, skipKeys, skipValues, classesOut, styleOut, runState, protectedVariants, pendingProtectedVariants, protectedVariantKeys) => {
986
+ runState ??= { remaining: MAX_REFINE_RUNS };
908
987
  protectedVariants ??= {};
909
988
  protectedVariantKeys ??= /* @__PURE__ */ new Set();
910
989
  let workingResolved = resolved;
990
+ let unstableChanges = null;
911
991
  let lastClasses = [];
912
992
  let lastStyle = {};
913
993
  let isFirstRun = true;
@@ -935,8 +1015,16 @@ function create({ transformClass = (className) => className } = {}) {
935
1015
  }
936
1016
  return nextResolved;
937
1017
  }
1018
+ if (process.env.NODE_ENV !== "production" && runState.remaining < 10) {
1019
+ if (!unstableChanges) unstableChanges = /* @__PURE__ */ new Map();
1020
+ accumulateUnstableVariantChanges(unstableChanges, workingResolved, nextResolved);
1021
+ }
938
1022
  if (useDirectOutput && runState.remaining === 0) {
939
- warnRefineLimit(runState);
1023
+ warnRefineLimit({
1024
+ runState,
1025
+ creationFrame,
1026
+ unstableChanges
1027
+ });
940
1028
  return nextResolved;
941
1029
  }
942
1030
  if (useDirectOutput) {
@@ -949,7 +1037,11 @@ function create({ transformClass = (className) => className } = {}) {
949
1037
  workingResolved = nextResolved;
950
1038
  isFirstRun = false;
951
1039
  }
952
- warnRefineLimit(runState);
1040
+ warnRefineLimit({
1041
+ runState,
1042
+ creationFrame,
1043
+ unstableChanges
1044
+ });
953
1045
  for (let i = 0; i < lastClasses.length; i++) classesOut.push(lastClasses[i]);
954
1046
  Object.assign(styleOut, lastStyle);
955
1047
  return workingResolved;
@@ -974,13 +1066,11 @@ function create({ transformClass = (className) => className } = {}) {
974
1066
  return workingResolved;
975
1067
  };
976
1068
  const resolveRefine = refine || extMetasWithRefineCount > 0 ? (resolved, userVariantProps, filterOwnVariants = true, runState, protectedVariants, pendingProtectedVariants, protectedVariantKeys) => {
977
- runState ??= {
978
- remaining: MAX_REFINE_RUNS,
979
- warned: false
980
- };
1069
+ runState ??= { remaining: MAX_REFINE_RUNS };
981
1070
  protectedVariants ??= {};
982
1071
  protectedVariantKeys ??= /* @__PURE__ */ new Set();
983
1072
  let workingResolved = resolved;
1073
+ let unstableChanges = null;
984
1074
  let reachedLimit = true;
985
1075
  while (runState.remaining > 0) {
986
1076
  runState.remaining -= 1;
@@ -994,9 +1084,17 @@ function create({ transformClass = (className) => className } = {}) {
994
1084
  reachedLimit = false;
995
1085
  break;
996
1086
  }
1087
+ if (process.env.NODE_ENV !== "production" && runState.remaining < 10) {
1088
+ if (!unstableChanges) unstableChanges = /* @__PURE__ */ new Map();
1089
+ accumulateUnstableVariantChanges(unstableChanges, workingResolved, nextResolved);
1090
+ }
997
1091
  workingResolved = nextResolved;
998
1092
  }
999
- if (reachedLimit) warnRefineLimit(runState);
1093
+ if (reachedLimit) warnRefineLimit({
1094
+ runState,
1095
+ creationFrame,
1096
+ unstableChanges
1097
+ });
1000
1098
  return workingResolved;
1001
1099
  } : null;
1002
1100
  const computeResult = (props = EMPTY_DEFAULTS) => {