rerender-lens 0.1.0 → 0.3.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 (79) hide show
  1. package/CHANGELOG.md +164 -6
  2. package/README.md +298 -75
  3. package/dist/budget-COBu7jBU.d.cts +64 -0
  4. package/dist/budget-LkNjGtRc.d.ts +64 -0
  5. package/dist/cli.cjs +458 -0
  6. package/dist/cli.cjs.map +1 -0
  7. package/dist/cli.d.cts +4 -0
  8. package/dist/cli.d.ts +4 -0
  9. package/dist/cli.js +452 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/devtools-BHGUUo-p.d.ts +244 -0
  12. package/dist/devtools-DmhWiWcN.d.cts +244 -0
  13. package/dist/index.cjs +1686 -74
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +67 -153
  16. package/dist/index.d.ts +67 -153
  17. package/dist/index.js +1643 -13
  18. package/dist/index.js.map +1 -1
  19. package/dist/notifiers-BGQtWKfX.d.cts +90 -0
  20. package/dist/notifiers-BjjGSHpp.d.ts +90 -0
  21. package/dist/playwright.cjs +196 -0
  22. package/dist/playwright.cjs.map +1 -0
  23. package/dist/playwright.d.cts +35 -0
  24. package/dist/playwright.d.ts +35 -0
  25. package/dist/playwright.js +184 -0
  26. package/dist/playwright.js.map +1 -0
  27. package/dist/relay.cjs +166 -0
  28. package/dist/relay.cjs.map +1 -0
  29. package/dist/relay.d.cts +41 -0
  30. package/dist/relay.d.ts +41 -0
  31. package/dist/relay.js +161 -0
  32. package/dist/relay.js.map +1 -0
  33. package/dist/rerender-lens.iife.js +1717 -0
  34. package/dist/setup.cjs +1510 -0
  35. package/dist/setup.cjs.map +1 -0
  36. package/dist/setup.d.cts +2 -0
  37. package/dist/setup.d.ts +2 -0
  38. package/dist/setup.js +1508 -0
  39. package/dist/setup.js.map +1 -0
  40. package/dist/types-BzUEVkxJ.d.cts +177 -0
  41. package/dist/types-BzUEVkxJ.d.ts +177 -0
  42. package/dist/vite.cjs +113 -0
  43. package/dist/vite.cjs.map +1 -0
  44. package/dist/vite.d.cts +84 -0
  45. package/dist/vite.d.ts +84 -0
  46. package/dist/vite.js +103 -0
  47. package/dist/vite.js.map +1 -0
  48. package/dist/vitest-setup.cjs +1299 -0
  49. package/dist/vitest-setup.cjs.map +1 -0
  50. package/dist/vitest-setup.d.cts +8 -0
  51. package/dist/vitest-setup.d.ts +8 -0
  52. package/dist/vitest-setup.js +1297 -0
  53. package/dist/vitest-setup.js.map +1 -0
  54. package/dist/vitest.cjs +1360 -0
  55. package/dist/vitest.cjs.map +1 -0
  56. package/dist/vitest.d.cts +52 -0
  57. package/dist/vitest.d.ts +52 -0
  58. package/dist/vitest.js +1351 -0
  59. package/dist/vitest.js.map +1 -0
  60. package/package.json +83 -17
  61. package/panel/panel.css +418 -0
  62. package/panel/panel.html +12 -0
  63. package/panel/panel.js +3325 -0
  64. package/dist/chunk-3YHI7BCU.js +0 -544
  65. package/dist/chunk-3YHI7BCU.js.map +0 -1
  66. package/dist/chunk-DOYB4MKH.cjs +0 -562
  67. package/dist/chunk-DOYB4MKH.cjs.map +0 -1
  68. package/dist/jsx-dev-runtime.cjs +0 -32
  69. package/dist/jsx-dev-runtime.cjs.map +0 -1
  70. package/dist/jsx-dev-runtime.d.cts +0 -8
  71. package/dist/jsx-dev-runtime.d.ts +0 -8
  72. package/dist/jsx-dev-runtime.js +0 -9
  73. package/dist/jsx-dev-runtime.js.map +0 -1
  74. package/dist/jsx-runtime.cjs +0 -34
  75. package/dist/jsx-runtime.cjs.map +0 -1
  76. package/dist/jsx-runtime.d.cts +0 -9
  77. package/dist/jsx-runtime.d.ts +0 -9
  78. package/dist/jsx-runtime.js +0 -10
  79. package/dist/jsx-runtime.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,15 +1,173 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ - **`npx rerender-lens panel`.** A dependency-free relay (`rerender-lens/relay`) that serves the
6
+ panel and forwards messages between apps and panels over server-sent events and POST, so the
7
+ panel works for any app on any origin without the extension or Vite: Next.js, Webpack, a remote
8
+ dev box, a phone. `createDevtoolsNotifier({ relay })`, `window.__RERENDER_LENS_RELAY__`, or
9
+ `RERENDER_LENS_RELAY` / `NEXT_PUBLIC_RERENDER_LENS_RELAY` with `rerender-lens/setup`. Commands
10
+ (Settings, highlight, replay, clear) go back to the app; the panel re-attaches when an app
11
+ reloads.
12
+ - **`rerender-lens/vitest`.** `setupFiles: ['rerender-lens/vitest/setup']` collects every test
13
+ file's reports; the reporter prints the run's ranked fixes, enforces a budget file, and can
14
+ write a panel-compatible export. `setupRerenderLens(options, { afterAll })` for custom options
15
+ and `failFast`.
16
+ - **`rerender-lens/playwright`.** `installRerenderLens(page, options)` injects the library at
17
+ document start (the bundle now ships as `dist/rerender-lens.iife.js`); `pullReports`,
18
+ `clearReports`, `expectWithinBudget`.
19
+ - A second copy of the library (another bundle on the same page) no longer wraps the hook
20
+ again, which doubled every report.
21
+
22
+ ## 0.3.0
23
+
24
+ Highlights: the panel without the extension (Vite plugin `panel: true`), the `rerender-lens/vite`
25
+ plugin and `rerender-lens/setup` entry, sessions with before/after comparison, custom hook names,
26
+ updaters and effect-loop detection, full hook/context/state snapshots, CI budgets and a CLI, the
27
+ side panel and window modes, and a UI refresh. Details, newest first:
28
+
29
+ - **Panel without the extension.** `rerenderLens({ panel: true })` serves the panel from the Vite
30
+ dev server at `/__rerender-lens/`; the app publishes on a same-origin `BroadcastChannel`
31
+ (`createDevtoolsNotifier({ channel })`) and the panel sends commands back over it. The `panel/`
32
+ directory ships in the package. `pages` limits which HTML pages get the setup script.
33
+ - **Overhead readout.** `info().overhead` reports the time the library spent inspecting commits
34
+ (total and worst commit); the panel shows it in the status tooltip.
35
+ - **Panel polish.** Offenders and Fixes are windowed above 200 rows (sticky header, sortable);
36
+ an Offenders column chooser adds *Places* and *Last seen*; the search box takes `~text` to
37
+ search prop, hook, context and state values; per-component notes and a *Mute* toggle (muted
38
+ components leave the Fixes ranking and the summary strip, persisted per origin); roles and
39
+ ARIA states on the tree, lists, toast and settings dialog, Escape closes Settings.
40
+ - **CI tooling.** `rankFixes` / `formatFixes`, `summarizeReports` / `compareSummaries`, and
41
+ `checkBudget` / `toBudget` / `assertWithinBudget` in the package; the collector gains
42
+ `fixes()`, `summary()` and `assertWithinBudget()`, and `assertNoAvoidable()` now ends with the
43
+ ranked fixes. A `rerender-lens` CLI (`fixes`, `summary --out`, `compare`, `budget --init`)
44
+ works on panel exports and session files and exits non-zero on regressions or violations.
45
+ - **Source context.** The report shows the lines around where the element was created (DevTools
46
+ resources or a fetch of the module in side-panel mode).
47
+ - **Shareable links.** *Copy link* puts the report into a `panel.html?report=…` URL (deflated when
48
+ the browser can); anyone with the extension opens it without the page.
49
+ - **Custom hook names, opt-in** (`resolveHookNames`, also in Settings): hook changes and
50
+ snapshots read `useCounter › useCart › useState#0`. Like React DevTools, the library re-runs a
51
+ component type once with a stand-in dispatcher and reads the custom hooks off the call stack;
52
+ results are cached per type and a failing replay yields no names.
53
+ - **Updaters.** Every report names the components that scheduled the commit (`updaters`, from
54
+ React's updater tracking), so the Commits view shows "set by <X>" even when X is untracked.
55
+ - **Effect loops.** A commit scheduled right after the previous one by a component that rendered
56
+ in it is flagged `effect-after-commit` with the offending commit id; the report explains the
57
+ effect → setState pattern. Suspense boundaries resolving are labelled `suspense-resolved`.
58
+ - **Store advice.** A `useSyncExternalStore` snapshot with equal contents now gets Redux
59
+ (`shallowEqual` / `createSelector`) or Zustand (`useShallow`) advice when the hook chain
60
+ identifies the store hook.
61
+ - **Instances.** Reports carry the element `key`; the tree can group by instance
62
+ (`<Row key="a">`, `<Row #12>`) via the Instances toggle.
63
+ - **`rerender-lens/vite`**: a Vite plugin that starts the library before React in dev, with the
64
+ DevTools notifier, from one line in `vite.config.ts`. **`rerender-lens/setup`**: a side-effect
65
+ entry for Next.js `instrumentation-client.ts`, Webpack entry arrays and the like.
66
+ - CI lints the Firefox package with `web-ext lint`; `extension/store/QA.md` is the manual
67
+ checklist for what no test reaches. A docs site builds from the READMEs (`npm run build:docs`)
68
+ and deploys to GitHub Pages; `npm run docs:media` captures its screenshots and GIF.
69
+ - **Full state on every report.** `hookState` (every `useState` / `useReducer` /
70
+ `useSyncExternalStore` value), `contexts` (every context the component reads) and, for class
71
+ components, `state`. The panel's report shows Hooks, State and Contexts in full, with the
72
+ changed entries as prev → next; the console prints them too. `includeState: false` turns it off
73
+ (also in the panel's Settings).
74
+ - **Sessions.** Record, use the app, Stop; apply a fix; record again. The Sessions view compares
75
+ two sessions: avoidable re-renders per component with deltas, totals, wasted time, and which
76
+ suggested fixes went away. Summaries persist per origin; exports include them.
77
+ - **Provider attribution.** A `useContext` change now names the component that renders the
78
+ Provider and, for object values, which keys changed. When only some keys changed the reason
79
+ and the Fixes view say so and suggest splitting the context or selecting slices.
80
+ - **Children diffs.** Elements are serialized with their props, so a changed `children` prop
81
+ shows the differing leaf (`children[0].props.label`); re-created children get their own advice
82
+ and fix snippet (memoize or hoist them).
83
+ - **Commit priority.** Every report carries the priority React gave the commit (discrete input,
84
+ continuous input, transition / async, low, idle); the Commits list and report show it. The
85
+ bridge `info()` also counts scheduled roots vs commits.
86
+ - Not done: custom hook names for hook changes. React DevTools gets them by re-running the
87
+ component with a fake dispatcher; doing that from a commit hook is too invasive.
88
+ - The panel outside DevTools: **Open side panel** shows it in Chrome's side panel next to the
89
+ page, **Open in window** in its own window (toolbar popup, or the new buttons in the DevTools
90
+ panel). Both use the background relay plus `chrome.scripting` instead of DevTools APIs, follow
91
+ the active tab unless pinned, and stack the layout when narrower than 720px. Firefox gets the
92
+ same page as a sidebar.
93
+ - UI refresh: new palette and spacing in light and dark, icon toolbar with labels that collapse
94
+ in narrow layouts, segmented view switcher, a summary strip (renders, avoidable, wasted time,
95
+ top offender, best fix, all clickable), clearer selection and banners.
96
+ - `selfDuration` is now the component's own render time; the previous value (the whole subtree)
97
+ moved to `treeDuration`. Children that bailed out contribute nothing, as in the Profiler.
98
+ - `memoized` on every report (`React.memo` / `PureComponent`). Reports of unmemoized components
99
+ with avoidable prop changes explain that `React.memo` is needed as well; the panel's Fixes view
100
+ lists it first.
101
+ - Bridge `info()` reports `source` (`page` or `extension`) and `injected`. The panel warns when the
102
+ page runs its own copy next to the injected one.
103
+ - Extension: per-origin "Let React DevTools create the hook" toggle (popup and Settings). With it
104
+ on, the injector waits one task so React DevTools' own script can install the global hook first.
105
+ - Elements sidebar: an element without a React component above it now says so instead of
106
+ "not running".
107
+ - jsdom tests for `background.js`, `popup.js` and `sidebar.js` (shared fake `chrome`).
108
+ - Panel source moved to TypeScript (`extension/src/panel.ts`); `npm run build` writes the
109
+ committed `extension/panel.js`, and CI fails when it is out of date.
110
+ - Tree and live stream are virtualized (only the rows in view exist in the DOM); the stream now
111
+ keeps every buffered report instead of the last 300. `panel.html?demo&flood=5000` loads
112
+ synthetic data for scale testing.
113
+ - Keyboard: `/` focuses search, `f` opens the Fix tab, `Esc` clears the page highlight. The
114
+ details tab is remembered per origin.
115
+ - `different` object props show the differing leaves (`filters.page: 1 → 2`) without expanding
116
+ the whole value; the kind label names the first differing path.
117
+ - Root-cause page: click a root cause in a commit to see every commit it started, the components
118
+ it re-rendered avoidably, and the fixes for them.
119
+
120
+ ## 0.2.0
121
+
122
+ Library
123
+
124
+ - Every report carries `commitId` (shared by the reports of one React commit) and, in dev
125
+ builds, `source` (`_debugSource` on React <= 18, parsed from `_debugStack` on React 19).
126
+ - DevTools bridge protocol 2: `hello` carries library version, protocol, React renderers and
127
+ options; `pull(since)` for polling panels; `info()`, `configure()`, `getOptions()`,
128
+ `highlight(instanceId)`, `flashAvoidable(on)` (in-page overlay), `inspect(node)`.
129
+ - `getRenderers()` / `isProductionReact()`: what react-dom registered on the DevTools hook.
130
+ `attach` wraps `hook.inject` so hooks created by react-refresh (Vite) are covered too.
131
+ - `serializeOptions` / `deserializeOptions`, `VERSION`.
132
+ - An IIFE build (`extension/vendor/rerender-lens.js`, global `RerenderLens`) for injection.
133
+
134
+ Extension
135
+
136
+ - Store-ready packaging: `npm run build:ext` writes Chrome, Edge and Firefox zips; the manifest
137
+ version follows `package.json`; CI uploads the zips; the release workflow publishes to the
138
+ Chrome Web Store when secrets are configured. Listing text and privacy policy in
139
+ `extension/store/`.
140
+ - Zero-config mode: enable an origin from the toolbar popup or the panel's Settings and tick
141
+ *Inject the library*; the extension loads rerender-lens into the page before React.
142
+ Optional host permissions for non-local hosts; local development hosts are on by default.
143
+ - Three-state status (no page / no library / connected with library and React versions),
144
+ protocol-mismatch and production-build warnings.
145
+ - Polling fallback through `inspectedWindow.eval` when no content script is present.
146
+ - New views: Offenders (sortable table), Commits (root causes, render cascade, contexts),
147
+ Fixes (ranked by avoidable re-renders removed, with snippets). Per-report Fix tab.
148
+ - Hover-to-highlight and flash of avoidable renders in the page; "open source" links into the
149
+ Sources panel; Copy as Markdown; JSON export/import.
150
+ - Settings drawer driving `configure()` live, persisted per origin; panel state (filters,
151
+ collapsed nodes, split width) persisted per origin.
152
+ - Elements-panel sidebar for `$0`; toolbar badge with the avoidable count per tab.
153
+ - Reports are batched per animation frame; malformed payloads are ignored.
154
+ - Playwright end-to-end suite (`npm run e2e`) running Chromium with the extension against the
155
+ example app (relay, badge, injection, panel).
156
+
3
157
  ## 0.1.0
4
158
 
5
159
  Initial release.
6
160
 
7
- - `init(React, options)` patches `createElement` and the state hooks; tracked components report every update.
161
+ - `init(options)` observes React commits through the DevTools global hook and reports every
162
+ update of a tracked component. No patching, no wrappers: Fast Refresh, memo comparators,
163
+ forwardRef, classes and the automatic JSX runtime all work unchanged.
8
164
  - Change classification: `deep-equal`, `function`, `element`, `different`, `added`, `removed`.
9
165
  - Trigger classification: `props`, `parent`, `state`, `hooks`, `mixed`; `avoidable` flag.
10
- - Supports function components, `React.memo` (custom comparators preserved), `forwardRef`, `memo(forwardRef())`, class and `PureComponent` components.
11
- - `useWhyRerender(name, values)` hook for tracking one component without patching.
166
+ - `parent` (nearest ancestor that rendered, and why), `owner`, `path`, `instanceId`,
167
+ `renderCount`, `selfDuration` on every report.
168
+ - Hook state (`useState`, `useReducer`, `useSyncExternalStore`) and `useContext` diffs.
169
+ - `useWhyRerender(name, values)` hook for tracking one component without `init`.
12
170
  - `createCollector()` with `assertNoAvoidable()` for tests; `combineNotifiers()`.
13
- - `createDevtoolsNotifier()` posting structured-clone-safe reports on `window` for a DevTools panel.
14
- - `rerender-lens/jsx-runtime` and `rerender-lens/jsx-dev-runtime` for the automatic JSX runtime.
15
- - StrictMode double-render is not reported.
171
+ - `createDevtoolsNotifier()` posting structured-clone-safe reports on `window`.
172
+ - `ignoreHotReload` and `maxReportsPerComponent` options.
173
+ - Example Vite app with three deliberate re-render bugs.