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.js CHANGED
@@ -27,10 +27,49 @@ function remember(seen, a, b) {
27
27
  set.add(b);
28
28
  return false;
29
29
  }
30
+ var scope = null;
31
+ var DEFAULT_DIFF_BUDGET = 5e4;
32
+ function beginDiffScope(budget = 2e5) {
33
+ const previous = scope;
34
+ scope = { cache: /* @__PURE__ */ new WeakMap(), remaining: budget, exhausted: false };
35
+ return () => {
36
+ scope = previous;
37
+ };
38
+ }
39
+ function diffBudgetExhausted() {
40
+ return scope?.exhausted === true;
41
+ }
30
42
  function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
31
43
  if (Object.is(a, b)) return true;
32
44
  if (typeof a !== typeof b) return false;
33
45
  if (typeof a !== "object" || a === null || b === null) return false;
46
+ if (!scope) {
47
+ const end = beginDiffScope(DEFAULT_DIFF_BUDGET);
48
+ try {
49
+ return deepEqual(a, b, seen);
50
+ } finally {
51
+ end();
52
+ }
53
+ }
54
+ const sc = scope;
55
+ const objA = a;
56
+ const objB = b;
57
+ const hit = sc.cache.get(objA)?.get(objB);
58
+ if (hit !== void 0) return hit;
59
+ if (--sc.remaining < 0) {
60
+ sc.exhausted = true;
61
+ return false;
62
+ }
63
+ const result = deepEqualObjects(objA, objB, seen);
64
+ let m = sc.cache.get(objA);
65
+ if (!m) {
66
+ m = /* @__PURE__ */ new WeakMap();
67
+ sc.cache.set(objA, m);
68
+ }
69
+ m.set(objB, result);
70
+ return result;
71
+ }
72
+ function deepEqualObjects(a, b, seen) {
34
73
  const objA = a;
35
74
  const objB = b;
36
75
  if (remember(seen, objA, objB)) return true;
@@ -59,6 +98,10 @@ function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
59
98
  }
60
99
  if (a instanceof Set) {
61
100
  if (!(b instanceof Set) || a.size !== b.size) return false;
101
+ if (a.size > 50) {
102
+ for (const v of a) if (!b.has(v)) return false;
103
+ return true;
104
+ }
62
105
  outer: for (const v of a) {
63
106
  if (b.has(v)) continue;
64
107
  for (const w of b) if (deepEqual(v, w, seen)) continue outer;
@@ -74,9 +117,10 @@ function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
74
117
  const ka = Object.keys(a);
75
118
  const kb = Object.keys(b);
76
119
  if (ka.length !== kb.length) return false;
120
+ const rb = b;
77
121
  for (const k of ka) {
78
122
  if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
79
- if (!deepEqual(a[k], b[k], seen)) return false;
123
+ if (!deepEqual(a[k], rb[k], seen)) return false;
80
124
  }
81
125
  return true;
82
126
  }
@@ -118,7 +162,7 @@ function diffRecords(prev, next, basePath = "") {
118
162
  if (Object.is(a, b)) continue;
119
163
  const kind = classify(a, b);
120
164
  if (kind === "different") {
121
- changes.push({ path: firstDifferentPath(a, b, path), kind, prev: a, next: b });
165
+ changes.push({ path: diffBudgetExhausted() ? path : firstDifferentPath(a, b, path), kind, prev: a, next: b });
122
166
  } else {
123
167
  changes.push({ path, kind, prev: a, next: b });
124
168
  }
@@ -329,7 +373,7 @@ function getState() {
329
373
  const g = globalThis;
330
374
  let s = g[KEY];
331
375
  if (!s) {
332
- 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 };
376
+ 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 };
333
377
  g[KEY] = s;
334
378
  }
335
379
  return s;
@@ -359,7 +403,7 @@ function dispatch(report, override) {
359
403
  try {
360
404
  options.notifier(report);
361
405
  } catch (err) {
362
- (options.console ?? console).warn("[rerender-lens] notifier threw", err);
406
+ warnOnce("notifier", `notifier threw: ${String(err)}`);
363
407
  }
364
408
  }
365
409
  }
@@ -747,11 +791,14 @@ var counts = /* @__PURE__ */ new WeakMap();
747
791
  var ids = /* @__PURE__ */ new WeakMap();
748
792
  var fibersById = /* @__PURE__ */ new Map();
749
793
  var hasWeakRef = typeof WeakRef === "function";
794
+ var pruneAt = 5e3;
750
795
  function remember2(id, fiber) {
751
796
  if (!hasWeakRef) return;
797
+ if (fibersById.get(id)?.deref() === fiber) return;
752
798
  fibersById.set(id, new WeakRef(fiber));
753
- if (fibersById.size > 5e3) {
799
+ if (fibersById.size > pruneAt) {
754
800
  for (const [k, ref] of fibersById) if (!ref.deref()) fibersById.delete(k);
801
+ pruneAt = Math.max(5e3, fibersById.size * 2);
755
802
  }
756
803
  }
757
804
  function instanceIdOf(fiber) {
@@ -780,7 +827,7 @@ function nearestComponent(fiber) {
780
827
  }
781
828
  var INTERNAL_FRAME = /react-dom|react_jsx|jsx-(dev-)?runtime|\/react\/|node_modules\/react|react-stack-bottom-frame|react_stack_bottom_frame|scheduler/;
782
829
  function parseStackLocation(stack) {
783
- for (const line of stack.split("\n")) {
830
+ for (const line of stack.split("\n", 24)) {
784
831
  const m = /(?:at\s+(?:.*?\s+)?\(?|@)?((?:https?|file|webpack|vite|blob):[^\s()]+?):(\d+):(\d+)\)?\s*$/.exec(line.trim());
785
832
  if (!m || !m[1] || INTERNAL_FRAME.test(m[1])) continue;
786
833
  return { fileName: m[1], lineNumber: Number(m[2]), columnNumber: Number(m[3]) };
@@ -796,9 +843,16 @@ function sourceOf(fiber) {
796
843
  return out;
797
844
  }
798
845
  const st = fiber._debugStack;
799
- const text = typeof st === "string" ? st : st && typeof st.stack === "string" ? st.stack : null;
800
- return text ? parseStackLocation(text) : void 0;
846
+ if (st && typeof st === "object") {
847
+ if (sourceCache.has(st)) return sourceCache.get(st);
848
+ const text = typeof st.stack === "string" ? st.stack : null;
849
+ const out = text ? parseStackLocation(text) : void 0;
850
+ sourceCache.set(st, out);
851
+ return out;
852
+ }
853
+ return typeof st === "string" ? parseStackLocation(st) : void 0;
801
854
  }
855
+ var sourceCache = /* @__PURE__ */ new WeakMap();
802
856
  function bumpCount(fiber) {
803
857
  const prev = counts.get(fiber) ?? (fiber.alternate ? counts.get(fiber.alternate) : void 0) ?? 0;
804
858
  const n = prev + 1;
@@ -986,6 +1040,8 @@ function updatersOf(root) {
986
1040
  }
987
1041
  var lastCommit = null;
988
1042
  var EFFECT_LOOP_WINDOW_MS = 50;
1043
+ var MAX_REPORTS_PER_COMMIT = 200;
1044
+ var COMMIT_TIME_BUDGET_MS = 25;
989
1045
  function onCommit(root, commitPriority) {
990
1046
  const s = getState();
991
1047
  if (!s.enabled) return;
@@ -1016,7 +1072,8 @@ function onCommit(root, commitPriority) {
1016
1072
  const updaters = updatersOf(root);
1017
1073
  let commitCause;
1018
1074
  let afterCommit;
1019
- if (lastCommit && at - lastCommit.at < EFFECT_LOOP_WINDOW_MS && updaters.some((u) => lastCommit.rendered.has(u))) {
1075
+ const inputDriven = commitPriority === "immediate" || commitPriority === "user-blocking";
1076
+ if (!inputDriven && lastCommit && at - lastCommit.at < EFFECT_LOOP_WINDOW_MS && updaters.some((u) => lastCommit.rendered.has(u))) {
1020
1077
  commitCause = "effect-after-commit";
1021
1078
  afterCommit = lastCommit.id;
1022
1079
  } else if (suspenseResolved) commitCause = "suspense-resolved";
@@ -1028,47 +1085,59 @@ function onCommit(root, commitPriority) {
1028
1085
  lastCommit = { id: commitId, at, rendered: renderedNames };
1029
1086
  const dispatcherRef = o.resolveHookNames ? getDispatcherRef() : null;
1030
1087
  const parentCache = /* @__PURE__ */ new Map();
1031
- for (const fiber of rendered) {
1032
- const alt = fiber.alternate;
1033
- const a = analyze(fiber, alt, trackHooks);
1034
- const durations = durationsOf(fiber);
1035
- let hookState = o.includeState !== false && trackHooks ? snapshotHooks(fiber) : void 0;
1036
- if (dispatcherRef && fiber.tag !== ClassComponent && (a.hookChanges.length || hookState && hookState.length)) {
1037
- const names = resolveHookNames(fiber, dispatcherRef);
1038
- if (names.size) {
1039
- for (const c of a.hookChanges) if (c.hook !== "useContext" && names.has(c.index)) c.custom = names.get(c.index);
1040
- if (hookState) hookState = hookState.map((h) => names.has(h.index) ? { ...h, custom: names.get(h.index) } : h);
1088
+ const endDiffScope = beginDiffScope();
1089
+ const deadline = at + COMMIT_TIME_BUDGET_MS;
1090
+ try {
1091
+ for (let i = 0; i < rendered.length; i++) {
1092
+ if (i >= MAX_REPORTS_PER_COMMIT || (i & 15) === 15 && nowMs() > deadline) {
1093
+ s.truncated += rendered.length - i;
1094
+ warnOnce("truncated", `a commit rendered ${rendered.length} tracked components; only ${i} were reported (see info().truncated). Narrow \`include\` or turn off includeState.`);
1095
+ break;
1096
+ }
1097
+ const fiber = rendered[i];
1098
+ const alt = fiber.alternate;
1099
+ const a = analyze(fiber, alt, trackHooks);
1100
+ const durations = durationsOf(fiber);
1101
+ let hookState = o.includeState !== false && trackHooks ? snapshotHooks(fiber) : void 0;
1102
+ if (dispatcherRef && fiber.tag !== ClassComponent && (a.hookChanges.length || hookState && hookState.length)) {
1103
+ const names = resolveHookNames(fiber, dispatcherRef);
1104
+ if (names.size) {
1105
+ for (const c of a.hookChanges) if (c.hook !== "useContext" && names.has(c.index)) c.custom = names.get(c.index);
1106
+ if (hookState) hookState = hookState.map((h) => names.has(h.index) ? { ...h, custom: names.get(h.index) } : h);
1107
+ }
1041
1108
  }
1109
+ const report = buildReport({
1110
+ component: fiberName(fiber),
1111
+ instanceId: instanceId(fiber),
1112
+ renderCount: bumpCount(fiber),
1113
+ prevProps: alt.memoizedProps ?? {},
1114
+ nextProps: fiber.memoizedProps ?? {},
1115
+ propChanges: a.propChanges,
1116
+ stateChanges: a.stateChanges,
1117
+ hookChanges: a.hookChanges,
1118
+ ...o.includeState !== false ? {
1119
+ hookState,
1120
+ contexts: trackHooks ? snapshotContexts(fiber) : void 0,
1121
+ state: fiber.tag === ClassComponent && fiber.memoizedState && typeof fiber.memoizedState === "object" ? fiber.memoizedState : void 0
1122
+ } : {},
1123
+ updaters,
1124
+ commitCause,
1125
+ afterCommit,
1126
+ key: fiber.key === null || fiber.key === void 0 ? null : String(fiber.key),
1127
+ parent: a.trigger === "parent" ? nearestRenderedAncestor(fiber, parentCache, trackHooks) : null,
1128
+ owner: ownerName(fiber),
1129
+ path: componentPath(fiber),
1130
+ memoized: isMemoizedFiber(fiber),
1131
+ selfDuration: durations ? durations.self : void 0,
1132
+ treeDuration: durations ? durations.tree : void 0,
1133
+ commitId,
1134
+ commitPriority,
1135
+ source: sourceOf(fiber)
1136
+ });
1137
+ dispatch(report);
1042
1138
  }
1043
- const report = buildReport({
1044
- component: fiberName(fiber),
1045
- instanceId: instanceId(fiber),
1046
- renderCount: bumpCount(fiber),
1047
- prevProps: alt.memoizedProps ?? {},
1048
- nextProps: fiber.memoizedProps ?? {},
1049
- propChanges: a.propChanges,
1050
- stateChanges: a.stateChanges,
1051
- hookChanges: a.hookChanges,
1052
- ...o.includeState !== false ? {
1053
- hookState,
1054
- contexts: trackHooks ? snapshotContexts(fiber) : void 0,
1055
- state: fiber.tag === ClassComponent && fiber.memoizedState && typeof fiber.memoizedState === "object" ? fiber.memoizedState : void 0
1056
- } : {},
1057
- updaters,
1058
- commitCause,
1059
- afterCommit,
1060
- key: fiber.key === null || fiber.key === void 0 ? null : String(fiber.key),
1061
- parent: a.trigger === "parent" ? nearestRenderedAncestor(fiber, parentCache, trackHooks) : null,
1062
- owner: ownerName(fiber),
1063
- path: componentPath(fiber),
1064
- memoized: isMemoizedFiber(fiber),
1065
- selfDuration: durations ? durations.self : void 0,
1066
- treeDuration: durations ? durations.tree : void 0,
1067
- commitId,
1068
- commitPriority,
1069
- source: sourceOf(fiber)
1070
- });
1071
- dispatch(report);
1139
+ } finally {
1140
+ endDiffScope();
1072
1141
  }
1073
1142
  }
1074
1143