rerender-lens 0.1.0 → 0.2.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/CHANGELOG.md +95 -6
- package/README.md +156 -74
- package/dist/index.cjs +1051 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +232 -22
- package/dist/index.d.ts +232 -22
- package/dist/index.js +1028 -10
- package/dist/index.js.map +1 -1
- package/package.json +10 -24
- package/dist/chunk-3YHI7BCU.js +0 -544
- package/dist/chunk-3YHI7BCU.js.map +0 -1
- package/dist/chunk-DOYB4MKH.cjs +0 -562
- package/dist/chunk-DOYB4MKH.cjs.map +0 -1
- package/dist/jsx-dev-runtime.cjs +0 -32
- package/dist/jsx-dev-runtime.cjs.map +0 -1
- package/dist/jsx-dev-runtime.d.cts +0 -8
- package/dist/jsx-dev-runtime.d.ts +0 -8
- package/dist/jsx-dev-runtime.js +0 -9
- package/dist/jsx-dev-runtime.js.map +0 -1
- package/dist/jsx-runtime.cjs +0 -34
- package/dist/jsx-runtime.cjs.map +0 -1
- package/dist/jsx-runtime.d.cts +0 -9
- package/dist/jsx-runtime.d.ts +0 -9
- package/dist/jsx-runtime.js +0 -10
- package/dist/jsx-runtime.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,834 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var chunkDOYB4MKH_cjs = require('./chunk-DOYB4MKH.cjs');
|
|
4
3
|
var react = require('react');
|
|
5
4
|
|
|
5
|
+
// src/types.ts
|
|
6
|
+
var MARKER = "rerenderLens";
|
|
7
|
+
|
|
8
|
+
// src/diff.ts
|
|
9
|
+
var REACT_ELEMENT = /* @__PURE__ */ Symbol.for("react.element");
|
|
10
|
+
var REACT_TRANSITIONAL_ELEMENT = /* @__PURE__ */ Symbol.for("react.transitional.element");
|
|
11
|
+
function isReactElement(v) {
|
|
12
|
+
if (typeof v !== "object" || v === null) return false;
|
|
13
|
+
const t = v.$$typeof;
|
|
14
|
+
return t === REACT_ELEMENT || t === REACT_TRANSITIONAL_ELEMENT;
|
|
15
|
+
}
|
|
16
|
+
function isPlainObject(v) {
|
|
17
|
+
if (typeof v !== "object" || v === null) return false;
|
|
18
|
+
const proto = Object.getPrototypeOf(v);
|
|
19
|
+
return proto === Object.prototype || proto === null;
|
|
20
|
+
}
|
|
21
|
+
function remember(seen, a, b) {
|
|
22
|
+
let set = seen.get(a);
|
|
23
|
+
if (!set) {
|
|
24
|
+
set = /* @__PURE__ */ new Set();
|
|
25
|
+
seen.set(a, set);
|
|
26
|
+
}
|
|
27
|
+
if (set.has(b)) return true;
|
|
28
|
+
set.add(b);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function deepEqual(a, b, seen = /* @__PURE__ */ new Map()) {
|
|
32
|
+
if (Object.is(a, b)) return true;
|
|
33
|
+
if (typeof a !== typeof b) return false;
|
|
34
|
+
if (typeof a !== "object" || a === null || b === null) return false;
|
|
35
|
+
const objA = a;
|
|
36
|
+
const objB = b;
|
|
37
|
+
if (remember(seen, objA, objB)) return true;
|
|
38
|
+
if (a instanceof Date) return b instanceof Date && a.getTime() === b.getTime();
|
|
39
|
+
if (a instanceof RegExp) return b instanceof RegExp && a.source === b.source && a.flags === b.flags;
|
|
40
|
+
if (Array.isArray(a)) {
|
|
41
|
+
if (!Array.isArray(b) || a.length !== b.length) return false;
|
|
42
|
+
for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i], seen)) return false;
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(b)) return false;
|
|
46
|
+
if (ArrayBuffer.isView(a)) {
|
|
47
|
+
if (!ArrayBuffer.isView(b) || a.constructor !== b.constructor) return false;
|
|
48
|
+
const ta = a;
|
|
49
|
+
const tb = b;
|
|
50
|
+
if (ta.length !== tb.length) return false;
|
|
51
|
+
for (let i = 0; i < ta.length; i++) if (ta[i] !== tb[i]) return false;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (a instanceof Map) {
|
|
55
|
+
if (!(b instanceof Map) || a.size !== b.size) return false;
|
|
56
|
+
for (const [k, v] of a) {
|
|
57
|
+
if (!b.has(k) || !deepEqual(v, b.get(k), seen)) return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (a instanceof Set) {
|
|
62
|
+
if (!(b instanceof Set) || a.size !== b.size) return false;
|
|
63
|
+
outer: for (const v of a) {
|
|
64
|
+
if (b.has(v)) continue;
|
|
65
|
+
for (const w of b) if (deepEqual(v, w, seen)) continue outer;
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (isReactElement(a)) {
|
|
71
|
+
if (!isReactElement(b)) return false;
|
|
72
|
+
return a.type === b.type && a.key === b.key && deepEqual(a.props, b.props, seen);
|
|
73
|
+
}
|
|
74
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
75
|
+
const ka = Object.keys(a);
|
|
76
|
+
const kb = Object.keys(b);
|
|
77
|
+
if (ka.length !== kb.length) return false;
|
|
78
|
+
for (const k of ka) {
|
|
79
|
+
if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
|
|
80
|
+
if (!deepEqual(a[k], b[k], seen)) return false;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
function classify(prev, next) {
|
|
87
|
+
if (typeof prev === "function" && typeof next === "function") {
|
|
88
|
+
return prev.name === next.name && prev.toString() === next.toString() ? "function" : "different";
|
|
89
|
+
}
|
|
90
|
+
if (isReactElement(prev) && isReactElement(next)) {
|
|
91
|
+
return deepEqual(prev, next) ? "element" : "different";
|
|
92
|
+
}
|
|
93
|
+
return deepEqual(prev, next) ? "deep-equal" : "different";
|
|
94
|
+
}
|
|
95
|
+
function joinPath(base, key) {
|
|
96
|
+
if (typeof key === "number") return `${base}[${key}]`;
|
|
97
|
+
if (base === "") return key;
|
|
98
|
+
return /^[A-Za-z_$][\w$]*$/.test(key) ? `${base}.${key}` : `${base}[${JSON.stringify(key)}]`;
|
|
99
|
+
}
|
|
100
|
+
function diffRecords(prev, next, basePath = "") {
|
|
101
|
+
const p = prev ?? {};
|
|
102
|
+
const n = next ?? {};
|
|
103
|
+
const changes = [];
|
|
104
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(p), ...Object.keys(n)]);
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
const inPrev = Object.prototype.hasOwnProperty.call(p, key);
|
|
107
|
+
const inNext = Object.prototype.hasOwnProperty.call(n, key);
|
|
108
|
+
const path = joinPath(basePath, key);
|
|
109
|
+
if (inPrev && !inNext) {
|
|
110
|
+
changes.push({ path, kind: "removed", prev: p[key], next: void 0 });
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (!inPrev && inNext) {
|
|
114
|
+
changes.push({ path, kind: "added", prev: void 0, next: n[key] });
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const a = p[key];
|
|
118
|
+
const b = n[key];
|
|
119
|
+
if (Object.is(a, b)) continue;
|
|
120
|
+
const kind = classify(a, b);
|
|
121
|
+
if (kind === "different") {
|
|
122
|
+
changes.push({ path: firstDifferentPath(a, b, path), kind, prev: a, next: b });
|
|
123
|
+
} else {
|
|
124
|
+
changes.push({ path, kind, prev: a, next: b });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return changes;
|
|
128
|
+
}
|
|
129
|
+
function firstDifferentPath(a, b, path, depth = 0) {
|
|
130
|
+
if (depth > 8) return path;
|
|
131
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
132
|
+
if (a.length !== b.length) return `${path}.length`;
|
|
133
|
+
for (let i = 0; i < a.length; i++) {
|
|
134
|
+
if (!deepEqual(a[i], b[i])) return firstDifferentPath(a[i], b[i], joinPath(path, i), depth + 1);
|
|
135
|
+
}
|
|
136
|
+
return path;
|
|
137
|
+
}
|
|
138
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
139
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
140
|
+
for (const k of keys) {
|
|
141
|
+
if (!deepEqual(a[k], b[k])) return firstDifferentPath(a[k], b[k], joinPath(path, k), depth + 1);
|
|
142
|
+
}
|
|
143
|
+
return path;
|
|
144
|
+
}
|
|
145
|
+
return path;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/report.ts
|
|
149
|
+
var now = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
150
|
+
var isGenuine = (c) => c.kind === "different" || c.kind === "added" || c.kind === "removed";
|
|
151
|
+
var isChildren = (change) => change.path === "children";
|
|
152
|
+
function fixFor(change) {
|
|
153
|
+
if (isChildren(change) && (change.kind === "element" || change.kind === "deep-equal")) {
|
|
154
|
+
return `lift the children out of the parent's render: memoize them with useMemo, hoist static elements to module scope, or render them from a component that does not re-render`;
|
|
155
|
+
}
|
|
156
|
+
switch (change.kind) {
|
|
157
|
+
case "function":
|
|
158
|
+
return `wrap it in useCallback (or hoist it out of the parent's render)`;
|
|
159
|
+
case "element":
|
|
160
|
+
return `memoize the element with useMemo, or pass it as children from a stable parent`;
|
|
161
|
+
case "deep-equal":
|
|
162
|
+
return Array.isArray(change.next) ? `memoize the array with useMemo (same items, new reference)` : `memoize the object with useMemo, or hoist it to module scope if it is constant`;
|
|
163
|
+
default:
|
|
164
|
+
return "";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function describe(change) {
|
|
168
|
+
if (isChildren(change) && (change.kind === "element" || change.kind === "deep-equal")) {
|
|
169
|
+
return `children are new React elements with the same types and props on every render of the parent`;
|
|
170
|
+
}
|
|
171
|
+
switch (change.kind) {
|
|
172
|
+
case "deep-equal":
|
|
173
|
+
return `prop "${change.path}" is a new reference but deep-equal to the previous value`;
|
|
174
|
+
case "function":
|
|
175
|
+
return `prop "${change.path}" is a new function instance on every render`;
|
|
176
|
+
case "element":
|
|
177
|
+
return `prop "${change.path}" is a new React element with the same type and props`;
|
|
178
|
+
case "different":
|
|
179
|
+
return `prop "${change.path}" changed`;
|
|
180
|
+
case "added":
|
|
181
|
+
return `prop "${change.path}" was added`;
|
|
182
|
+
case "removed":
|
|
183
|
+
return `prop "${change.path}" was removed`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function buildReport(input) {
|
|
187
|
+
const stateChanges = input.stateChanges ?? [];
|
|
188
|
+
const hookChanges = input.hookChanges ?? [];
|
|
189
|
+
const isStateHook = (h) => h.hook === "useState" || h.hook === "useReducer";
|
|
190
|
+
const genuineProps = input.propChanges.some(isGenuine);
|
|
191
|
+
const genuineState = stateChanges.some(isGenuine) || hookChanges.some((h) => isStateHook(h) && isGenuine(h));
|
|
192
|
+
const genuineHooks = hookChanges.some((h) => !isStateHook(h) && isGenuine(h));
|
|
193
|
+
const causes = [genuineProps && "props", genuineState && "state", genuineHooks && "hooks"].filter(Boolean);
|
|
194
|
+
let trigger;
|
|
195
|
+
if (causes.length === 0) trigger = "parent";
|
|
196
|
+
else if (causes.length === 1) trigger = causes[0];
|
|
197
|
+
else trigger = "mixed";
|
|
198
|
+
const avoidable = trigger === "parent";
|
|
199
|
+
const reasons = [];
|
|
200
|
+
if (avoidable && input.propChanges.length === 0 && stateChanges.length === 0 && hookChanges.length === 0) {
|
|
201
|
+
const who = input.parent ? `<${input.parent.name}> re-rendered (${describeTrigger(input.parent.trigger)})` : "its parent re-rendered";
|
|
202
|
+
reasons.push(`re-rendered with identical props because ${who}. Wrap "${input.component}" in React.memo (or extend PureComponent).`);
|
|
203
|
+
} else if (avoidable && input.parent) {
|
|
204
|
+
reasons.push(`caused by <${input.parent.name}> re-rendering (${describeTrigger(input.parent.trigger)}).`);
|
|
205
|
+
}
|
|
206
|
+
for (const c of input.propChanges) {
|
|
207
|
+
const fix = fixFor(c);
|
|
208
|
+
reasons.push(fix ? `${describe(c)}: ${fix}.` : `${describe(c)}.`);
|
|
209
|
+
}
|
|
210
|
+
const memoized = input.memoized !== false;
|
|
211
|
+
if (avoidable && !memoized && input.propChanges.length > 0) {
|
|
212
|
+
reasons.push(
|
|
213
|
+
`"${input.component}" is not memoized, so fixing the props alone will not stop this re-render: also wrap it in React.memo (or extend PureComponent).`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
for (const c of stateChanges) {
|
|
217
|
+
if (isGenuine(c)) reasons.push(`state "${c.path}" changed.`);
|
|
218
|
+
else reasons.push(`setState was called with a value deep-equal to the current "${c.path}" (new reference, same contents).`);
|
|
219
|
+
}
|
|
220
|
+
for (const c of hookChanges) {
|
|
221
|
+
if (c.hook === "useContext" && isGenuine(c)) {
|
|
222
|
+
const where = c.provider && c.provider.component ? ` (provided by <${c.provider.component}>)` : "";
|
|
223
|
+
const keys = c.changedKeys && typeof c.totalKeys === "number" && c.changedKeys.length > 0 && c.changedKeys.length < c.totalKeys ? `: only ${c.changedKeys.map((k) => `"${k}"`).join(", ")} of ${c.totalKeys} keys changed, yet every consumer re-renders. Split the context or memoize the slices consumers read` : "";
|
|
224
|
+
reasons.push(`${c.path} changed${where}${keys}.`);
|
|
225
|
+
} else if (isGenuine(c)) reasons.push(`${c.hook} #${c.index} changed.`);
|
|
226
|
+
else if (isStateHook(c))
|
|
227
|
+
reasons.push(`${c.hook} #${c.index} was set to a value deep-equal to the current one (new reference, same contents): reuse the existing object or bail out before calling the setter.`);
|
|
228
|
+
else reasons.push(`${c.hook} #${c.index} returned a new reference that is deep-equal to the previous value: memoize the context/store value where it is produced.`);
|
|
229
|
+
}
|
|
230
|
+
const report = {
|
|
231
|
+
component: input.component,
|
|
232
|
+
instanceId: input.instanceId ?? 0,
|
|
233
|
+
commitId: input.commitId ?? 0,
|
|
234
|
+
renderCount: input.renderCount,
|
|
235
|
+
trigger,
|
|
236
|
+
avoidable,
|
|
237
|
+
memoized,
|
|
238
|
+
props: { prev: input.prevProps, next: input.nextProps },
|
|
239
|
+
propChanges: input.propChanges,
|
|
240
|
+
stateChanges,
|
|
241
|
+
hookChanges,
|
|
242
|
+
parent: input.parent ?? null,
|
|
243
|
+
owner: input.owner ?? null,
|
|
244
|
+
path: input.path ?? [],
|
|
245
|
+
reasons,
|
|
246
|
+
time: now()
|
|
247
|
+
};
|
|
248
|
+
if (input.selfDuration !== void 0) report.selfDuration = input.selfDuration;
|
|
249
|
+
if (input.treeDuration !== void 0) report.treeDuration = input.treeDuration;
|
|
250
|
+
if (input.commitPriority) report.commitPriority = input.commitPriority;
|
|
251
|
+
if (input.source) report.source = input.source;
|
|
252
|
+
return report;
|
|
253
|
+
}
|
|
254
|
+
function describeTrigger(t) {
|
|
255
|
+
switch (t) {
|
|
256
|
+
case "props":
|
|
257
|
+
return "its props changed";
|
|
258
|
+
case "state":
|
|
259
|
+
return "its state changed";
|
|
260
|
+
case "hooks":
|
|
261
|
+
return "a context or store it reads changed";
|
|
262
|
+
case "mixed":
|
|
263
|
+
return "its props and state changed";
|
|
264
|
+
default:
|
|
265
|
+
return "its own parent re-rendered";
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
var KIND_LABEL = {
|
|
269
|
+
"deep-equal": "equal by value",
|
|
270
|
+
function: "new function",
|
|
271
|
+
element: "equal element",
|
|
272
|
+
different: "changed",
|
|
273
|
+
added: "added",
|
|
274
|
+
removed: "removed"
|
|
275
|
+
};
|
|
276
|
+
function summarize(report) {
|
|
277
|
+
const counts2 = /* @__PURE__ */ new Map();
|
|
278
|
+
for (const c of [...report.propChanges, ...report.stateChanges, ...report.hookChanges]) {
|
|
279
|
+
counts2.set(c.kind, (counts2.get(c.kind) ?? 0) + 1);
|
|
280
|
+
}
|
|
281
|
+
const parts = [...counts2].map(([kind, n]) => `${n} ${KIND_LABEL[kind]}`);
|
|
282
|
+
if (parts.length === 0) parts.push("no changes");
|
|
283
|
+
return parts.join(", ");
|
|
284
|
+
}
|
|
285
|
+
function printReport(report, options) {
|
|
286
|
+
const c = options.console ?? console;
|
|
287
|
+
const open = options.collapse === false ? c.group : c.groupCollapsed;
|
|
288
|
+
const verdict = report.avoidable ? "avoidable re-render" : `re-render (${report.trigger})`;
|
|
289
|
+
open.call(c, `[rerender-lens] <${report.component}> ${verdict}: ${summarize(report)}`);
|
|
290
|
+
for (const r of report.reasons) c.log(`- ${r}`);
|
|
291
|
+
if (report.path.length) c.log(`at ${[...report.path, report.component].join(" > ")}`);
|
|
292
|
+
for (const ch of [...report.propChanges, ...report.stateChanges, ...report.hookChanges]) {
|
|
293
|
+
c.log(`${ch.path} (${KIND_LABEL[ch.kind]})`, { prev: ch.prev, next: ch.next });
|
|
294
|
+
}
|
|
295
|
+
c.log("props", report.props);
|
|
296
|
+
c.groupEnd();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/state.ts
|
|
300
|
+
var KEY = /* @__PURE__ */ Symbol.for("rerender-lens.state");
|
|
301
|
+
function getState() {
|
|
302
|
+
const g = globalThis;
|
|
303
|
+
let s = g[KEY];
|
|
304
|
+
if (!s) {
|
|
305
|
+
s = { options: {}, enabled: false, printed: /* @__PURE__ */ new Map(), detach: null, nextInstanceId: 1, nextCommitId: 1, warnedOnce: /* @__PURE__ */ new Set(), scheduled: 0, commits: 0 };
|
|
306
|
+
g[KEY] = s;
|
|
307
|
+
}
|
|
308
|
+
return s;
|
|
309
|
+
}
|
|
310
|
+
function warnOnce(key, message) {
|
|
311
|
+
const s = getState();
|
|
312
|
+
if (s.warnedOnce.has(key)) return;
|
|
313
|
+
s.warnedOnce.add(key);
|
|
314
|
+
(s.options.console ?? console).warn(`[rerender-lens] ${message}`);
|
|
315
|
+
}
|
|
316
|
+
function dispatch(report, override) {
|
|
317
|
+
const s = getState();
|
|
318
|
+
const options = override ? { ...s.options, ...override } : s.options;
|
|
319
|
+
if (!options.silent && (report.avoidable || options.logAll)) {
|
|
320
|
+
const max = options.maxReportsPerComponent ?? 0;
|
|
321
|
+
const n = (s.printed.get(report.component) ?? 0) + 1;
|
|
322
|
+
s.printed.set(report.component, n);
|
|
323
|
+
if (max <= 0 || n <= max) {
|
|
324
|
+
printReport(report, options);
|
|
325
|
+
} else if (n === max + 1) {
|
|
326
|
+
(options.console ?? console).log(
|
|
327
|
+
`[rerender-lens] <${report.component}> reached maxReportsPerComponent (${max}); further reports are not printed.`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (options.notifier) {
|
|
332
|
+
try {
|
|
333
|
+
options.notifier(report);
|
|
334
|
+
} catch (err) {
|
|
335
|
+
(options.console ?? console).warn("[rerender-lens] notifier threw", err);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/fiber.ts
|
|
341
|
+
var FunctionComponent = 0;
|
|
342
|
+
var ClassComponent = 1;
|
|
343
|
+
var HostRoot = 3;
|
|
344
|
+
var HostComponent = 5;
|
|
345
|
+
var ContextProvider = 10;
|
|
346
|
+
var ForwardRef = 11;
|
|
347
|
+
var MemoComponent = 14;
|
|
348
|
+
var SimpleMemoComponent = 15;
|
|
349
|
+
var PerformedWork = 1;
|
|
350
|
+
var HOOK = "__REACT_DEVTOOLS_GLOBAL_HOOK__";
|
|
351
|
+
function ensureDevtoolsHook() {
|
|
352
|
+
const g = globalThis;
|
|
353
|
+
let hook = g[HOOK];
|
|
354
|
+
if (!hook) {
|
|
355
|
+
let nextId = 0;
|
|
356
|
+
const renderers = /* @__PURE__ */ new Map();
|
|
357
|
+
hook = {
|
|
358
|
+
renderers,
|
|
359
|
+
supportsFiber: true,
|
|
360
|
+
inject(renderer) {
|
|
361
|
+
const id = ++nextId;
|
|
362
|
+
renderers.set(id, renderer);
|
|
363
|
+
return id;
|
|
364
|
+
},
|
|
365
|
+
onCommitFiberRoot() {
|
|
366
|
+
},
|
|
367
|
+
onCommitFiberUnmount() {
|
|
368
|
+
},
|
|
369
|
+
onScheduleFiberRoot() {
|
|
370
|
+
},
|
|
371
|
+
onPostCommitFiberRoot() {
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
g[HOOK] = hook;
|
|
375
|
+
}
|
|
376
|
+
return hook;
|
|
377
|
+
}
|
|
378
|
+
var capturedRenderers = /* @__PURE__ */ new Map();
|
|
379
|
+
var INJECT_WRAPPED = /* @__PURE__ */ Symbol.for("rerender-lens.injectWrapped");
|
|
380
|
+
function pickRenderer(r) {
|
|
381
|
+
const info = r ?? {};
|
|
382
|
+
return { version: info.version, bundleType: info.bundleType, rendererPackageName: info.rendererPackageName };
|
|
383
|
+
}
|
|
384
|
+
function wrapInject(hook) {
|
|
385
|
+
const h = hook;
|
|
386
|
+
if (h[INJECT_WRAPPED] || typeof hook.inject !== "function") return;
|
|
387
|
+
const original = hook.inject;
|
|
388
|
+
hook.inject = function(renderer) {
|
|
389
|
+
const id = original.call(this, renderer);
|
|
390
|
+
capturedRenderers.set(typeof id === "number" ? id : capturedRenderers.size + 1, pickRenderer(renderer));
|
|
391
|
+
return id;
|
|
392
|
+
};
|
|
393
|
+
h[INJECT_WRAPPED] = true;
|
|
394
|
+
}
|
|
395
|
+
function attach() {
|
|
396
|
+
const hook = ensureDevtoolsHook();
|
|
397
|
+
wrapInject(hook);
|
|
398
|
+
const previous = hook.onCommitFiberRoot;
|
|
399
|
+
const patched = function(id, root, priority, didError) {
|
|
400
|
+
try {
|
|
401
|
+
onCommit(root, priorityLabel(priority));
|
|
402
|
+
} catch (err) {
|
|
403
|
+
warnOnce("commit", `failed to inspect a commit: ${String(err)}`);
|
|
404
|
+
}
|
|
405
|
+
if (typeof previous === "function") return previous.call(this, id, root, priority, didError);
|
|
406
|
+
};
|
|
407
|
+
hook.onCommitFiberRoot = patched;
|
|
408
|
+
const previousSchedule = hook.onScheduleFiberRoot;
|
|
409
|
+
const patchedSchedule = function(id, root, children) {
|
|
410
|
+
getState().scheduled++;
|
|
411
|
+
if (typeof previousSchedule === "function") return previousSchedule.call(this, id, root, children);
|
|
412
|
+
};
|
|
413
|
+
hook.onScheduleFiberRoot = patchedSchedule;
|
|
414
|
+
return () => {
|
|
415
|
+
if (hook.onCommitFiberRoot === patched) hook.onCommitFiberRoot = previous;
|
|
416
|
+
if (hook.onScheduleFiberRoot === patchedSchedule) hook.onScheduleFiberRoot = previousSchedule;
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function priorityLabel(priority) {
|
|
420
|
+
switch (priority) {
|
|
421
|
+
case 1:
|
|
422
|
+
return "immediate";
|
|
423
|
+
case 2:
|
|
424
|
+
return "user-blocking";
|
|
425
|
+
case 3:
|
|
426
|
+
return "normal";
|
|
427
|
+
case 4:
|
|
428
|
+
return "low";
|
|
429
|
+
case 5:
|
|
430
|
+
return "idle";
|
|
431
|
+
default:
|
|
432
|
+
return void 0;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function getRenderers() {
|
|
436
|
+
const g = globalThis;
|
|
437
|
+
const hook = g[HOOK];
|
|
438
|
+
const byId = new Map(capturedRenderers);
|
|
439
|
+
if (hook && hook.renderers && typeof hook.renderers.forEach === "function") {
|
|
440
|
+
hook.renderers.forEach((r, id) => {
|
|
441
|
+
if (!byId.has(id)) byId.set(id, pickRenderer(r));
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
return [...byId.values()];
|
|
445
|
+
}
|
|
446
|
+
function isProductionReact() {
|
|
447
|
+
const renderers = getRenderers();
|
|
448
|
+
return renderers.length > 0 && renderers.every((r) => r.bundleType === 0);
|
|
449
|
+
}
|
|
450
|
+
var flagsOf = (f) => f.flags ?? f.effectTag ?? 0;
|
|
451
|
+
var isComponentTag = (tag) => tag === FunctionComponent || tag === ClassComponent || tag === ForwardRef || tag === MemoComponent || tag === SimpleMemoComponent;
|
|
452
|
+
function isMemoizedFiber(fiber) {
|
|
453
|
+
if (fiber.tag === MemoComponent || fiber.tag === SimpleMemoComponent) return true;
|
|
454
|
+
if (fiber.tag === ClassComponent) {
|
|
455
|
+
const inst = fiber.stateNode;
|
|
456
|
+
return !!(inst && inst.isPureReactComponent);
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
function durationsOf(fiber) {
|
|
461
|
+
if (typeof fiber.actualDuration !== "number") return null;
|
|
462
|
+
const tree = fiber.actualDuration;
|
|
463
|
+
let children = 0;
|
|
464
|
+
for (let c = fiber.child; c; c = c.sibling) if (typeof c.actualDuration === "number") children += c.actualDuration;
|
|
465
|
+
return { self: Math.max(0, tree - children), tree };
|
|
466
|
+
}
|
|
467
|
+
function fiberType(fiber) {
|
|
468
|
+
return fiber.elementType ?? fiber.type;
|
|
469
|
+
}
|
|
470
|
+
function fiberName(fiber) {
|
|
471
|
+
return getDisplayName(fiberType(fiber));
|
|
472
|
+
}
|
|
473
|
+
function didRender(fiber) {
|
|
474
|
+
return (flagsOf(fiber) & PerformedWork) !== 0;
|
|
475
|
+
}
|
|
476
|
+
function isHotSwapped(fiber, alt) {
|
|
477
|
+
return fiber.type !== alt.type;
|
|
478
|
+
}
|
|
479
|
+
var counts = /* @__PURE__ */ new WeakMap();
|
|
480
|
+
var ids = /* @__PURE__ */ new WeakMap();
|
|
481
|
+
var fibersById = /* @__PURE__ */ new Map();
|
|
482
|
+
var hasWeakRef = typeof WeakRef === "function";
|
|
483
|
+
function remember2(id, fiber) {
|
|
484
|
+
if (!hasWeakRef) return;
|
|
485
|
+
fibersById.set(id, new WeakRef(fiber));
|
|
486
|
+
if (fibersById.size > 5e3) {
|
|
487
|
+
for (const [k, ref] of fibersById) if (!ref.deref()) fibersById.delete(k);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function instanceIdOf(fiber) {
|
|
491
|
+
return ids.get(fiber) ?? (fiber.alternate ? ids.get(fiber.alternate) : void 0);
|
|
492
|
+
}
|
|
493
|
+
function fiberById(id) {
|
|
494
|
+
const f = fibersById.get(id)?.deref() ?? null;
|
|
495
|
+
if (!f) return null;
|
|
496
|
+
const alt = f.alternate;
|
|
497
|
+
if (alt && counts.get(alt) !== void 0 && (counts.get(alt) ?? 0) > (counts.get(f) ?? 0)) return alt;
|
|
498
|
+
return f;
|
|
499
|
+
}
|
|
500
|
+
function instanceId(fiber) {
|
|
501
|
+
const existing = instanceIdOf(fiber);
|
|
502
|
+
if (existing !== void 0) {
|
|
503
|
+
ids.set(fiber, existing);
|
|
504
|
+
remember2(existing, fiber);
|
|
505
|
+
return existing;
|
|
506
|
+
}
|
|
507
|
+
const s = getState();
|
|
508
|
+
const id = s.nextInstanceId++;
|
|
509
|
+
ids.set(fiber, id);
|
|
510
|
+
remember2(id, fiber);
|
|
511
|
+
return id;
|
|
512
|
+
}
|
|
513
|
+
function hostNodesOf(fiber, limit = 50) {
|
|
514
|
+
const out = [];
|
|
515
|
+
const stack = [];
|
|
516
|
+
for (let c = fiber.child; c; c = c.sibling) stack.push(c);
|
|
517
|
+
while (stack.length && out.length < limit) {
|
|
518
|
+
const f = stack.pop();
|
|
519
|
+
if (f.tag === HostComponent) {
|
|
520
|
+
if (typeof Element !== "undefined" && f.stateNode instanceof Element) out.push(f.stateNode);
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
for (let c = f.child; c; c = c.sibling) stack.push(c);
|
|
524
|
+
}
|
|
525
|
+
return out;
|
|
526
|
+
}
|
|
527
|
+
function fiberForNode(node) {
|
|
528
|
+
if (!node || typeof node !== "object") return null;
|
|
529
|
+
const key = Object.keys(node).find((k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$"));
|
|
530
|
+
return key ? node[key] ?? null : null;
|
|
531
|
+
}
|
|
532
|
+
function nearestComponent(fiber) {
|
|
533
|
+
let f = fiber;
|
|
534
|
+
while (f && f.tag !== HostRoot) {
|
|
535
|
+
if (isComponentTag(f.tag)) return f;
|
|
536
|
+
f = f.return;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
var INTERNAL_FRAME = /react-dom|react_jsx|jsx-(dev-)?runtime|\/react\/|node_modules\/react|react-stack-bottom-frame|react_stack_bottom_frame|scheduler/;
|
|
541
|
+
function parseStackLocation(stack) {
|
|
542
|
+
for (const line of stack.split("\n")) {
|
|
543
|
+
const m = /(?:at\s+(?:.*?\s+)?\(?|@)?((?:https?|file|webpack|vite|blob):[^\s()]+?):(\d+):(\d+)\)?\s*$/.exec(line.trim());
|
|
544
|
+
if (!m || !m[1] || INTERNAL_FRAME.test(m[1])) continue;
|
|
545
|
+
return { fileName: m[1], lineNumber: Number(m[2]), columnNumber: Number(m[3]) };
|
|
546
|
+
}
|
|
547
|
+
return void 0;
|
|
548
|
+
}
|
|
549
|
+
function sourceOf(fiber) {
|
|
550
|
+
const src = fiber._debugSource;
|
|
551
|
+
if (src && typeof src.fileName === "string") {
|
|
552
|
+
const out = { fileName: src.fileName };
|
|
553
|
+
if (typeof src.lineNumber === "number") out.lineNumber = src.lineNumber;
|
|
554
|
+
if (typeof src.columnNumber === "number") out.columnNumber = src.columnNumber;
|
|
555
|
+
return out;
|
|
556
|
+
}
|
|
557
|
+
const st = fiber._debugStack;
|
|
558
|
+
const text = typeof st === "string" ? st : st && typeof st.stack === "string" ? st.stack : null;
|
|
559
|
+
return text ? parseStackLocation(text) : void 0;
|
|
560
|
+
}
|
|
561
|
+
function bumpCount(fiber) {
|
|
562
|
+
const prev = counts.get(fiber) ?? (fiber.alternate ? counts.get(fiber.alternate) : void 0) ?? 0;
|
|
563
|
+
const n = prev + 1;
|
|
564
|
+
counts.set(fiber, n);
|
|
565
|
+
return n;
|
|
566
|
+
}
|
|
567
|
+
function componentPath(fiber) {
|
|
568
|
+
const path = [];
|
|
569
|
+
let f = fiber.return;
|
|
570
|
+
while (f && f.tag !== HostRoot) {
|
|
571
|
+
if (isComponentTag(f.tag)) path.unshift(fiberName(f));
|
|
572
|
+
f = f.return;
|
|
573
|
+
}
|
|
574
|
+
return path;
|
|
575
|
+
}
|
|
576
|
+
function ownerName(fiber) {
|
|
577
|
+
const owner = fiber._debugOwner;
|
|
578
|
+
if (!owner) return null;
|
|
579
|
+
if (owner.type !== void 0) return getDisplayName(owner.type);
|
|
580
|
+
return typeof owner.name === "string" ? owner.name : null;
|
|
581
|
+
}
|
|
582
|
+
var isStateNode = (n) => !!n.queue && typeof n.queue.lastRenderedReducer === "function";
|
|
583
|
+
var isStoreNode = (n) => !!n.queue && typeof n.queue.getSnapshot === "function";
|
|
584
|
+
function hookLabel(node) {
|
|
585
|
+
if (isStoreNode(node)) return "useSyncExternalStore";
|
|
586
|
+
if (isStateNode(node)) {
|
|
587
|
+
const r = node.queue.lastRenderedReducer;
|
|
588
|
+
return r.name === "basicStateReducer" ? "useState" : "useReducer";
|
|
589
|
+
}
|
|
590
|
+
return "state";
|
|
591
|
+
}
|
|
592
|
+
var NODELESS_HOOKS = /* @__PURE__ */ new Set(["useContext", "useDebugValue", "use"]);
|
|
593
|
+
function diffHooks(fiber, alt) {
|
|
594
|
+
const out = [];
|
|
595
|
+
if (fiber.tag === ClassComponent) return out;
|
|
596
|
+
let a = alt.memoizedState;
|
|
597
|
+
let b = fiber.memoizedState;
|
|
598
|
+
if (!a || !b || typeof b !== "object" || !("next" in b)) return out;
|
|
599
|
+
const types = fiber._debugHookTypes?.filter((t) => !NODELESS_HOOKS.has(t));
|
|
600
|
+
let nodeCount = 0;
|
|
601
|
+
for (let n = b; n; n = n.next) nodeCount++;
|
|
602
|
+
const labels = types && types.length === nodeCount ? types : null;
|
|
603
|
+
let i = 0;
|
|
604
|
+
while (a && b) {
|
|
605
|
+
if ((isStateNode(b) || isStoreNode(b)) && !Object.is(a.memoizedState, b.memoizedState)) {
|
|
606
|
+
const hook = labels?.[i] ?? hookLabel(b);
|
|
607
|
+
out.push({
|
|
608
|
+
path: `${hook}#${i}`,
|
|
609
|
+
hook,
|
|
610
|
+
index: i,
|
|
611
|
+
kind: classify(a.memoizedState, b.memoizedState),
|
|
612
|
+
prev: a.memoizedState,
|
|
613
|
+
next: b.memoizedState
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
a = a.next;
|
|
617
|
+
b = b.next;
|
|
618
|
+
i++;
|
|
619
|
+
}
|
|
620
|
+
return out;
|
|
621
|
+
}
|
|
622
|
+
function findProvider(fiber, context) {
|
|
623
|
+
let f = fiber.return;
|
|
624
|
+
while (f && f.tag !== HostRoot) {
|
|
625
|
+
if (f.tag === ContextProvider) {
|
|
626
|
+
const t = f.type;
|
|
627
|
+
if (t === context || t && typeof t === "object" && t._context === context) return f;
|
|
628
|
+
}
|
|
629
|
+
f = f.return;
|
|
630
|
+
}
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
var isPlainObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
|
|
634
|
+
function diffContexts(fiber, alt) {
|
|
635
|
+
const out = [];
|
|
636
|
+
let a = alt.dependencies?.firstContext ?? null;
|
|
637
|
+
let b = fiber.dependencies?.firstContext ?? null;
|
|
638
|
+
let i = 0;
|
|
639
|
+
while (a && b) {
|
|
640
|
+
if (a.context === b.context && !Object.is(a.memoizedValue, b.memoizedValue)) {
|
|
641
|
+
const name = b.context.displayName ?? "Context";
|
|
642
|
+
const change = {
|
|
643
|
+
path: `useContext(${name})`,
|
|
644
|
+
hook: "useContext",
|
|
645
|
+
index: i,
|
|
646
|
+
kind: classify(a.memoizedValue, b.memoizedValue),
|
|
647
|
+
prev: a.memoizedValue,
|
|
648
|
+
next: b.memoizedValue
|
|
649
|
+
};
|
|
650
|
+
const provider = findProvider(fiber, b.context);
|
|
651
|
+
if (provider) {
|
|
652
|
+
const path = componentPath(provider);
|
|
653
|
+
change.provider = { component: path[path.length - 1] ?? null, path };
|
|
654
|
+
}
|
|
655
|
+
if (isPlainObject2(a.memoizedValue) && isPlainObject2(b.memoizedValue)) {
|
|
656
|
+
const prev = a.memoizedValue;
|
|
657
|
+
const next = b.memoizedValue;
|
|
658
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
|
|
659
|
+
change.changedKeys = [...keys].filter((k) => !Object.is(prev[k], next[k]));
|
|
660
|
+
change.totalKeys = keys.size;
|
|
661
|
+
}
|
|
662
|
+
out.push(change);
|
|
663
|
+
}
|
|
664
|
+
a = a.next;
|
|
665
|
+
b = b.next;
|
|
666
|
+
i++;
|
|
667
|
+
}
|
|
668
|
+
return out;
|
|
669
|
+
}
|
|
670
|
+
var isGenuine2 = (c) => c.kind === "different" || c.kind === "added" || c.kind === "removed";
|
|
671
|
+
function analyze(fiber, alt, trackHooks) {
|
|
672
|
+
const prevProps = alt.memoizedProps ?? {};
|
|
673
|
+
const nextProps = fiber.memoizedProps ?? {};
|
|
674
|
+
const propChanges = prevProps === nextProps ? [] : diffRecords(prevProps, nextProps);
|
|
675
|
+
let stateChanges = [];
|
|
676
|
+
let hookChanges = [];
|
|
677
|
+
if (fiber.tag === ClassComponent) {
|
|
678
|
+
if (alt.memoizedState !== fiber.memoizedState) {
|
|
679
|
+
stateChanges = diffRecords(
|
|
680
|
+
alt.memoizedState ?? {},
|
|
681
|
+
fiber.memoizedState ?? {}
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
} else if (trackHooks) {
|
|
685
|
+
hookChanges = [...diffHooks(fiber, alt), ...diffContexts(fiber, alt)];
|
|
686
|
+
}
|
|
687
|
+
const isStateHook = (h) => h.hook !== "useContext" && h.hook !== "useSyncExternalStore";
|
|
688
|
+
const causes = [];
|
|
689
|
+
if (propChanges.some(isGenuine2)) causes.push("props");
|
|
690
|
+
if (stateChanges.some(isGenuine2) || hookChanges.some((h) => isStateHook(h) && isGenuine2(h))) causes.push("state");
|
|
691
|
+
if (hookChanges.some((h) => !isStateHook(h) && isGenuine2(h))) causes.push("hooks");
|
|
692
|
+
const trigger = causes.length === 0 ? "parent" : causes.length === 1 ? causes[0] : "mixed";
|
|
693
|
+
return { propChanges, stateChanges, hookChanges, trigger };
|
|
694
|
+
}
|
|
695
|
+
function nearestRenderedAncestor(fiber, cache, trackHooks) {
|
|
696
|
+
let f = fiber.return;
|
|
697
|
+
while (f && f.tag !== HostRoot) {
|
|
698
|
+
if (isComponentTag(f.tag) && f.alternate && didRender(f)) {
|
|
699
|
+
let info = cache.get(f);
|
|
700
|
+
if (info === void 0) {
|
|
701
|
+
info = { name: fiberName(f), trigger: analyze(f, f.alternate, trackHooks).trigger };
|
|
702
|
+
cache.set(f, info);
|
|
703
|
+
}
|
|
704
|
+
return info;
|
|
705
|
+
}
|
|
706
|
+
f = f.return;
|
|
707
|
+
}
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
function onCommit(root, commitPriority) {
|
|
711
|
+
const s = getState();
|
|
712
|
+
if (!s.enabled) return;
|
|
713
|
+
s.commits++;
|
|
714
|
+
const o = s.options;
|
|
715
|
+
const trackHooks = o.trackHooks !== false;
|
|
716
|
+
const rendered = [];
|
|
717
|
+
let hot = false;
|
|
718
|
+
const stack = [root.current];
|
|
719
|
+
while (stack.length) {
|
|
720
|
+
const fiber = stack.pop();
|
|
721
|
+
const alt = fiber.alternate;
|
|
722
|
+
if (alt && isComponentTag(fiber.tag) && didRender(fiber)) {
|
|
723
|
+
if (isHotSwapped(fiber, alt)) hot = true;
|
|
724
|
+
if (shouldTrack(fiberType(fiber), o)) rendered.push(fiber);
|
|
725
|
+
}
|
|
726
|
+
if (!alt || fiber.child !== alt.child) {
|
|
727
|
+
for (let c = fiber.child; c; c = c.sibling) stack.push(c);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (hot && o.ignoreHotReload !== false) return;
|
|
731
|
+
rendered.reverse();
|
|
732
|
+
if (rendered.length === 0) return;
|
|
733
|
+
const commitId = s.nextCommitId++;
|
|
734
|
+
const parentCache = /* @__PURE__ */ new Map();
|
|
735
|
+
for (const fiber of rendered) {
|
|
736
|
+
const alt = fiber.alternate;
|
|
737
|
+
const a = analyze(fiber, alt, trackHooks);
|
|
738
|
+
const durations = durationsOf(fiber);
|
|
739
|
+
const report = buildReport({
|
|
740
|
+
component: fiberName(fiber),
|
|
741
|
+
instanceId: instanceId(fiber),
|
|
742
|
+
renderCount: bumpCount(fiber),
|
|
743
|
+
prevProps: alt.memoizedProps ?? {},
|
|
744
|
+
nextProps: fiber.memoizedProps ?? {},
|
|
745
|
+
propChanges: a.propChanges,
|
|
746
|
+
stateChanges: a.stateChanges,
|
|
747
|
+
hookChanges: a.hookChanges,
|
|
748
|
+
parent: a.trigger === "parent" ? nearestRenderedAncestor(fiber, parentCache, trackHooks) : null,
|
|
749
|
+
owner: ownerName(fiber),
|
|
750
|
+
path: componentPath(fiber),
|
|
751
|
+
memoized: isMemoizedFiber(fiber),
|
|
752
|
+
selfDuration: durations ? durations.self : void 0,
|
|
753
|
+
treeDuration: durations ? durations.tree : void 0,
|
|
754
|
+
commitId,
|
|
755
|
+
commitPriority,
|
|
756
|
+
source: sourceOf(fiber)
|
|
757
|
+
});
|
|
758
|
+
dispatch(report);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// src/tracker.ts
|
|
763
|
+
var MEMO = /* @__PURE__ */ Symbol.for("react.memo");
|
|
764
|
+
var FORWARD_REF = /* @__PURE__ */ Symbol.for("react.forward_ref");
|
|
765
|
+
var isMemo = (t) => typeof t === "object" && t !== null && t.$$typeof === MEMO;
|
|
766
|
+
var isForwardRef = (t) => typeof t === "object" && t !== null && t.$$typeof === FORWARD_REF;
|
|
767
|
+
var isClass = (t) => typeof t === "function" && !!t.prototype?.isReactComponent;
|
|
768
|
+
var isPureClass = (t) => isClass(t) && !!t.prototype.isPureReactComponent;
|
|
769
|
+
var isComponentLike = (t) => typeof t === "function" || isMemo(t) || isForwardRef(t);
|
|
770
|
+
function getDisplayName(type) {
|
|
771
|
+
if (typeof type === "string") return type;
|
|
772
|
+
if (isMemo(type)) return type.displayName ?? getDisplayName(type.type);
|
|
773
|
+
if (isForwardRef(type)) return type.displayName ?? getDisplayName(type.render);
|
|
774
|
+
if (typeof type === "function") {
|
|
775
|
+
const t = type;
|
|
776
|
+
return t.displayName ?? (t.name || "Anonymous");
|
|
777
|
+
}
|
|
778
|
+
return "Anonymous";
|
|
779
|
+
}
|
|
780
|
+
function hasMarker(type) {
|
|
781
|
+
if (type === null || typeof type !== "function" && typeof type !== "object") return false;
|
|
782
|
+
if (type[MARKER] === true) return true;
|
|
783
|
+
if (isMemo(type)) return hasMarker(type.type);
|
|
784
|
+
if (isForwardRef(type)) return hasMarker(type.render);
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
function matches(m, name) {
|
|
788
|
+
if (typeof m === "string") return m === name;
|
|
789
|
+
if (m instanceof RegExp) return m.test(name);
|
|
790
|
+
return m(name);
|
|
791
|
+
}
|
|
792
|
+
function shouldTrack(type, o) {
|
|
793
|
+
if (!isComponentLike(type)) return false;
|
|
794
|
+
const name = getDisplayName(type);
|
|
795
|
+
if (o.exclude?.some((m) => matches(m, name))) return false;
|
|
796
|
+
if (hasMarker(type)) return true;
|
|
797
|
+
if (o.include?.some((m) => matches(m, name))) return true;
|
|
798
|
+
if (o.trackAllComponents) return true;
|
|
799
|
+
if (o.trackAllMemoized) return isMemo(type) || isPureClass(type);
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
function init(options = {}) {
|
|
803
|
+
const s = getState();
|
|
804
|
+
s.options = { ...options };
|
|
805
|
+
s.printed.clear();
|
|
806
|
+
if (!s.enabled) {
|
|
807
|
+
s.detach = attach();
|
|
808
|
+
s.enabled = true;
|
|
809
|
+
}
|
|
810
|
+
return disable;
|
|
811
|
+
}
|
|
812
|
+
function configure(options) {
|
|
813
|
+
const s = getState();
|
|
814
|
+
s.options = { ...s.options, ...options };
|
|
815
|
+
}
|
|
816
|
+
function disable() {
|
|
817
|
+
const s = getState();
|
|
818
|
+
if (!s.enabled) return;
|
|
819
|
+
s.detach?.();
|
|
820
|
+
s.detach = null;
|
|
821
|
+
s.enabled = false;
|
|
822
|
+
}
|
|
823
|
+
function isEnabled() {
|
|
824
|
+
return getState().enabled;
|
|
825
|
+
}
|
|
826
|
+
function track(component, name) {
|
|
827
|
+
const t = component;
|
|
828
|
+
t[MARKER] = true;
|
|
829
|
+
if (name) t.displayName = name;
|
|
830
|
+
return component;
|
|
831
|
+
}
|
|
6
832
|
function useWhyRerender(name, values, options) {
|
|
7
833
|
const ref = react.useRef({
|
|
8
834
|
committed: null,
|
|
@@ -13,16 +839,16 @@ function useWhyRerender(name, values, options) {
|
|
|
13
839
|
if (st.committed && !st.reported) {
|
|
14
840
|
st.reported = true;
|
|
15
841
|
st.count++;
|
|
16
|
-
const propChanges =
|
|
17
|
-
|
|
18
|
-
|
|
842
|
+
const propChanges = diffRecords(st.committed, values);
|
|
843
|
+
dispatch(
|
|
844
|
+
buildReport({
|
|
19
845
|
component: name,
|
|
20
846
|
renderCount: st.count,
|
|
21
847
|
prevProps: st.committed,
|
|
22
848
|
nextProps: values,
|
|
23
849
|
propChanges
|
|
24
850
|
}),
|
|
25
|
-
options ? { ...
|
|
851
|
+
options ? { ...getState().options, ...options } : void 0
|
|
26
852
|
);
|
|
27
853
|
}
|
|
28
854
|
react.useEffect(() => {
|
|
@@ -62,9 +888,99 @@ function combineNotifiers(...notifiers) {
|
|
|
62
888
|
};
|
|
63
889
|
}
|
|
64
890
|
|
|
891
|
+
// src/overlay.ts
|
|
892
|
+
var ROOT_ID = "rerender-lens-overlay";
|
|
893
|
+
var layer = null;
|
|
894
|
+
function ensureLayer() {
|
|
895
|
+
if (typeof document === "undefined" || !document.body) return null;
|
|
896
|
+
if (layer && layer.root.isConnected) return layer;
|
|
897
|
+
let root = document.getElementById(ROOT_ID);
|
|
898
|
+
if (!root) {
|
|
899
|
+
root = document.createElement("div");
|
|
900
|
+
root.id = ROOT_ID;
|
|
901
|
+
root.setAttribute("aria-hidden", "true");
|
|
902
|
+
Object.assign(root.style, {
|
|
903
|
+
position: "fixed",
|
|
904
|
+
inset: "0",
|
|
905
|
+
pointerEvents: "none",
|
|
906
|
+
zIndex: "2147483647",
|
|
907
|
+
overflow: "visible"
|
|
908
|
+
});
|
|
909
|
+
document.body.appendChild(root);
|
|
910
|
+
}
|
|
911
|
+
layer = { root, sticky: [] };
|
|
912
|
+
return layer;
|
|
913
|
+
}
|
|
914
|
+
function box(rect, color, fill, label) {
|
|
915
|
+
const b = document.createElement("div");
|
|
916
|
+
Object.assign(b.style, {
|
|
917
|
+
position: "absolute",
|
|
918
|
+
left: `${rect.left}px`,
|
|
919
|
+
top: `${rect.top}px`,
|
|
920
|
+
width: `${Math.max(rect.width, 2)}px`,
|
|
921
|
+
height: `${Math.max(rect.height, 2)}px`,
|
|
922
|
+
boxSizing: "border-box",
|
|
923
|
+
border: `2px solid ${color}`,
|
|
924
|
+
background: fill,
|
|
925
|
+
borderRadius: "2px"
|
|
926
|
+
});
|
|
927
|
+
if (label) {
|
|
928
|
+
const tag = document.createElement("span");
|
|
929
|
+
tag.textContent = label;
|
|
930
|
+
Object.assign(tag.style, {
|
|
931
|
+
position: "absolute",
|
|
932
|
+
left: "-2px",
|
|
933
|
+
top: rect.top > 20 ? "-20px" : "100%",
|
|
934
|
+
font: "11px/16px system-ui, sans-serif",
|
|
935
|
+
color: "#fff",
|
|
936
|
+
background: color,
|
|
937
|
+
padding: "1px 5px",
|
|
938
|
+
borderRadius: "2px",
|
|
939
|
+
whiteSpace: "nowrap"
|
|
940
|
+
});
|
|
941
|
+
b.appendChild(tag);
|
|
942
|
+
}
|
|
943
|
+
return b;
|
|
944
|
+
}
|
|
945
|
+
function highlight(target) {
|
|
946
|
+
const l = ensureLayer();
|
|
947
|
+
if (!l) return;
|
|
948
|
+
for (const s of l.sticky) s.remove();
|
|
949
|
+
l.sticky = [];
|
|
950
|
+
if (!target) return;
|
|
951
|
+
for (const node of target.nodes) {
|
|
952
|
+
const rect = node.getBoundingClientRect();
|
|
953
|
+
if (rect.width === 0 && rect.height === 0) continue;
|
|
954
|
+
const b = box(rect, "#1a73e8", "rgba(26, 115, 232, 0.12)", target.label);
|
|
955
|
+
l.root.appendChild(b);
|
|
956
|
+
l.sticky.push(b);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
function clearHighlight() {
|
|
960
|
+
highlight(null);
|
|
961
|
+
}
|
|
962
|
+
function flash(target, duration = 500) {
|
|
963
|
+
const l = ensureLayer();
|
|
964
|
+
if (!l) return;
|
|
965
|
+
for (const node of target.nodes) {
|
|
966
|
+
const rect = node.getBoundingClientRect();
|
|
967
|
+
if (rect.width === 0 && rect.height === 0) continue;
|
|
968
|
+
const b = box(rect, "#d93025", "rgba(217, 48, 37, 0.15)");
|
|
969
|
+
b.style.transition = `opacity ${duration}ms ease-out`;
|
|
970
|
+
l.root.appendChild(b);
|
|
971
|
+
requestAnimationFrame(() => {
|
|
972
|
+
b.style.opacity = "0";
|
|
973
|
+
});
|
|
974
|
+
setTimeout(() => b.remove(), duration + 50);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/version.ts
|
|
979
|
+
var VERSION = "0.2.0" ;
|
|
980
|
+
|
|
65
981
|
// src/devtools.ts
|
|
66
982
|
var DEVTOOLS_MARKER = "__rerenderLens";
|
|
67
|
-
var PROTOCOL_VERSION =
|
|
983
|
+
var PROTOCOL_VERSION = 2;
|
|
68
984
|
function serialize(value, maxDepth = 6, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
69
985
|
if (value === null || value === void 0) return value;
|
|
70
986
|
const t = typeof value;
|
|
@@ -78,7 +994,12 @@ function serialize(value, maxDepth = 6, seen = /* @__PURE__ */ new WeakSet(), de
|
|
|
78
994
|
if (depth >= maxDepth) return "[\u2026]";
|
|
79
995
|
seen.add(obj);
|
|
80
996
|
try {
|
|
81
|
-
if (
|
|
997
|
+
if (isReactElement(obj)) {
|
|
998
|
+
const out2 = { $type: "element", name: getDisplayName(obj.type) };
|
|
999
|
+
if (obj.key !== null && obj.key !== void 0) out2.key = String(obj.key);
|
|
1000
|
+
out2.props = serialize(obj.props, maxDepth, seen, depth + 1);
|
|
1001
|
+
return out2;
|
|
1002
|
+
}
|
|
82
1003
|
if (obj instanceof Date) return { $type: "Date", value: obj.toISOString() };
|
|
83
1004
|
if (obj instanceof RegExp) return { $type: "RegExp", value: String(obj) };
|
|
84
1005
|
if (obj instanceof Map) {
|
|
@@ -96,20 +1017,66 @@ function serialize(value, maxDepth = 6, seen = /* @__PURE__ */ new WeakSet(), de
|
|
|
96
1017
|
seen.delete(obj);
|
|
97
1018
|
}
|
|
98
1019
|
}
|
|
1020
|
+
var matcherToString = (m) => typeof m === "string" ? m : m instanceof RegExp ? String(m) : null;
|
|
1021
|
+
var stringToMatcher = (s) => {
|
|
1022
|
+
const m = /^\/(.+)\/([a-z]*)$/.exec(s);
|
|
1023
|
+
if (m && m[1] !== void 0) {
|
|
1024
|
+
try {
|
|
1025
|
+
return new RegExp(m[1], m[2]);
|
|
1026
|
+
} catch {
|
|
1027
|
+
return s;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return s;
|
|
1031
|
+
};
|
|
1032
|
+
function serializeOptions(o) {
|
|
1033
|
+
const out = {};
|
|
1034
|
+
const bools = ["trackAllMemoized", "trackAllComponents", "trackHooks", "logAll", "silent", "collapse", "ignoreHotReload"];
|
|
1035
|
+
for (const k of bools) if (typeof o[k] === "boolean") out[k] = o[k];
|
|
1036
|
+
if (typeof o.maxReportsPerComponent === "number") out.maxReportsPerComponent = o.maxReportsPerComponent;
|
|
1037
|
+
if (o.include) out.include = o.include.map(matcherToString).filter((x) => x !== null);
|
|
1038
|
+
if (o.exclude) out.exclude = o.exclude.map(matcherToString).filter((x) => x !== null);
|
|
1039
|
+
return out;
|
|
1040
|
+
}
|
|
1041
|
+
function deserializeOptions(o) {
|
|
1042
|
+
const out = {};
|
|
1043
|
+
const bools = ["trackAllMemoized", "trackAllComponents", "trackHooks", "logAll", "silent", "collapse", "ignoreHotReload"];
|
|
1044
|
+
for (const k of bools) if (typeof o[k] === "boolean") out[k] = o[k];
|
|
1045
|
+
if (typeof o.maxReportsPerComponent === "number") out.maxReportsPerComponent = o.maxReportsPerComponent;
|
|
1046
|
+
if (Array.isArray(o.include)) out.include = o.include.filter((s) => typeof s === "string" && s).map(stringToMatcher);
|
|
1047
|
+
if (Array.isArray(o.exclude)) out.exclude = o.exclude.filter((s) => typeof s === "string" && s).map(stringToMatcher);
|
|
1048
|
+
return out;
|
|
1049
|
+
}
|
|
99
1050
|
function createDevtoolsNotifier(options = {}) {
|
|
100
1051
|
const bufferSize = options.bufferSize ?? 300;
|
|
101
1052
|
const maxDepth = options.maxDepth ?? 6;
|
|
102
1053
|
const target = options.target ?? (typeof window !== "undefined" ? window : void 0);
|
|
103
1054
|
const buffer = [];
|
|
1055
|
+
let seq = 0;
|
|
1056
|
+
let flashOn = options.flashAvoidable ?? false;
|
|
104
1057
|
const post = (type, payload) => {
|
|
105
1058
|
if (!target) return;
|
|
106
1059
|
const msg = { [DEVTOOLS_MARKER]: true, version: PROTOCOL_VERSION, type, payload };
|
|
107
1060
|
target.postMessage(msg, "*");
|
|
108
1061
|
};
|
|
1062
|
+
const source = options.source ?? "page";
|
|
1063
|
+
const info = () => ({
|
|
1064
|
+
count: buffer.length,
|
|
1065
|
+
library: VERSION,
|
|
1066
|
+
protocol: PROTOCOL_VERSION,
|
|
1067
|
+
react: getRenderers(),
|
|
1068
|
+
production: isProductionReact(),
|
|
1069
|
+
enabled: isEnabled(),
|
|
1070
|
+
options: serializeOptions(getState().options),
|
|
1071
|
+
source,
|
|
1072
|
+
injected: typeof window !== "undefined" && typeof window.__RERENDER_LENS_INJECTED__ === "string",
|
|
1073
|
+
scheduled: getState().scheduled,
|
|
1074
|
+
commits: getState().commits
|
|
1075
|
+
});
|
|
109
1076
|
const bridge = {
|
|
110
1077
|
replay: () => {
|
|
111
|
-
post("hello",
|
|
112
|
-
for (const
|
|
1078
|
+
post("hello", info());
|
|
1079
|
+
for (const e of buffer) post("report", e.payload);
|
|
113
1080
|
},
|
|
114
1081
|
clear: () => {
|
|
115
1082
|
buffer.length = 0;
|
|
@@ -118,80 +1085,96 @@ function createDevtoolsNotifier(options = {}) {
|
|
|
118
1085
|
get size() {
|
|
119
1086
|
return buffer.length;
|
|
120
1087
|
},
|
|
121
|
-
version: PROTOCOL_VERSION
|
|
1088
|
+
version: PROTOCOL_VERSION,
|
|
1089
|
+
library: VERSION,
|
|
1090
|
+
info,
|
|
1091
|
+
pull: (since = 0) => {
|
|
1092
|
+
const first = buffer[0];
|
|
1093
|
+
const dropped = since > 0 && !!first && first.seq > since + 1;
|
|
1094
|
+
const reports = buffer.filter((e) => e.seq > since).map((e) => e.payload);
|
|
1095
|
+
return { seq, reports, dropped };
|
|
1096
|
+
},
|
|
1097
|
+
configure: (next) => {
|
|
1098
|
+
configure(deserializeOptions(next ?? {}));
|
|
1099
|
+
return serializeOptions(getState().options);
|
|
1100
|
+
},
|
|
1101
|
+
getOptions: () => serializeOptions(getState().options),
|
|
1102
|
+
highlight: (id) => {
|
|
1103
|
+
if (id === null || id === void 0) {
|
|
1104
|
+
clearHighlight();
|
|
1105
|
+
return true;
|
|
1106
|
+
}
|
|
1107
|
+
const fiber = fiberById(id);
|
|
1108
|
+
if (!fiber) {
|
|
1109
|
+
clearHighlight();
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
const nodes = hostNodesOf(fiber);
|
|
1113
|
+
highlight({ nodes, label: fiberName(fiber) });
|
|
1114
|
+
return nodes.length > 0;
|
|
1115
|
+
},
|
|
1116
|
+
flashAvoidable: (on) => {
|
|
1117
|
+
flashOn = !!on;
|
|
1118
|
+
},
|
|
1119
|
+
inspect: (node) => {
|
|
1120
|
+
const comp = nearestComponent(fiberForNode(node));
|
|
1121
|
+
if (!comp) return null;
|
|
1122
|
+
const id = instanceIdOf(comp) ?? null;
|
|
1123
|
+
const path = [];
|
|
1124
|
+
let f = comp.return;
|
|
1125
|
+
while (f) {
|
|
1126
|
+
if (f.tag === 0 || f.tag === 1 || f.tag === 11 || f.tag === 14 || f.tag === 15) path.unshift(fiberName(f));
|
|
1127
|
+
f = f.return;
|
|
1128
|
+
}
|
|
1129
|
+
return {
|
|
1130
|
+
component: fiberName(comp),
|
|
1131
|
+
instanceId: id,
|
|
1132
|
+
tracked: shouldTrack(fiberType(comp), getState().options),
|
|
1133
|
+
path,
|
|
1134
|
+
reports: id === null ? [] : buffer.filter((e) => e.instanceId === id).slice(-20).map((e) => e.payload)
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
122
1137
|
};
|
|
123
1138
|
if (typeof window !== "undefined") window.__RERENDER_LENS_DEVTOOLS__ = bridge;
|
|
124
|
-
post("hello",
|
|
1139
|
+
post("hello", info());
|
|
125
1140
|
return (report) => {
|
|
126
1141
|
const payload = serialize(report, maxDepth);
|
|
127
|
-
buffer.push(payload);
|
|
1142
|
+
buffer.push({ seq: ++seq, instanceId: report.instanceId, payload });
|
|
128
1143
|
if (buffer.length > bufferSize) buffer.splice(0, buffer.length - bufferSize);
|
|
129
1144
|
post("report", payload);
|
|
1145
|
+
if (flashOn && report.avoidable && report.instanceId) {
|
|
1146
|
+
const fiber = fiberById(report.instanceId);
|
|
1147
|
+
if (fiber) flash({ nodes: hostNodesOf(fiber), label: report.component });
|
|
1148
|
+
}
|
|
130
1149
|
};
|
|
131
1150
|
}
|
|
132
1151
|
|
|
133
|
-
Object.defineProperty(exports, "MARKER", {
|
|
134
|
-
enumerable: true,
|
|
135
|
-
get: function () { return chunkDOYB4MKH_cjs.MARKER; }
|
|
136
|
-
});
|
|
137
|
-
Object.defineProperty(exports, "buildReport", {
|
|
138
|
-
enumerable: true,
|
|
139
|
-
get: function () { return chunkDOYB4MKH_cjs.buildReport; }
|
|
140
|
-
});
|
|
141
|
-
Object.defineProperty(exports, "classify", {
|
|
142
|
-
enumerable: true,
|
|
143
|
-
get: function () { return chunkDOYB4MKH_cjs.classify; }
|
|
144
|
-
});
|
|
145
|
-
Object.defineProperty(exports, "configure", {
|
|
146
|
-
enumerable: true,
|
|
147
|
-
get: function () { return chunkDOYB4MKH_cjs.configure; }
|
|
148
|
-
});
|
|
149
|
-
Object.defineProperty(exports, "deepEqual", {
|
|
150
|
-
enumerable: true,
|
|
151
|
-
get: function () { return chunkDOYB4MKH_cjs.deepEqual; }
|
|
152
|
-
});
|
|
153
|
-
Object.defineProperty(exports, "diffRecords", {
|
|
154
|
-
enumerable: true,
|
|
155
|
-
get: function () { return chunkDOYB4MKH_cjs.diffRecords; }
|
|
156
|
-
});
|
|
157
|
-
Object.defineProperty(exports, "disable", {
|
|
158
|
-
enumerable: true,
|
|
159
|
-
get: function () { return chunkDOYB4MKH_cjs.disable; }
|
|
160
|
-
});
|
|
161
|
-
Object.defineProperty(exports, "getDisplayName", {
|
|
162
|
-
enumerable: true,
|
|
163
|
-
get: function () { return chunkDOYB4MKH_cjs.getDisplayName; }
|
|
164
|
-
});
|
|
165
|
-
Object.defineProperty(exports, "init", {
|
|
166
|
-
enumerable: true,
|
|
167
|
-
get: function () { return chunkDOYB4MKH_cjs.init; }
|
|
168
|
-
});
|
|
169
|
-
Object.defineProperty(exports, "isEnabled", {
|
|
170
|
-
enumerable: true,
|
|
171
|
-
get: function () { return chunkDOYB4MKH_cjs.isEnabled; }
|
|
172
|
-
});
|
|
173
|
-
Object.defineProperty(exports, "printReport", {
|
|
174
|
-
enumerable: true,
|
|
175
|
-
get: function () { return chunkDOYB4MKH_cjs.printReport; }
|
|
176
|
-
});
|
|
177
|
-
Object.defineProperty(exports, "resolveType", {
|
|
178
|
-
enumerable: true,
|
|
179
|
-
get: function () { return chunkDOYB4MKH_cjs.resolveType; }
|
|
180
|
-
});
|
|
181
|
-
Object.defineProperty(exports, "summarize", {
|
|
182
|
-
enumerable: true,
|
|
183
|
-
get: function () { return chunkDOYB4MKH_cjs.summarize; }
|
|
184
|
-
});
|
|
185
|
-
Object.defineProperty(exports, "track", {
|
|
186
|
-
enumerable: true,
|
|
187
|
-
get: function () { return chunkDOYB4MKH_cjs.track; }
|
|
188
|
-
});
|
|
189
1152
|
exports.DEVTOOLS_MARKER = DEVTOOLS_MARKER;
|
|
1153
|
+
exports.MARKER = MARKER;
|
|
190
1154
|
exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
|
|
1155
|
+
exports.VERSION = VERSION;
|
|
1156
|
+
exports.buildReport = buildReport;
|
|
1157
|
+
exports.classify = classify;
|
|
191
1158
|
exports.combineNotifiers = combineNotifiers;
|
|
1159
|
+
exports.configure = configure;
|
|
192
1160
|
exports.createCollector = createCollector;
|
|
193
1161
|
exports.createDevtoolsNotifier = createDevtoolsNotifier;
|
|
1162
|
+
exports.deepEqual = deepEqual;
|
|
1163
|
+
exports.deserializeOptions = deserializeOptions;
|
|
1164
|
+
exports.diffRecords = diffRecords;
|
|
1165
|
+
exports.disable = disable;
|
|
1166
|
+
exports.ensureDevtoolsHook = ensureDevtoolsHook;
|
|
1167
|
+
exports.getDisplayName = getDisplayName;
|
|
1168
|
+
exports.getRenderers = getRenderers;
|
|
1169
|
+
exports.init = init;
|
|
1170
|
+
exports.isEnabled = isEnabled;
|
|
1171
|
+
exports.isProductionReact = isProductionReact;
|
|
1172
|
+
exports.printReport = printReport;
|
|
194
1173
|
exports.serialize = serialize;
|
|
1174
|
+
exports.serializeOptions = serializeOptions;
|
|
1175
|
+
exports.shouldTrack = shouldTrack;
|
|
1176
|
+
exports.summarize = summarize;
|
|
1177
|
+
exports.track = track;
|
|
195
1178
|
exports.useWhyRerender = useWhyRerender;
|
|
196
1179
|
//# sourceMappingURL=index.cjs.map
|
|
197
1180
|
//# sourceMappingURL=index.cjs.map
|