clava 0.4.0 → 0.4.1

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,25 @@
1
1
  # clava
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Improved refine iteration warning with debugging context
6
+
7
+ 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.
8
+
9
+ ```txt
10
+ 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.
11
+ Variant(s) that did not stabilize: size.
12
+ Component created at:
13
+ at toolbarButton (src/components/toolbar.ts:42:18)
14
+ ...
15
+ ```
16
+
17
+ 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.
18
+
19
+ ### Other updates
20
+
21
+ - Removed refine warning-only code from production bundles when bundlers statically replace `process.env.NODE_ENV`.
22
+
3
23
  ## 0.4.0
4
24
 
5
25
  ### 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
@@ -224,6 +224,7 @@ function isHTMLObjStyle(style) {
224
224
  const META_KEY = "__meta";
225
225
  const EMPTY_DEFAULTS = Object.freeze({});
226
226
  const MAX_REFINE_RUNS = 50;
227
+ const REFINE_UNSTABLE_TRACKING_WINDOW = 10;
227
228
  function areVariantsEqual(a, b) {
228
229
  for (const key in a) {
229
230
  if (!Object.hasOwn(a, key)) continue;
@@ -235,10 +236,47 @@ function areVariantsEqual(a, b) {
235
236
  }
236
237
  return true;
237
238
  }
238
- function warnRefineLimit(runState) {
239
+ function captureCreationFrame(skipFn) {
240
+ if (process.env.NODE_ENV === "production") return void 0;
241
+ if (typeof Error.captureStackTrace === "function") {
242
+ const holder = {};
243
+ Error.captureStackTrace(holder, skipFn);
244
+ return holder;
245
+ }
246
+ return /* @__PURE__ */ new Error();
247
+ }
248
+ function formatCreationStack(frame) {
249
+ let stack = frame.stack;
250
+ if (!stack) return void 0;
251
+ const newlineIdx = stack.indexOf("\n");
252
+ if (newlineIdx > 0) {
253
+ const firstLine = stack.slice(0, newlineIdx);
254
+ if (firstLine === "Error" || firstLine.startsWith("Error:")) stack = stack.slice(newlineIdx + 1);
255
+ }
256
+ return stack;
257
+ }
258
+ function accumulateUnstableVariantKeys(into, prev, next) {
259
+ for (const key in next) {
260
+ if (!Object.hasOwn(next, key)) continue;
261
+ if (!Object.is(prev[key], next[key])) into.add(key);
262
+ }
263
+ for (const key in prev) {
264
+ if (!Object.hasOwn(prev, key)) continue;
265
+ if (Object.hasOwn(next, key)) continue;
266
+ into.add(key);
267
+ }
268
+ }
269
+ function warnRefineLimit(runState, creationFrame, unstableKeys) {
270
+ if (process.env.NODE_ENV === "production") return;
239
271
  if (runState.warned) return;
240
272
  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.");
273
+ 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.";
274
+ if (unstableKeys && unstableKeys.size > 0) message += `\nVariant(s) that did not stabilize: ${Array.from(unstableKeys).join(", ")}.`;
275
+ if (creationFrame) {
276
+ const creationStack = formatCreationStack(creationFrame);
277
+ if (creationStack) message += `\nComponent created at:\n${creationStack}`;
278
+ }
279
+ console.warn(message);
242
280
  }
243
281
  function getExtUserVariantProps(userVariantProps, protectedVariants, changedVariants) {
244
282
  const extUserVariantProps = {};
@@ -578,6 +616,7 @@ function create({ transformClass = (className) => className } = {}) {
578
616
  }
579
617
  const extMetasWithRefineCount = extMetasWithRefine.length;
580
618
  const shouldCollectChangedVariants = extMetasWithRefineCount > 0;
619
+ const creationFrame = !!refine || extMetasWithRefineCount > 0 ? captureCreationFrame(cv) : void 0;
581
620
  const functionVariantKeys = /* @__PURE__ */ new Set();
582
621
  for (let i = 0; i < extCount; i++) {
583
622
  const fnKeys = extMetas[i].functionVariantKeys;
@@ -898,16 +937,12 @@ function create({ transformClass = (className) => className } = {}) {
898
937
  if (cStyle) Object.assign(styleOut, cStyle);
899
938
  return workingResolved;
900
939
  };
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
- };
940
+ const compute = !refine && extMetasWithRefineCount === 0 ? computeOnce : (resolved, userVariantProps, skipKeys, skipValues, classesOut, styleOut, runState, protectedVariants, pendingProtectedVariants, protectedVariantKeys) => {
941
+ runState ??= { remaining: MAX_REFINE_RUNS };
908
942
  protectedVariants ??= {};
909
943
  protectedVariantKeys ??= /* @__PURE__ */ new Set();
910
944
  let workingResolved = resolved;
945
+ let unstableKeys = null;
911
946
  let lastClasses = [];
912
947
  let lastStyle = {};
913
948
  let isFirstRun = true;
@@ -935,8 +970,12 @@ function create({ transformClass = (className) => className } = {}) {
935
970
  }
936
971
  return nextResolved;
937
972
  }
973
+ if (process.env.NODE_ENV !== "production" && runState.remaining < REFINE_UNSTABLE_TRACKING_WINDOW) {
974
+ if (!unstableKeys) unstableKeys = /* @__PURE__ */ new Set();
975
+ accumulateUnstableVariantKeys(unstableKeys, workingResolved, nextResolved);
976
+ }
938
977
  if (useDirectOutput && runState.remaining === 0) {
939
- warnRefineLimit(runState);
978
+ warnRefineLimit(runState, creationFrame, unstableKeys);
940
979
  return nextResolved;
941
980
  }
942
981
  if (useDirectOutput) {
@@ -949,7 +988,7 @@ function create({ transformClass = (className) => className } = {}) {
949
988
  workingResolved = nextResolved;
950
989
  isFirstRun = false;
951
990
  }
952
- warnRefineLimit(runState);
991
+ warnRefineLimit(runState, creationFrame, unstableKeys);
953
992
  for (let i = 0; i < lastClasses.length; i++) classesOut.push(lastClasses[i]);
954
993
  Object.assign(styleOut, lastStyle);
955
994
  return workingResolved;
@@ -974,13 +1013,11 @@ function create({ transformClass = (className) => className } = {}) {
974
1013
  return workingResolved;
975
1014
  };
976
1015
  const resolveRefine = refine || extMetasWithRefineCount > 0 ? (resolved, userVariantProps, filterOwnVariants = true, runState, protectedVariants, pendingProtectedVariants, protectedVariantKeys) => {
977
- runState ??= {
978
- remaining: MAX_REFINE_RUNS,
979
- warned: false
980
- };
1016
+ runState ??= { remaining: MAX_REFINE_RUNS };
981
1017
  protectedVariants ??= {};
982
1018
  protectedVariantKeys ??= /* @__PURE__ */ new Set();
983
1019
  let workingResolved = resolved;
1020
+ let unstableKeys = null;
984
1021
  let reachedLimit = true;
985
1022
  while (runState.remaining > 0) {
986
1023
  runState.remaining -= 1;
@@ -994,9 +1031,13 @@ function create({ transformClass = (className) => className } = {}) {
994
1031
  reachedLimit = false;
995
1032
  break;
996
1033
  }
1034
+ if (process.env.NODE_ENV !== "production" && runState.remaining < REFINE_UNSTABLE_TRACKING_WINDOW) {
1035
+ if (!unstableKeys) unstableKeys = /* @__PURE__ */ new Set();
1036
+ accumulateUnstableVariantKeys(unstableKeys, workingResolved, nextResolved);
1037
+ }
997
1038
  workingResolved = nextResolved;
998
1039
  }
999
- if (reachedLimit) warnRefineLimit(runState);
1040
+ if (reachedLimit) warnRefineLimit(runState, creationFrame, unstableKeys);
1000
1041
  return workingResolved;
1001
1042
  } : null;
1002
1043
  const computeResult = (props = EMPTY_DEFAULTS) => {