rerender-lens 0.3.0 → 0.4.0

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/dist/vitest.cjs CHANGED
@@ -31,10 +31,49 @@ function remember(seen, a, b) {
31
31
  set.add(b);
32
32
  return false;
33
33
  }
34
+ var scope = null;
35
+ var DEFAULT_DIFF_BUDGET = 5e4;
36
+ function beginDiffScope(budget = 2e5) {
37
+ const previous = scope;
38
+ scope = { cache: /* @__PURE__ */ new WeakMap(), remaining: budget, exhausted: false };
39
+ return () => {
40
+ scope = previous;
41
+ };
42
+ }
43
+ function diffBudgetExhausted() {
44
+ return scope?.exhausted === true;
45
+ }
34
46
  function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
35
47
  if (Object.is(a, b)) return true;
36
48
  if (typeof a !== typeof b) return false;
37
49
  if (typeof a !== "object" || a === null || b === null) return false;
50
+ if (!scope) {
51
+ const end = beginDiffScope(DEFAULT_DIFF_BUDGET);
52
+ try {
53
+ return deepEqual(a, b, seen);
54
+ } finally {
55
+ end();
56
+ }
57
+ }
58
+ const sc = scope;
59
+ const objA = a;
60
+ const objB = b;
61
+ const hit = sc.cache.get(objA)?.get(objB);
62
+ if (hit !== void 0) return hit;
63
+ if (--sc.remaining < 0) {
64
+ sc.exhausted = true;
65
+ return false;
66
+ }
67
+ const result = deepEqualObjects(objA, objB, seen);
68
+ let m = sc.cache.get(objA);
69
+ if (!m) {
70
+ m = /* @__PURE__ */ new WeakMap();
71
+ sc.cache.set(objA, m);
72
+ }
73
+ m.set(objB, result);
74
+ return result;
75
+ }
76
+ function deepEqualObjects(a, b, seen) {
38
77
  const objA = a;
39
78
  const objB = b;
40
79
  if (remember(seen, objA, objB)) return true;
@@ -63,6 +102,10 @@ function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
63
102
  }
64
103
  if (a instanceof Set) {
65
104
  if (!(b instanceof Set) || a.size !== b.size) return false;
105
+ if (a.size > 50) {
106
+ for (const v of a) if (!b.has(v)) return false;
107
+ return true;
108
+ }
66
109
  outer: for (const v of a) {
67
110
  if (b.has(v)) continue;
68
111
  for (const w of b) if (deepEqual(v, w, seen)) continue outer;
@@ -78,9 +121,10 @@ function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
78
121
  const ka = Object.keys(a);
79
122
  const kb = Object.keys(b);
80
123
  if (ka.length !== kb.length) return false;
124
+ const rb = b;
81
125
  for (const k of ka) {
82
126
  if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
83
- if (!deepEqual(a[k], b[k], seen)) return false;
127
+ if (!deepEqual(a[k], rb[k], seen)) return false;
84
128
  }
85
129
  return true;
86
130
  }
@@ -122,7 +166,7 @@ function diffRecords(prev, next, basePath = "") {
122
166
  if (Object.is(a, b)) continue;
123
167
  const kind = classify(a, b);
124
168
  if (kind === "different") {
125
- changes.push({ path: firstDifferentPath(a, b, path), kind, prev: a, next: b });
169
+ changes.push({ path: diffBudgetExhausted() ? path : firstDifferentPath(a, b, path), kind, prev: a, next: b });
126
170
  } else {
127
171
  changes.push({ path, kind, prev: a, next: b });
128
172
  }
@@ -333,7 +377,7 @@ function getState() {
333
377
  const g = globalThis;
334
378
  let s = g[KEY];
335
379
  if (!s) {
336
- s = { options: {}, enabled: false, printed: /* @__PURE__ */ new Map(), detach: null, nextInstanceId: 1, nextCommitId: 1, warnedOnce: /* @__PURE__ */ new Set(), scheduled: 0, commits: 0, overheadMs: 0, maxCommitMs: 0 };
380
+ s = { options: {}, enabled: false, printed: /* @__PURE__ */ new Map(), detach: null, nextInstanceId: 1, nextCommitId: 1, warnedOnce: /* @__PURE__ */ new Set(), scheduled: 0, commits: 0, overheadMs: 0, maxCommitMs: 0, truncated: 0 };
337
381
  g[KEY] = s;
338
382
  }
339
383
  return s;
@@ -363,7 +407,7 @@ function dispatch(report, override) {
363
407
  try {
364
408
  options.notifier(report);
365
409
  } catch (err) {
366
- (options.console ?? console).warn("[rerender-lens] notifier threw", err);
410
+ warnOnce("notifier", `notifier threw: ${String(err)}`);
367
411
  }
368
412
  }
369
413
  }
@@ -751,11 +795,14 @@ var counts = /* @__PURE__ */ new WeakMap();
751
795
  var ids = /* @__PURE__ */ new WeakMap();
752
796
  var fibersById = /* @__PURE__ */ new Map();
753
797
  var hasWeakRef = typeof WeakRef === "function";
798
+ var pruneAt = 5e3;
754
799
  function remember2(id, fiber) {
755
800
  if (!hasWeakRef) return;
801
+ if (fibersById.get(id)?.deref() === fiber) return;
756
802
  fibersById.set(id, new WeakRef(fiber));
757
- if (fibersById.size > 5e3) {
803
+ if (fibersById.size > pruneAt) {
758
804
  for (const [k, ref] of fibersById) if (!ref.deref()) fibersById.delete(k);
805
+ pruneAt = Math.max(5e3, fibersById.size * 2);
759
806
  }
760
807
  }
761
808
  function instanceIdOf(fiber) {
@@ -784,7 +831,7 @@ function nearestComponent(fiber) {
784
831
  }
785
832
  var INTERNAL_FRAME = /react-dom|react_jsx|jsx-(dev-)?runtime|\/react\/|node_modules\/react|react-stack-bottom-frame|react_stack_bottom_frame|scheduler/;
786
833
  function parseStackLocation(stack) {
787
- for (const line of stack.split("\n")) {
834
+ for (const line of stack.split("\n", 24)) {
788
835
  const m = /(?:at\s+(?:.*?\s+)?\(?|@)?((?:https?|file|webpack|vite|blob):[^\s()]+?):(\d+):(\d+)\)?\s*$/.exec(line.trim());
789
836
  if (!m || !m[1] || INTERNAL_FRAME.test(m[1])) continue;
790
837
  return { fileName: m[1], lineNumber: Number(m[2]), columnNumber: Number(m[3]) };
@@ -800,9 +847,16 @@ function sourceOf(fiber) {
800
847
  return out;
801
848
  }
802
849
  const st = fiber._debugStack;
803
- const text = typeof st === "string" ? st : st && typeof st.stack === "string" ? st.stack : null;
804
- return text ? parseStackLocation(text) : void 0;
850
+ if (st && typeof st === "object") {
851
+ if (sourceCache.has(st)) return sourceCache.get(st);
852
+ const text = typeof st.stack === "string" ? st.stack : null;
853
+ const out = text ? parseStackLocation(text) : void 0;
854
+ sourceCache.set(st, out);
855
+ return out;
856
+ }
857
+ return typeof st === "string" ? parseStackLocation(st) : void 0;
805
858
  }
859
+ var sourceCache = /* @__PURE__ */ new WeakMap();
806
860
  function bumpCount(fiber) {
807
861
  const prev = counts.get(fiber) ?? (fiber.alternate ? counts.get(fiber.alternate) : void 0) ?? 0;
808
862
  const n = prev + 1;
@@ -990,6 +1044,8 @@ function updatersOf(root) {
990
1044
  }
991
1045
  var lastCommit = null;
992
1046
  var EFFECT_LOOP_WINDOW_MS = 50;
1047
+ var MAX_REPORTS_PER_COMMIT = 200;
1048
+ var COMMIT_TIME_BUDGET_MS = 25;
993
1049
  function onCommit(root, commitPriority) {
994
1050
  const s = getState();
995
1051
  if (!s.enabled) return;
@@ -1020,7 +1076,8 @@ function onCommit(root, commitPriority) {
1020
1076
  const updaters = updatersOf(root);
1021
1077
  let commitCause;
1022
1078
  let afterCommit;
1023
- if (lastCommit && at - lastCommit.at < EFFECT_LOOP_WINDOW_MS && updaters.some((u) => lastCommit.rendered.has(u))) {
1079
+ const inputDriven = commitPriority === "immediate" || commitPriority === "user-blocking";
1080
+ if (!inputDriven && lastCommit && at - lastCommit.at < EFFECT_LOOP_WINDOW_MS && updaters.some((u) => lastCommit.rendered.has(u))) {
1024
1081
  commitCause = "effect-after-commit";
1025
1082
  afterCommit = lastCommit.id;
1026
1083
  } else if (suspenseResolved) commitCause = "suspense-resolved";
@@ -1032,47 +1089,59 @@ function onCommit(root, commitPriority) {
1032
1089
  lastCommit = { id: commitId, at, rendered: renderedNames };
1033
1090
  const dispatcherRef = o.resolveHookNames ? getDispatcherRef() : null;
1034
1091
  const parentCache = /* @__PURE__ */ new Map();
1035
- for (const fiber of rendered) {
1036
- const alt = fiber.alternate;
1037
- const a = analyze(fiber, alt, trackHooks);
1038
- const durations = durationsOf(fiber);
1039
- let hookState = o.includeState !== false && trackHooks ? snapshotHooks(fiber) : void 0;
1040
- if (dispatcherRef && fiber.tag !== ClassComponent && (a.hookChanges.length || hookState && hookState.length)) {
1041
- const names = resolveHookNames(fiber, dispatcherRef);
1042
- if (names.size) {
1043
- for (const c of a.hookChanges) if (c.hook !== "useContext" && names.has(c.index)) c.custom = names.get(c.index);
1044
- if (hookState) hookState = hookState.map((h) => names.has(h.index) ? { ...h, custom: names.get(h.index) } : h);
1092
+ const endDiffScope = beginDiffScope();
1093
+ const deadline = at + COMMIT_TIME_BUDGET_MS;
1094
+ try {
1095
+ for (let i = 0; i < rendered.length; i++) {
1096
+ if (i >= MAX_REPORTS_PER_COMMIT || (i & 15) === 15 && nowMs() > deadline) {
1097
+ s.truncated += rendered.length - i;
1098
+ warnOnce("truncated", `a commit rendered ${rendered.length} tracked components; only ${i} were reported (see info().truncated). Narrow \`include\` or turn off includeState.`);
1099
+ break;
1100
+ }
1101
+ const fiber = rendered[i];
1102
+ const alt = fiber.alternate;
1103
+ const a = analyze(fiber, alt, trackHooks);
1104
+ const durations = durationsOf(fiber);
1105
+ let hookState = o.includeState !== false && trackHooks ? snapshotHooks(fiber) : void 0;
1106
+ if (dispatcherRef && fiber.tag !== ClassComponent && (a.hookChanges.length || hookState && hookState.length)) {
1107
+ const names = resolveHookNames(fiber, dispatcherRef);
1108
+ if (names.size) {
1109
+ for (const c of a.hookChanges) if (c.hook !== "useContext" && names.has(c.index)) c.custom = names.get(c.index);
1110
+ if (hookState) hookState = hookState.map((h) => names.has(h.index) ? { ...h, custom: names.get(h.index) } : h);
1111
+ }
1045
1112
  }
1113
+ const report = buildReport({
1114
+ component: fiberName(fiber),
1115
+ instanceId: instanceId(fiber),
1116
+ renderCount: bumpCount(fiber),
1117
+ prevProps: alt.memoizedProps ?? {},
1118
+ nextProps: fiber.memoizedProps ?? {},
1119
+ propChanges: a.propChanges,
1120
+ stateChanges: a.stateChanges,
1121
+ hookChanges: a.hookChanges,
1122
+ ...o.includeState !== false ? {
1123
+ hookState,
1124
+ contexts: trackHooks ? snapshotContexts(fiber) : void 0,
1125
+ state: fiber.tag === ClassComponent && fiber.memoizedState && typeof fiber.memoizedState === "object" ? fiber.memoizedState : void 0
1126
+ } : {},
1127
+ updaters,
1128
+ commitCause,
1129
+ afterCommit,
1130
+ key: fiber.key === null || fiber.key === void 0 ? null : String(fiber.key),
1131
+ parent: a.trigger === "parent" ? nearestRenderedAncestor(fiber, parentCache, trackHooks) : null,
1132
+ owner: ownerName(fiber),
1133
+ path: componentPath(fiber),
1134
+ memoized: isMemoizedFiber(fiber),
1135
+ selfDuration: durations ? durations.self : void 0,
1136
+ treeDuration: durations ? durations.tree : void 0,
1137
+ commitId,
1138
+ commitPriority,
1139
+ source: sourceOf(fiber)
1140
+ });
1141
+ dispatch(report);
1046
1142
  }
1047
- const report = buildReport({
1048
- component: fiberName(fiber),
1049
- instanceId: instanceId(fiber),
1050
- renderCount: bumpCount(fiber),
1051
- prevProps: alt.memoizedProps ?? {},
1052
- nextProps: fiber.memoizedProps ?? {},
1053
- propChanges: a.propChanges,
1054
- stateChanges: a.stateChanges,
1055
- hookChanges: a.hookChanges,
1056
- ...o.includeState !== false ? {
1057
- hookState,
1058
- contexts: trackHooks ? snapshotContexts(fiber) : void 0,
1059
- state: fiber.tag === ClassComponent && fiber.memoizedState && typeof fiber.memoizedState === "object" ? fiber.memoizedState : void 0
1060
- } : {},
1061
- updaters,
1062
- commitCause,
1063
- afterCommit,
1064
- key: fiber.key === null || fiber.key === void 0 ? null : String(fiber.key),
1065
- parent: a.trigger === "parent" ? nearestRenderedAncestor(fiber, parentCache, trackHooks) : null,
1066
- owner: ownerName(fiber),
1067
- path: componentPath(fiber),
1068
- memoized: isMemoizedFiber(fiber),
1069
- selfDuration: durations ? durations.self : void 0,
1070
- treeDuration: durations ? durations.tree : void 0,
1071
- commitId,
1072
- commitPriority,
1073
- source: sourceOf(fiber)
1074
- });
1075
- dispatch(report);
1143
+ } finally {
1144
+ endDiffScope();
1076
1145
  }
1077
1146
  }
1078
1147