clava 0.4.1 → 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 +5 -0
- package/dist/index.js +114 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +41 -126
- package/src/refine-warning.ts +161 -0
- package/tests/build.test.ts +1 -0
- package/tests/refine-warning.test.ts +28 -0
- package/tests/refine.test.ts +177 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
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
|
+
|
|
3
8
|
## 0.4.1
|
|
4
9
|
|
|
5
10
|
### Improved refine iteration warning with debugging context
|
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) {
|
|
@@ -224,7 +312,6 @@ function isHTMLObjStyle(style) {
|
|
|
224
312
|
const META_KEY = "__meta";
|
|
225
313
|
const EMPTY_DEFAULTS = Object.freeze({});
|
|
226
314
|
const MAX_REFINE_RUNS = 50;
|
|
227
|
-
const REFINE_UNSTABLE_TRACKING_WINDOW = 10;
|
|
228
315
|
function areVariantsEqual(a, b) {
|
|
229
316
|
for (const key in a) {
|
|
230
317
|
if (!Object.hasOwn(a, key)) continue;
|
|
@@ -236,48 +323,6 @@ function areVariantsEqual(a, b) {
|
|
|
236
323
|
}
|
|
237
324
|
return true;
|
|
238
325
|
}
|
|
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;
|
|
271
|
-
if (runState.warned) return;
|
|
272
|
-
runState.warned = true;
|
|
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);
|
|
280
|
-
}
|
|
281
326
|
function getExtUserVariantProps(userVariantProps, protectedVariants, changedVariants) {
|
|
282
327
|
const extUserVariantProps = {};
|
|
283
328
|
Object.assign(extUserVariantProps, userVariantProps);
|
|
@@ -775,7 +820,7 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
775
820
|
if (!Object.hasOwn(newVariants, key)) continue;
|
|
776
821
|
const value = newVariants[key];
|
|
777
822
|
setChangedVariant(key, value, true);
|
|
778
|
-
if (getCurrentVariantValue(key)
|
|
823
|
+
if (Object.is(getCurrentVariantValue(key), value)) continue;
|
|
779
824
|
ensureUpdated()[key] = value;
|
|
780
825
|
}
|
|
781
826
|
return;
|
|
@@ -789,7 +834,7 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
789
834
|
if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) continue;
|
|
790
835
|
}
|
|
791
836
|
setChangedVariant(key, value, true);
|
|
792
|
-
if (getCurrentVariantValue(key)
|
|
837
|
+
if (Object.is(getCurrentVariantValue(key), value)) continue;
|
|
793
838
|
ensureUpdated()[key] = value;
|
|
794
839
|
}
|
|
795
840
|
},
|
|
@@ -804,9 +849,9 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
804
849
|
const valueKey = getVariantValueKey(value);
|
|
805
850
|
if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) continue;
|
|
806
851
|
}
|
|
852
|
+
if (Object.is(getCurrentVariantValue(key), value)) continue;
|
|
807
853
|
setChangedVariant(key, value);
|
|
808
854
|
if (pendingProtectedVariants) pendingProtectedVariants[key] = value;
|
|
809
|
-
if (getCurrentVariantValue(key) === value) continue;
|
|
810
855
|
ensureUpdated()[key] = value;
|
|
811
856
|
}
|
|
812
857
|
},
|
|
@@ -942,7 +987,7 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
942
987
|
protectedVariants ??= {};
|
|
943
988
|
protectedVariantKeys ??= /* @__PURE__ */ new Set();
|
|
944
989
|
let workingResolved = resolved;
|
|
945
|
-
let
|
|
990
|
+
let unstableChanges = null;
|
|
946
991
|
let lastClasses = [];
|
|
947
992
|
let lastStyle = {};
|
|
948
993
|
let isFirstRun = true;
|
|
@@ -970,12 +1015,16 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
970
1015
|
}
|
|
971
1016
|
return nextResolved;
|
|
972
1017
|
}
|
|
973
|
-
if (process.env.NODE_ENV !== "production" && runState.remaining <
|
|
974
|
-
if (!
|
|
975
|
-
|
|
1018
|
+
if (process.env.NODE_ENV !== "production" && runState.remaining < 10) {
|
|
1019
|
+
if (!unstableChanges) unstableChanges = /* @__PURE__ */ new Map();
|
|
1020
|
+
accumulateUnstableVariantChanges(unstableChanges, workingResolved, nextResolved);
|
|
976
1021
|
}
|
|
977
1022
|
if (useDirectOutput && runState.remaining === 0) {
|
|
978
|
-
warnRefineLimit(
|
|
1023
|
+
warnRefineLimit({
|
|
1024
|
+
runState,
|
|
1025
|
+
creationFrame,
|
|
1026
|
+
unstableChanges
|
|
1027
|
+
});
|
|
979
1028
|
return nextResolved;
|
|
980
1029
|
}
|
|
981
1030
|
if (useDirectOutput) {
|
|
@@ -988,7 +1037,11 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
988
1037
|
workingResolved = nextResolved;
|
|
989
1038
|
isFirstRun = false;
|
|
990
1039
|
}
|
|
991
|
-
warnRefineLimit(
|
|
1040
|
+
warnRefineLimit({
|
|
1041
|
+
runState,
|
|
1042
|
+
creationFrame,
|
|
1043
|
+
unstableChanges
|
|
1044
|
+
});
|
|
992
1045
|
for (let i = 0; i < lastClasses.length; i++) classesOut.push(lastClasses[i]);
|
|
993
1046
|
Object.assign(styleOut, lastStyle);
|
|
994
1047
|
return workingResolved;
|
|
@@ -1017,7 +1070,7 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
1017
1070
|
protectedVariants ??= {};
|
|
1018
1071
|
protectedVariantKeys ??= /* @__PURE__ */ new Set();
|
|
1019
1072
|
let workingResolved = resolved;
|
|
1020
|
-
let
|
|
1073
|
+
let unstableChanges = null;
|
|
1021
1074
|
let reachedLimit = true;
|
|
1022
1075
|
while (runState.remaining > 0) {
|
|
1023
1076
|
runState.remaining -= 1;
|
|
@@ -1031,13 +1084,17 @@ function create({ transformClass = (className) => className } = {}) {
|
|
|
1031
1084
|
reachedLimit = false;
|
|
1032
1085
|
break;
|
|
1033
1086
|
}
|
|
1034
|
-
if (process.env.NODE_ENV !== "production" && runState.remaining <
|
|
1035
|
-
if (!
|
|
1036
|
-
|
|
1087
|
+
if (process.env.NODE_ENV !== "production" && runState.remaining < 10) {
|
|
1088
|
+
if (!unstableChanges) unstableChanges = /* @__PURE__ */ new Map();
|
|
1089
|
+
accumulateUnstableVariantChanges(unstableChanges, workingResolved, nextResolved);
|
|
1037
1090
|
}
|
|
1038
1091
|
workingResolved = nextResolved;
|
|
1039
1092
|
}
|
|
1040
|
-
if (reachedLimit) warnRefineLimit(
|
|
1093
|
+
if (reachedLimit) warnRefineLimit({
|
|
1094
|
+
runState,
|
|
1095
|
+
creationFrame,
|
|
1096
|
+
unstableChanges
|
|
1097
|
+
});
|
|
1041
1098
|
return workingResolved;
|
|
1042
1099
|
} : null;
|
|
1043
1100
|
const computeResult = (props = EMPTY_DEFAULTS) => {
|