rerender-lens 0.5.0 → 0.7.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +23 -4
  3. package/dist/{budget-CRswn-nC.d.cts → budget-CA1MS7lB.d.cts} +1 -1
  4. package/dist/{budget-BnJlYwrV.d.ts → budget-ok1VDJ4t.d.ts} +1 -1
  5. package/dist/cli.cjs +78 -26
  6. package/dist/cli.cjs.map +1 -1
  7. package/dist/cli.js +78 -26
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{devtools-BYWat7nQ.d.ts → devtools-Cl5n_52v.d.ts} +11 -2
  10. package/dist/{devtools-DJi_6AfJ.d.cts → devtools-UwQx2VYS.d.cts} +11 -2
  11. package/dist/index.cjs +99 -9
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +13 -7
  14. package/dist/index.d.ts +13 -7
  15. package/dist/index.js +99 -10
  16. package/dist/index.js.map +1 -1
  17. package/dist/jest-setup.cjs +20 -3
  18. package/dist/jest-setup.cjs.map +1 -1
  19. package/dist/jest-setup.d.cts +4 -4
  20. package/dist/jest-setup.d.ts +4 -4
  21. package/dist/jest-setup.js +20 -3
  22. package/dist/jest-setup.js.map +1 -1
  23. package/dist/jest.cjs +20 -3
  24. package/dist/jest.cjs.map +1 -1
  25. package/dist/jest.d.cts +4 -4
  26. package/dist/jest.d.ts +4 -4
  27. package/dist/jest.js +20 -3
  28. package/dist/jest.js.map +1 -1
  29. package/dist/{notifiers-DwKlvv4H.d.cts → notifiers-B5poAEvE.d.cts} +2 -2
  30. package/dist/{notifiers-FNzKHs0a.d.ts → notifiers-CqQW0Eum.d.ts} +2 -2
  31. package/dist/playwright.d.cts +4 -4
  32. package/dist/playwright.d.ts +4 -4
  33. package/dist/relay.cjs +65 -21
  34. package/dist/relay.cjs.map +1 -1
  35. package/dist/relay.d.cts +25 -3
  36. package/dist/relay.d.ts +25 -3
  37. package/dist/relay.js +65 -21
  38. package/dist/relay.js.map +1 -1
  39. package/dist/rerender-lens.iife.js +105 -12
  40. package/dist/{runner-report-CW8X5RwK.d.ts → runner-report-Bvqoe89M.d.ts} +2 -2
  41. package/dist/{runner-report-B8Sc3FZO.d.cts → runner-report-CvpDg2zt.d.cts} +2 -2
  42. package/dist/setup.cjs +98 -9
  43. package/dist/setup.cjs.map +1 -1
  44. package/dist/setup.js +98 -9
  45. package/dist/setup.js.map +1 -1
  46. package/dist/{types-DW5-N2XH.d.cts → types-DwNN5cWe.d.cts} +28 -1
  47. package/dist/{types-DW5-N2XH.d.ts → types-DwNN5cWe.d.ts} +28 -1
  48. package/dist/vite.d.cts +1 -1
  49. package/dist/vite.d.ts +1 -1
  50. package/dist/vitest-setup.cjs +20 -3
  51. package/dist/vitest-setup.cjs.map +1 -1
  52. package/dist/vitest-setup.d.cts +4 -4
  53. package/dist/vitest-setup.d.ts +4 -4
  54. package/dist/vitest-setup.js +20 -3
  55. package/dist/vitest-setup.js.map +1 -1
  56. package/dist/vitest.cjs +20 -3
  57. package/dist/vitest.cjs.map +1 -1
  58. package/dist/vitest.d.cts +5 -5
  59. package/dist/vitest.d.ts +5 -5
  60. package/dist/vitest.js +20 -3
  61. package/dist/vitest.js.map +1 -1
  62. package/package.json +3 -2
  63. package/panel/panel.css +48 -0
  64. package/panel/panel.js +348 -20
package/dist/setup.js CHANGED
@@ -381,16 +381,27 @@ function printReport(report, options) {
381
381
  }
382
382
 
383
383
  // src/state.ts
384
+ var MAX_SEEN_COMPONENTS = 500;
384
385
  var KEY = /* @__PURE__ */ Symbol.for("rerender-lens.state");
385
386
  function getState() {
386
387
  const g = globalThis;
387
388
  let s = g[KEY];
388
389
  if (!s) {
389
- 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 };
390
+ 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, seen: /* @__PURE__ */ new Set(), seenTracked: /* @__PURE__ */ new Set(), seenOverflow: false };
390
391
  g[KEY] = s;
391
392
  }
392
393
  return s;
393
394
  }
395
+ function noteRendered(s, name, tracked) {
396
+ if (!s.seen.has(name)) {
397
+ if (s.seen.size >= MAX_SEEN_COMPONENTS) {
398
+ s.seenOverflow = true;
399
+ return;
400
+ }
401
+ s.seen.add(name);
402
+ }
403
+ if (tracked) s.seenTracked.add(name);
404
+ }
394
405
  function warnOnce(key, message) {
395
406
  const s = getState();
396
407
  if (s.warnedOnce.has(key)) return;
@@ -1117,8 +1128,11 @@ function onCommit(root, commitPriority) {
1117
1128
  const alt = fiber.alternate;
1118
1129
  if (alt && isComponentTag(fiber.tag) && didRender(fiber)) {
1119
1130
  if (isHotSwapped(fiber, alt)) hot = true;
1120
- renderedNames.add(fiberName(fiber));
1121
- if (shouldTrack(fiberType(fiber), o)) rendered.push(fiber);
1131
+ const name = fiberName(fiber);
1132
+ renderedNames.add(name);
1133
+ const tracked = shouldTrack(fiberType(fiber), o);
1134
+ noteRendered(s, name, tracked);
1135
+ if (tracked) rendered.push(fiber);
1122
1136
  }
1123
1137
  if (fiber.tag === SuspenseComponent && alt && alt.memoizedState !== null && fiber.memoizedState === null) (resolvedBoundaries ?? (resolvedBoundaries = [])).push(fiber);
1124
1138
  if (!alt || fiber.child !== alt.child) {
@@ -1243,10 +1257,53 @@ function shouldTrack(type, o) {
1243
1257
  if (o.trackAllMemoized) return isMemo(type) || isPureClass(type);
1244
1258
  return false;
1245
1259
  }
1260
+ var ANONYMOUS = "Anonymous";
1261
+ function explainTracking(type, o) {
1262
+ if (typeof type === "string")
1263
+ return { tracked: false, name: type, memoized: false, reason: `<${type}> is a DOM element, not a React component; only components are tracked.` };
1264
+ if (!isComponentLike(type))
1265
+ return { tracked: false, name: ANONYMOUS, memoized: false, reason: "This is not a React component type, so it is never tracked." };
1266
+ const name = getDisplayName(type);
1267
+ const memo = isMemo(type);
1268
+ const memoized = memo || isPureClass(type);
1269
+ if (o.exclude?.some((m) => matches(m, name)))
1270
+ return { tracked: false, name, memoized, reason: `<${name}> matches "exclude", which wins over every other rule.`, fix: `Remove "${name}" from the exclude list.` };
1271
+ if (hasMarker(type)) return { tracked: true, name, memoized, reason: `<${name}> is marked with track() (the ${MARKER} static).` };
1272
+ if (o.include?.some((m) => matches(m, name))) return { tracked: true, name, memoized, reason: `<${name}> matches "include".` };
1273
+ if (o.trackAllComponents) return { tracked: true, name, memoized, reason: '"Track every component" is on, so every component is tracked.' };
1274
+ if (o.trackAllMemoized && memoized)
1275
+ return { tracked: true, name, memoized, reason: `<${name}> is ${memo ? "a React.memo component" : "a PureComponent"}, and "Track every React.memo and PureComponent" is on.` };
1276
+ if (name === ANONYMOUS)
1277
+ return {
1278
+ tracked: false,
1279
+ name,
1280
+ memoized,
1281
+ reason: 'This component has no display name, so "include" and "exclude" cannot address it.',
1282
+ fix: "Give the function a name, set displayName, or mark it with track(fn, 'Name')."
1283
+ };
1284
+ if (o.trackAllMemoized)
1285
+ return {
1286
+ tracked: false,
1287
+ name,
1288
+ memoized,
1289
+ reason: `"Track every React.memo and PureComponent" only covers React.memo components and PureComponent classes, and <${name}> is neither.`,
1290
+ fix: `Add "${name}" to include, wrap it in React.memo, or turn on "Track every component".`
1291
+ };
1292
+ return {
1293
+ tracked: false,
1294
+ name,
1295
+ memoized,
1296
+ reason: `Nothing selects <${name}>: it is not marked with track(), no "include" pattern matches it, and neither "track every" option is on.`,
1297
+ fix: `Add "${name}" to include, or turn on "Track every React.memo and PureComponent" / "Track every component".`
1298
+ };
1299
+ }
1246
1300
  function init(options = {}) {
1247
1301
  const s = getState();
1248
1302
  s.options = { ...options };
1249
1303
  s.printed.clear();
1304
+ s.seen.clear();
1305
+ s.seenTracked.clear();
1306
+ s.seenOverflow = false;
1250
1307
  if (!s.enabled) {
1251
1308
  s.detach = attach();
1252
1309
  s.enabled = true;
@@ -1372,7 +1429,7 @@ function flash(target, duration = 500) {
1372
1429
  }
1373
1430
 
1374
1431
  // src/version.ts
1375
- var VERSION = "0.5.0" ;
1432
+ var VERSION = "0.7.0" ;
1376
1433
 
1377
1434
  // src/devtools.ts
1378
1435
  var DEVTOOLS_MARKER = "__rerenderLens";
@@ -1498,6 +1555,18 @@ function createDevtoolsNotifier(options = {}) {
1498
1555
  target.postMessage(msg, "*");
1499
1556
  };
1500
1557
  const source = options.source ?? "page";
1558
+ const trackingSummary = () => {
1559
+ const s = getState();
1560
+ const o = s.options;
1561
+ return {
1562
+ mode: o.trackAllComponents ? "all" : o.trackAllMemoized ? "memoized" : "marked",
1563
+ include: (o.include ?? []).map(matcherToString).filter((x) => x !== null),
1564
+ exclude: (o.exclude ?? []).map(matcherToString).filter((x) => x !== null),
1565
+ renderedCount: s.seen.size,
1566
+ trackedCount: s.seenTracked.size,
1567
+ overflow: s.seenOverflow
1568
+ };
1569
+ };
1501
1570
  const info = () => ({
1502
1571
  count: buffer.length,
1503
1572
  library: VERSION,
@@ -1511,7 +1580,8 @@ function createDevtoolsNotifier(options = {}) {
1511
1580
  scheduled: getState().scheduled,
1512
1581
  commits: getState().commits,
1513
1582
  overhead: { totalMs: getState().overheadMs, maxCommitMs: getState().maxCommitMs },
1514
- truncated: getState().truncated
1583
+ truncated: getState().truncated,
1584
+ tracking: trackingSummary()
1515
1585
  });
1516
1586
  const bridge = {
1517
1587
  replay: () => {
@@ -1557,6 +1627,10 @@ function createDevtoolsNotifier(options = {}) {
1557
1627
  flashAvoidable: (on) => {
1558
1628
  flashOn = !!on;
1559
1629
  },
1630
+ explain: (target2) => {
1631
+ const comp = typeof target2 === "number" ? fiberById(target2) : nearestComponent(fiberForNode(target2));
1632
+ return comp ? explainTracking(fiberType(comp), getState().options) : null;
1633
+ },
1560
1634
  inspect: (node) => {
1561
1635
  const comp = nearestComponent(fiberForNode(node));
1562
1636
  if (!comp) return null;
@@ -1571,6 +1645,7 @@ function createDevtoolsNotifier(options = {}) {
1571
1645
  component: fiberName(comp),
1572
1646
  instanceId: id,
1573
1647
  tracked: shouldTrack(fiberType(comp), getState().options),
1648
+ tracking: explainTracking(fiberType(comp), getState().options),
1574
1649
  path,
1575
1650
  reports: id === null ? [] : buffer.filter((e) => e.instanceId === id).slice(-20).map((e) => e.payload)
1576
1651
  };
@@ -1605,6 +1680,9 @@ function createDevtoolsNotifier(options = {}) {
1605
1680
  bridge.flashAvoidable(!!data.arg);
1606
1681
  reply.result = true;
1607
1682
  break;
1683
+ case "explain":
1684
+ reply.result = bridge.explain(data.arg);
1685
+ break;
1608
1686
  default:
1609
1687
  reply.error = `unknown command ${String(data.cmd)}`;
1610
1688
  }
@@ -1613,6 +1691,7 @@ function createDevtoolsNotifier(options = {}) {
1613
1691
  }
1614
1692
  return reply;
1615
1693
  };
1694
+ const isRelayGreeting = (data) => !!data && typeof data === "object" && data.__rerenderLensRelay === true && typeof data.app === "string";
1616
1695
  const isCommand = (data) => !!data && typeof data === "object" && data.__rerenderLensCmd === true && typeof data.id === "string";
1617
1696
  const envelope = (type, payload) => ({ [DEVTOOLS_MARKER]: true, version: PROTOCOL_VERSION, type, payload });
1618
1697
  const sinks = [];
@@ -1635,14 +1714,20 @@ function createDevtoolsNotifier(options = {}) {
1635
1714
  const relayUrl = options.relay ?? (typeof window !== "undefined" ? window.__RERENDER_LENS_RELAY__ : void 0);
1636
1715
  const ES = options.eventSource ?? (typeof EventSource === "function" ? EventSource : null);
1637
1716
  if (relayUrl && ES && typeof fetch === "function") {
1638
- const base = relayUrl.replace(/\/$/, "");
1717
+ const [rawBase, relayQuery = ""] = relayUrl.split("?");
1718
+ const base = (rawBase ?? "").replace(/\/$/, "");
1719
+ const relayToken = new URLSearchParams(relayQuery).get("token");
1720
+ const auth = relayToken ? `token=${encodeURIComponent(relayToken)}` : "";
1639
1721
  let queue = [];
1640
1722
  let scheduled = false;
1723
+ let appId = null;
1641
1724
  const flush = () => {
1642
1725
  scheduled = false;
1643
1726
  const batch = queue;
1644
1727
  queue = [];
1645
- fetch(`${base}/message`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(batch), keepalive: true }).catch(() => {
1728
+ const params = [appId ? `app=${encodeURIComponent(appId)}` : "", auth].filter(Boolean).join("&");
1729
+ const to = `${base}/message${params ? `?${params}` : ""}`;
1730
+ fetch(to, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(batch), keepalive: true }).catch(() => {
1646
1731
  });
1647
1732
  };
1648
1733
  const relaySend = (message) => {
@@ -1653,7 +1738,8 @@ function createDevtoolsNotifier(options = {}) {
1653
1738
  }
1654
1739
  };
1655
1740
  try {
1656
- const stream = new ES(`${base}/events?role=app`);
1741
+ const label = typeof location !== "undefined" ? `${location.host}${location.pathname}`.slice(0, 120) : "";
1742
+ const stream = new ES(`${base}/events?role=app${label ? `&label=${encodeURIComponent(label)}` : ""}${auth ? `&${auth}` : ""}`);
1657
1743
  stream.onmessage = (event) => {
1658
1744
  let parsed;
1659
1745
  try {
@@ -1661,7 +1747,10 @@ function createDevtoolsNotifier(options = {}) {
1661
1747
  } catch {
1662
1748
  return;
1663
1749
  }
1664
- for (const m of Array.isArray(parsed) ? parsed : [parsed]) if (isCommand(m)) relaySend(runCommand(m));
1750
+ for (const m of Array.isArray(parsed) ? parsed : [parsed]) {
1751
+ if (isRelayGreeting(m)) appId = m.app;
1752
+ else if (isCommand(m)) relaySend(runCommand(m));
1753
+ }
1665
1754
  };
1666
1755
  stream.onerror = () => {
1667
1756
  };