rerender-lens 0.2.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 (63) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +146 -5
  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 +652 -23
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +40 -336
  16. package/dist/index.d.ts +40 -336
  17. package/dist/index.js +636 -24
  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 +84 -4
  61. package/panel/panel.css +418 -0
  62. package/panel/panel.html +12 -0
  63. package/panel/panel.js +3325 -0
@@ -0,0 +1,177 @@
1
+ /** Why a single prop/state/hook value differs between two renders. */
2
+ type ChangeKind =
3
+ /** New reference, but deep-equal to the previous value. Avoidable. */
4
+ 'deep-equal'
5
+ /** A new function with the same name/body shape. Almost always avoidable. */
6
+ | 'function'
7
+ /** A new React element that renders the same type with deep-equal props. Avoidable. */
8
+ | 'element'
9
+ /** The value genuinely changed. */
10
+ | 'different'
11
+ /** Key was added. */
12
+ | 'added'
13
+ /** Key was removed. */
14
+ | 'removed';
15
+ interface Change {
16
+ /** Dot path from the root, e.g. `style.color` or `items[2]`. */
17
+ path: string;
18
+ kind: ChangeKind;
19
+ prev: unknown;
20
+ next: unknown;
21
+ }
22
+ /** What caused a re-render. */
23
+ type RenderTrigger =
24
+ /** At least one prop genuinely changed. */
25
+ 'props'
26
+ /** Props are equal by value, but the parent re-rendered. Wrap in React.memo. */
27
+ | 'parent'
28
+ /** Own state (useState/useReducer/this.setState) changed. */
29
+ | 'state'
30
+ /** A non-state hook value (useContext, useSyncExternalStore) changed. */
31
+ | 'hooks'
32
+ /** More than one of the above. */
33
+ | 'mixed';
34
+ interface HookChange extends Change {
35
+ /** `useState`, `useReducer`, `useContext`, `useSyncExternalStore`, or `state` when the exact hook is unknown. */
36
+ hook: string;
37
+ /** Position of the hook in call order (0-based). */
38
+ index: number;
39
+ /** `useContext` only: the component that renders the nearest matching Provider, and its ancestry. */
40
+ provider?: {
41
+ component: string | null;
42
+ path: string[];
43
+ };
44
+ /** `useContext` only, object values: top-level keys whose value changed (shallow), and how many keys the value has. */
45
+ changedKeys?: string[];
46
+ totalKeys?: number;
47
+ /** Custom hooks between the component and this primitive, innermost first (`resolveHookNames` option). */
48
+ custom?: string[];
49
+ }
50
+ /** Current value of one state-bearing hook (`useState`, `useReducer`, `useSyncExternalStore`) after the render. */
51
+ interface HookSnapshot {
52
+ /** Same label as `HookChange.path`, e.g. `useState#0`. */
53
+ path: string;
54
+ hook: string;
55
+ index: number;
56
+ value: unknown;
57
+ /** Custom hooks between the component and this primitive, innermost first (`resolveHookNames` option). */
58
+ custom?: string[];
59
+ }
60
+ /** What made React commit, beyond the usual state/props story. */
61
+ type CommitCause =
62
+ /** State was set right after the previous commit by a component that rendered in it: an effect → setState loop. */
63
+ 'effect-after-commit'
64
+ /** A Suspense boundary switched from its fallback to content. */
65
+ | 'suspense-resolved';
66
+ /** Scheduler priority of the commit, as React reports it to the DevTools hook (transitions run at `normal`). */
67
+ type CommitPriority = 'immediate' | 'user-blocking' | 'normal' | 'low' | 'idle';
68
+ interface ParentInfo {
69
+ /** Display name of the nearest ancestor component that also rendered in this commit. */
70
+ name: string;
71
+ /** Why that ancestor rendered. */
72
+ trigger: RenderTrigger;
73
+ }
74
+ /** Where a component's element was created (React <= 18: `_debugSource`; React 19: parsed from `_debugStack`). */
75
+ interface SourceLocation {
76
+ fileName: string;
77
+ lineNumber?: number;
78
+ columnNumber?: number;
79
+ }
80
+ interface RenderReport {
81
+ /** Display name of the tracked component. */
82
+ component: string;
83
+ /** Stable id of this component instance (fiber) across its lifetime. */
84
+ instanceId: number;
85
+ /** Monotonic id of the React commit that produced this report. Reports from one commit share it. 0 for `useWhyRerender`. */
86
+ commitId: number;
87
+ /** Priority React assigned to the commit: `immediate` for discrete input (clicks, keys), `user-blocking` for continuous input, `normal` for transitions and async updates. */
88
+ commitPriority?: CommitPriority;
89
+ /** Components that scheduled the update (`setState`, dispatch) for this commit, from React's updater tracking (dev builds). */
90
+ updaters?: string[];
91
+ commitCause?: CommitCause;
92
+ /** For `effect-after-commit`: the commit whose effects set the state. */
93
+ afterCommit?: number;
94
+ /** The element's `key`, when it has one. */
95
+ key?: string | null;
96
+ /** Monotonic per-instance update count (1 = first update; mount is never reported). */
97
+ renderCount: number;
98
+ trigger: RenderTrigger;
99
+ /** True when the re-render produced no genuine change in props, state, or hooks. */
100
+ avoidable: boolean;
101
+ props: {
102
+ prev: Record<string, unknown>;
103
+ next: Record<string, unknown>;
104
+ };
105
+ propChanges: Change[];
106
+ /** Class components only. */
107
+ stateChanges: Change[];
108
+ /** Function components: state hooks and contexts that changed. */
109
+ hookChanges: HookChange[];
110
+ /** Function components: every state hook with its current value (changed or not). Omitted when `includeState` is off. */
111
+ hookState?: HookSnapshot[];
112
+ /** Every context the component reads, with its current value. Omitted when `includeState` is off. */
113
+ contexts?: {
114
+ name: string;
115
+ value: unknown;
116
+ }[];
117
+ /** Class components: `this.state` after the render. Omitted when `includeState` is off. */
118
+ state?: Record<string, unknown>;
119
+ /** Nearest ancestor that rendered in the same commit, or null when the update started here. */
120
+ parent: ParentInfo | null;
121
+ /** Component that created this element (dev builds only). */
122
+ owner: string | null;
123
+ /** Component ancestry from the root down to this component, display names only. */
124
+ path: string[];
125
+ /** True for `React.memo` components and `PureComponent` classes: props alone decide whether they re-render. */
126
+ memoized: boolean;
127
+ /** Time spent in this component's own render (children excluded), in ms, when React exposes it (dev/profiling builds). */
128
+ selfDuration?: number;
129
+ /** Time spent rendering this component and everything below it that rendered in the same commit, in ms. */
130
+ treeDuration?: number;
131
+ /** Source location of the element that rendered this component, when React exposes it (dev builds). */
132
+ source?: SourceLocation;
133
+ /** Human-readable explanations and suggested fixes. */
134
+ reasons: string[];
135
+ /** `performance.now()` (or `Date.now()`) when the report was produced. */
136
+ time: number;
137
+ }
138
+ type Notifier = (report: RenderReport) => void;
139
+ type ComponentMatcher = string | RegExp | ((displayName: string) => boolean);
140
+ interface Options {
141
+ /** Track every `React.memo`-wrapped component and every `PureComponent`. Default false. */
142
+ trackAllMemoized?: boolean;
143
+ /** Track every component, memoized or not. Noisy. Default false. */
144
+ trackAllComponents?: boolean;
145
+ /** Components to track by display name (string = exact match). */
146
+ include?: ComponentMatcher[];
147
+ /** Components never to track, even when marked. */
148
+ exclude?: ComponentMatcher[];
149
+ /** Diff hook state and contexts of function components. Default true. */
150
+ trackHooks?: boolean;
151
+ /** Put the current values of every state hook, context and class state on each report (`hookState`, `contexts`, `state`). Default true. */
152
+ includeState?: boolean;
153
+ /**
154
+ * Resolve custom hook names (`useCart › useState#0`) by re-running each reported component type once
155
+ * with a stand-in dispatcher, as React DevTools does. Off by default: the extra render is visible to
156
+ * anything the component does during render (logging, counters). Dev builds only.
157
+ */
158
+ resolveHookNames?: boolean;
159
+ /** Report re-renders caused by genuine changes too, not only avoidable ones. Default false. */
160
+ logAll?: boolean;
161
+ /** Do not print to the console. Reports still reach `notifier`. Default false. */
162
+ silent?: boolean;
163
+ /** Receive every report. Combine several with `combineNotifiers`. */
164
+ notifier?: Notifier;
165
+ /** Use `console.groupCollapsed` instead of `console.group`. Default true. */
166
+ collapse?: boolean;
167
+ /** Console-like sink used for printing. Default `console`. */
168
+ console?: Pick<Console, 'log' | 'group' | 'groupCollapsed' | 'groupEnd' | 'warn'>;
169
+ /** Skip commits caused by Fast Refresh / hot module replacement. Default true. */
170
+ ignoreHotReload?: boolean;
171
+ /** Stop printing a component after this many reports (0 = unlimited). The notifier still receives them. Default 0. */
172
+ maxReportsPerComponent?: number;
173
+ }
174
+ /** Marker static: `MyComponent.rerenderLens = true` opts a component in. */
175
+ declare const MARKER: "rerenderLens";
176
+
177
+ export { type ChangeKind as C, type HookChange as H, MARKER as M, type Notifier as N, type Options as O, type ParentInfo as P, type RenderReport as R, type SourceLocation as S, type Change as a, type HookSnapshot as b, type CommitPriority as c, type CommitCause as d, type ComponentMatcher as e, type RenderTrigger as f };
package/dist/vite.cjs ADDED
@@ -0,0 +1,113 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+ var url = require('url');
6
+
7
+ // node_modules/tsup/assets/cjs_shims.js
8
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
9
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
10
+ var VIRTUAL_ID = "virtual:rerender-lens";
11
+ var RESOLVED_ID = "\0" + VIRTUAL_ID;
12
+ var VIRTUAL_URL = "/@id/__x00__" + VIRTUAL_ID;
13
+ var DEFAULT_PANEL_PATH = "/__rerender-lens/";
14
+ var serializeMatcher = (m) => m instanceof RegExp ? `new RegExp(${JSON.stringify(m.source)}, ${JSON.stringify(m.flags)})` : JSON.stringify(m);
15
+ function renderSetupModule(options = {}) {
16
+ const { devtools = true, applyInBuild: _applyInBuild, panel, channel, pages: _pages, include, exclude, ...rest } = options;
17
+ const entries = [];
18
+ for (const [k, v] of Object.entries(rest)) if (v !== void 0) entries.push(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);
19
+ if (include) entries.push(`include: [${include.map(serializeMatcher).join(", ")}]`);
20
+ if (exclude) entries.push(`exclude: [${exclude.map(serializeMatcher).join(", ")}]`);
21
+ const notifierOptions = panel ? `{ channel: ${JSON.stringify(channel || "rerender-lens")} }` : "";
22
+ if (devtools || panel) entries.push(`notifier: createDevtoolsNotifier(${notifierOptions})`);
23
+ return [
24
+ `import { init${devtools || panel ? ", createDevtoolsNotifier" : ""} } from 'rerender-lens';`,
25
+ `init({ ${entries.join(", ")} });`,
26
+ ""
27
+ ].join("\n");
28
+ }
29
+ function panelDir() {
30
+ const here = path.dirname(url.fileURLToPath(importMetaUrl));
31
+ for (const dir of [path.join(here, "..", "panel"), path.join(here, "..", "extension"), path.join(here, "..", "..", "extension")]) {
32
+ if (fs.existsSync(path.join(dir, "panel.html")) && fs.existsSync(path.join(dir, "panel.js"))) return dir;
33
+ }
34
+ return null;
35
+ }
36
+ var MIME = { ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8" };
37
+ function panelMiddleware(mount, channel, dir) {
38
+ const base = mount.endsWith("/") ? mount : mount + "/";
39
+ return (req, res, next) => {
40
+ const url = req.url || "/";
41
+ if (!url.startsWith(base.slice(0, -1))) return next();
42
+ const [pathOnly, query = ""] = url.split("?", 2);
43
+ if (pathOnly === base.slice(0, -1) || pathOnly === base) {
44
+ res.statusCode = 302;
45
+ res.setHeader("Location", `${base}panel.html?channel=${encodeURIComponent(channel)}${query ? "&" + query : ""}`);
46
+ res.end();
47
+ return;
48
+ }
49
+ const file = pathOnly.slice(base.length);
50
+ if (!/^panel\.(html|js|css)$/.test(file)) return next();
51
+ if (!dir) {
52
+ res.statusCode = 500;
53
+ res.setHeader("Content-Type", "text/plain");
54
+ res.end("rerender-lens: panel files not found (is the package built?)");
55
+ return;
56
+ }
57
+ res.statusCode = 200;
58
+ res.setHeader("Content-Type", MIME[file.slice(file.lastIndexOf("."))] || "application/octet-stream");
59
+ res.setHeader("Cache-Control", "no-store");
60
+ res.end(fs.readFileSync(path.join(dir, file)));
61
+ };
62
+ }
63
+ function rerenderLens(options = {}) {
64
+ const defaults = { trackAllMemoized: true };
65
+ const merged = { ...defaults, ...options };
66
+ const pages = merged.pages;
67
+ const wants = (path) => {
68
+ if (!pages) return true;
69
+ const p = path || "/";
70
+ if (typeof pages === "function") return pages(p);
71
+ return pages.some((x) => x === p || x === "/" && p === "/index.html" || x === "/index.html" && p === "/");
72
+ };
73
+ const mount = typeof merged.panel === "string" ? merged.panel : DEFAULT_PANEL_PATH;
74
+ const plugin = {
75
+ name: "rerender-lens",
76
+ enforce: "pre",
77
+ resolveId(id) {
78
+ return id === VIRTUAL_ID ? RESOLVED_ID : void 0;
79
+ },
80
+ load(id) {
81
+ return id === RESOLVED_ID ? renderSetupModule(merged) : void 0;
82
+ },
83
+ transformIndexHtml: {
84
+ order: "pre",
85
+ handler(_html, ctx) {
86
+ if (!wants(ctx?.path)) return [];
87
+ return [{ tag: "script", attrs: { type: "module", src: VIRTUAL_URL }, injectTo: "head-prepend" }];
88
+ }
89
+ }
90
+ };
91
+ if (!merged.applyInBuild) plugin.apply = "serve";
92
+ if (merged.panel) {
93
+ plugin.configureServer = (server) => {
94
+ server.middlewares.use(panelMiddleware(mount, merged.channel || "rerender-lens", panelDir()));
95
+ const announce = () => {
96
+ const local = server.resolvedUrls?.local?.[0];
97
+ server.config?.logger?.info(` \u279C rerender-lens panel: ${local ? local.replace(/\/$/, "") : ""}${mount}`);
98
+ };
99
+ if (server.httpServer) server.httpServer.once("listening", () => setTimeout(announce, 0));
100
+ };
101
+ }
102
+ return plugin;
103
+ }
104
+
105
+ exports.DEFAULT_PANEL_PATH = DEFAULT_PANEL_PATH;
106
+ exports.VIRTUAL_ID = VIRTUAL_ID;
107
+ exports.VIRTUAL_URL = VIRTUAL_URL;
108
+ exports.panelDir = panelDir;
109
+ exports.panelMiddleware = panelMiddleware;
110
+ exports.renderSetupModule = renderSetupModule;
111
+ exports.rerenderLens = rerenderLens;
112
+ //# sourceMappingURL=vite.cjs.map
113
+ //# sourceMappingURL=vite.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../node_modules/tsup/assets/cjs_shims.js","../src/vite.ts"],"names":["dirname","fileURLToPath","join","existsSync","readFileSync"],"mappings":"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","file":"vite.cjs","sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * Vite plugin: `rerenderLens()` in `vite.config.ts` starts rerender-lens in dev before React loads,\n * with the DevTools notifier, so the extension panel works without touching the app.\n *\n * With `panel: true` it also serves the panel itself at `/__rerender-lens/` (no extension needed): the\n * app publishes on a same-origin `BroadcastChannel`, the panel tab listens and sends commands back.\n *\n * The plugin serves a virtual module that imports `rerender-lens` and calls `init`, and injects a\n * `<script type=\"module\">` for it at the top of `<head>`. Module scripts execute in document order,\n * so it runs before the app entry (and after Vite's Fast Refresh preamble, which is fine: `init`\n * wraps whatever DevTools hook exists). Nothing happens in `vite build`.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Options } from './types';\n\n/** `Options` minus the parts that cannot be serialized into a module (functions). Matchers may be strings or RegExps. */\nexport type VitePluginOptions = Omit<Options, 'notifier' | 'console' | 'include' | 'exclude'> & {\n include?: (string | RegExp)[];\n exclude?: (string | RegExp)[];\n /** Also post reports to the DevTools bridge for the extension. Default true. */\n devtools?: boolean;\n /** Apply in `vite build` too (for staging builds you want to inspect). Default false. */\n applyInBuild?: boolean;\n /**\n * Serve the panel from the dev server (default path `/__rerender-lens/`) and publish reports on a\n * BroadcastChannel so it works without the extension. `true`, or a mount path. Default false.\n */\n panel?: boolean | string;\n /** BroadcastChannel name used with `panel`. Default `rerender-lens`. */\n channel?: string;\n /** Which HTML pages get the setup script: an allow-list of paths (`/`, `/admin.html`) or a predicate. Default all. */\n pages?: string[] | ((path: string) => boolean);\n};\n\nexport const VIRTUAL_ID = 'virtual:rerender-lens';\nconst RESOLVED_ID = '\\0' + VIRTUAL_ID;\n/** URL Vite serves the virtual module under (the `\\0` prefix is spelled `__x00__`). */\nexport const VIRTUAL_URL = '/@id/__x00__' + VIRTUAL_ID;\nexport const DEFAULT_PANEL_PATH = '/__rerender-lens/';\n\nconst serializeMatcher = (m: string | RegExp): string => (m instanceof RegExp ? `new RegExp(${JSON.stringify(m.source)}, ${JSON.stringify(m.flags)})` : JSON.stringify(m));\n\n/** Source of the virtual module. Exported for tests and for other bundlers' loaders. */\nexport function renderSetupModule(options: VitePluginOptions = {}): string {\n const { devtools = true, applyInBuild: _applyInBuild, panel, channel, pages: _pages, include, exclude, ...rest } = options;\n const entries: string[] = [];\n for (const [k, v] of Object.entries(rest)) if (v !== undefined) entries.push(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);\n if (include) entries.push(`include: [${include.map(serializeMatcher).join(', ')}]`);\n if (exclude) entries.push(`exclude: [${exclude.map(serializeMatcher).join(', ')}]`);\n const notifierOptions = panel ? `{ channel: ${JSON.stringify(channel || 'rerender-lens')} }` : '';\n if (devtools || panel) entries.push(`notifier: createDevtoolsNotifier(${notifierOptions})`);\n return [\n `import { init${devtools || panel ? ', createDevtoolsNotifier' : ''} } from 'rerender-lens';`,\n `init({ ${entries.join(', ')} });`,\n '',\n ].join('\\n');\n}\n\n/** Minimal shape of a Vite plugin, so `vite` stays an optional peer. */\nexport interface RerenderLensVitePlugin {\n name: string;\n apply?: 'serve' | 'build';\n enforce?: 'pre' | 'post';\n resolveId(id: string): string | undefined;\n load(id: string): string | undefined;\n transformIndexHtml: {\n order: 'pre';\n handler(html: string, ctx?: { path?: string; filename?: string }): { tag: string; attrs: Record<string, string>; injectTo: 'head-prepend' }[];\n };\n configureServer?(server: ViteServerLike): void;\n}\n\n/** The parts of Vite's dev server the plugin uses. */\nexport interface ViteServerLike {\n /** Connect-style; mounting without a path keeps `req.url` intact (a mount path would strip the prefix). */\n middlewares: { use(handler: (req: IncomingLike, res: ResponseLike, next: () => void) => void): void };\n config?: { logger?: { info(msg: string): void }; server?: { port?: number; host?: string | boolean } };\n resolvedUrls?: { local?: string[] } | null;\n httpServer?: { once(event: 'listening', cb: () => void): void } | null;\n}\ninterface IncomingLike {\n url?: string;\n method?: string;\n}\ninterface ResponseLike {\n statusCode: number;\n setHeader(name: string, value: string): void;\n end(body?: string | Uint8Array): void;\n}\n\n/** Directory holding panel.html/js/css: `panel/` next to `dist/` in the package, or the extension folder in this repo. */\nexport function panelDir(): string | null {\n const here = dirname(fileURLToPath(import.meta.url));\n for (const dir of [join(here, '..', 'panel'), join(here, '..', 'extension'), join(here, '..', '..', 'extension')]) {\n if (existsSync(join(dir, 'panel.html')) && existsSync(join(dir, 'panel.js'))) return dir;\n }\n return null;\n}\n\nconst MIME: Record<string, string> = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8' };\n\n/** Middleware serving the panel under `mount` (exported for tests). */\nexport function panelMiddleware(mount: string, channel: string, dir: string | null): (req: IncomingLike, res: ResponseLike, next: () => void) => void {\n const base = mount.endsWith('/') ? mount : mount + '/';\n return (req, res, next) => {\n const url = req.url || '/';\n if (!url.startsWith(base.slice(0, -1))) return next();\n const [pathOnly, query = ''] = url.split('?', 2);\n if (pathOnly === base.slice(0, -1) || pathOnly === base) {\n // The panel boots in channel mode from its query string.\n res.statusCode = 302;\n res.setHeader('Location', `${base}panel.html?channel=${encodeURIComponent(channel)}${query ? '&' + query : ''}`);\n res.end();\n return;\n }\n const file = pathOnly!.slice(base.length);\n if (!/^panel\\.(html|js|css)$/.test(file)) return next();\n if (!dir) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'text/plain');\n res.end('rerender-lens: panel files not found (is the package built?)');\n return;\n }\n res.statusCode = 200;\n res.setHeader('Content-Type', MIME[file.slice(file.lastIndexOf('.'))] || 'application/octet-stream');\n res.setHeader('Cache-Control', 'no-store');\n res.end(readFileSync(join(dir, file)));\n };\n}\n\nexport function rerenderLens(options: VitePluginOptions = {}): RerenderLensVitePlugin {\n const defaults: VitePluginOptions = { trackAllMemoized: true };\n const merged = { ...defaults, ...options };\n const pages = merged.pages;\n const wants = (path: string | undefined): boolean => {\n if (!pages) return true;\n const p = path || '/';\n if (typeof pages === 'function') return pages(p);\n return pages.some((x) => x === p || (x === '/' && p === '/index.html') || (x === '/index.html' && p === '/'));\n };\n const mount = typeof merged.panel === 'string' ? merged.panel : DEFAULT_PANEL_PATH;\n const plugin: RerenderLensVitePlugin = {\n name: 'rerender-lens',\n enforce: 'pre',\n resolveId(id) {\n return id === VIRTUAL_ID ? RESOLVED_ID : undefined;\n },\n load(id) {\n return id === RESOLVED_ID ? renderSetupModule(merged) : undefined;\n },\n transformIndexHtml: {\n order: 'pre',\n handler(_html, ctx) {\n if (!wants(ctx?.path)) return [];\n return [{ tag: 'script', attrs: { type: 'module', src: VIRTUAL_URL }, injectTo: 'head-prepend' }];\n },\n },\n };\n if (!merged.applyInBuild) plugin.apply = 'serve';\n if (merged.panel) {\n plugin.configureServer = (server) => {\n server.middlewares.use(panelMiddleware(mount, merged.channel || 'rerender-lens', panelDir()));\n const announce = (): void => {\n const local = server.resolvedUrls?.local?.[0];\n server.config?.logger?.info(` ➜ rerender-lens panel: ${local ? local.replace(/\\/$/, '') : ''}${mount}`);\n };\n if (server.httpServer) server.httpServer.once('listening', () => setTimeout(announce, 0));\n };\n }\n return plugin;\n}\n"]}
@@ -0,0 +1,84 @@
1
+ import { O as Options } from './types-BzUEVkxJ.cjs';
2
+
3
+ /** `Options` minus the parts that cannot be serialized into a module (functions). Matchers may be strings or RegExps. */
4
+ type VitePluginOptions = Omit<Options, 'notifier' | 'console' | 'include' | 'exclude'> & {
5
+ include?: (string | RegExp)[];
6
+ exclude?: (string | RegExp)[];
7
+ /** Also post reports to the DevTools bridge for the extension. Default true. */
8
+ devtools?: boolean;
9
+ /** Apply in `vite build` too (for staging builds you want to inspect). Default false. */
10
+ applyInBuild?: boolean;
11
+ /**
12
+ * Serve the panel from the dev server (default path `/__rerender-lens/`) and publish reports on a
13
+ * BroadcastChannel so it works without the extension. `true`, or a mount path. Default false.
14
+ */
15
+ panel?: boolean | string;
16
+ /** BroadcastChannel name used with `panel`. Default `rerender-lens`. */
17
+ channel?: string;
18
+ /** Which HTML pages get the setup script: an allow-list of paths (`/`, `/admin.html`) or a predicate. Default all. */
19
+ pages?: string[] | ((path: string) => boolean);
20
+ };
21
+ declare const VIRTUAL_ID = "virtual:rerender-lens";
22
+ /** URL Vite serves the virtual module under (the `\0` prefix is spelled `__x00__`). */
23
+ declare const VIRTUAL_URL: string;
24
+ declare const DEFAULT_PANEL_PATH = "/__rerender-lens/";
25
+ /** Source of the virtual module. Exported for tests and for other bundlers' loaders. */
26
+ declare function renderSetupModule(options?: VitePluginOptions): string;
27
+ /** Minimal shape of a Vite plugin, so `vite` stays an optional peer. */
28
+ interface RerenderLensVitePlugin {
29
+ name: string;
30
+ apply?: 'serve' | 'build';
31
+ enforce?: 'pre' | 'post';
32
+ resolveId(id: string): string | undefined;
33
+ load(id: string): string | undefined;
34
+ transformIndexHtml: {
35
+ order: 'pre';
36
+ handler(html: string, ctx?: {
37
+ path?: string;
38
+ filename?: string;
39
+ }): {
40
+ tag: string;
41
+ attrs: Record<string, string>;
42
+ injectTo: 'head-prepend';
43
+ }[];
44
+ };
45
+ configureServer?(server: ViteServerLike): void;
46
+ }
47
+ /** The parts of Vite's dev server the plugin uses. */
48
+ interface ViteServerLike {
49
+ /** Connect-style; mounting without a path keeps `req.url` intact (a mount path would strip the prefix). */
50
+ middlewares: {
51
+ use(handler: (req: IncomingLike, res: ResponseLike, next: () => void) => void): void;
52
+ };
53
+ config?: {
54
+ logger?: {
55
+ info(msg: string): void;
56
+ };
57
+ server?: {
58
+ port?: number;
59
+ host?: string | boolean;
60
+ };
61
+ };
62
+ resolvedUrls?: {
63
+ local?: string[];
64
+ } | null;
65
+ httpServer?: {
66
+ once(event: 'listening', cb: () => void): void;
67
+ } | null;
68
+ }
69
+ interface IncomingLike {
70
+ url?: string;
71
+ method?: string;
72
+ }
73
+ interface ResponseLike {
74
+ statusCode: number;
75
+ setHeader(name: string, value: string): void;
76
+ end(body?: string | Uint8Array): void;
77
+ }
78
+ /** Directory holding panel.html/js/css: `panel/` next to `dist/` in the package, or the extension folder in this repo. */
79
+ declare function panelDir(): string | null;
80
+ /** Middleware serving the panel under `mount` (exported for tests). */
81
+ declare function panelMiddleware(mount: string, channel: string, dir: string | null): (req: IncomingLike, res: ResponseLike, next: () => void) => void;
82
+ declare function rerenderLens(options?: VitePluginOptions): RerenderLensVitePlugin;
83
+
84
+ export { DEFAULT_PANEL_PATH, type RerenderLensVitePlugin, VIRTUAL_ID, VIRTUAL_URL, type VitePluginOptions, type ViteServerLike, panelDir, panelMiddleware, renderSetupModule, rerenderLens };
package/dist/vite.d.ts ADDED
@@ -0,0 +1,84 @@
1
+ import { O as Options } from './types-BzUEVkxJ.js';
2
+
3
+ /** `Options` minus the parts that cannot be serialized into a module (functions). Matchers may be strings or RegExps. */
4
+ type VitePluginOptions = Omit<Options, 'notifier' | 'console' | 'include' | 'exclude'> & {
5
+ include?: (string | RegExp)[];
6
+ exclude?: (string | RegExp)[];
7
+ /** Also post reports to the DevTools bridge for the extension. Default true. */
8
+ devtools?: boolean;
9
+ /** Apply in `vite build` too (for staging builds you want to inspect). Default false. */
10
+ applyInBuild?: boolean;
11
+ /**
12
+ * Serve the panel from the dev server (default path `/__rerender-lens/`) and publish reports on a
13
+ * BroadcastChannel so it works without the extension. `true`, or a mount path. Default false.
14
+ */
15
+ panel?: boolean | string;
16
+ /** BroadcastChannel name used with `panel`. Default `rerender-lens`. */
17
+ channel?: string;
18
+ /** Which HTML pages get the setup script: an allow-list of paths (`/`, `/admin.html`) or a predicate. Default all. */
19
+ pages?: string[] | ((path: string) => boolean);
20
+ };
21
+ declare const VIRTUAL_ID = "virtual:rerender-lens";
22
+ /** URL Vite serves the virtual module under (the `\0` prefix is spelled `__x00__`). */
23
+ declare const VIRTUAL_URL: string;
24
+ declare const DEFAULT_PANEL_PATH = "/__rerender-lens/";
25
+ /** Source of the virtual module. Exported for tests and for other bundlers' loaders. */
26
+ declare function renderSetupModule(options?: VitePluginOptions): string;
27
+ /** Minimal shape of a Vite plugin, so `vite` stays an optional peer. */
28
+ interface RerenderLensVitePlugin {
29
+ name: string;
30
+ apply?: 'serve' | 'build';
31
+ enforce?: 'pre' | 'post';
32
+ resolveId(id: string): string | undefined;
33
+ load(id: string): string | undefined;
34
+ transformIndexHtml: {
35
+ order: 'pre';
36
+ handler(html: string, ctx?: {
37
+ path?: string;
38
+ filename?: string;
39
+ }): {
40
+ tag: string;
41
+ attrs: Record<string, string>;
42
+ injectTo: 'head-prepend';
43
+ }[];
44
+ };
45
+ configureServer?(server: ViteServerLike): void;
46
+ }
47
+ /** The parts of Vite's dev server the plugin uses. */
48
+ interface ViteServerLike {
49
+ /** Connect-style; mounting without a path keeps `req.url` intact (a mount path would strip the prefix). */
50
+ middlewares: {
51
+ use(handler: (req: IncomingLike, res: ResponseLike, next: () => void) => void): void;
52
+ };
53
+ config?: {
54
+ logger?: {
55
+ info(msg: string): void;
56
+ };
57
+ server?: {
58
+ port?: number;
59
+ host?: string | boolean;
60
+ };
61
+ };
62
+ resolvedUrls?: {
63
+ local?: string[];
64
+ } | null;
65
+ httpServer?: {
66
+ once(event: 'listening', cb: () => void): void;
67
+ } | null;
68
+ }
69
+ interface IncomingLike {
70
+ url?: string;
71
+ method?: string;
72
+ }
73
+ interface ResponseLike {
74
+ statusCode: number;
75
+ setHeader(name: string, value: string): void;
76
+ end(body?: string | Uint8Array): void;
77
+ }
78
+ /** Directory holding panel.html/js/css: `panel/` next to `dist/` in the package, or the extension folder in this repo. */
79
+ declare function panelDir(): string | null;
80
+ /** Middleware serving the panel under `mount` (exported for tests). */
81
+ declare function panelMiddleware(mount: string, channel: string, dir: string | null): (req: IncomingLike, res: ResponseLike, next: () => void) => void;
82
+ declare function rerenderLens(options?: VitePluginOptions): RerenderLensVitePlugin;
83
+
84
+ export { DEFAULT_PANEL_PATH, type RerenderLensVitePlugin, VIRTUAL_ID, VIRTUAL_URL, type VitePluginOptions, type ViteServerLike, panelDir, panelMiddleware, renderSetupModule, rerenderLens };
package/dist/vite.js ADDED
@@ -0,0 +1,103 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ // src/vite.ts
6
+ var VIRTUAL_ID = "virtual:rerender-lens";
7
+ var RESOLVED_ID = "\0" + VIRTUAL_ID;
8
+ var VIRTUAL_URL = "/@id/__x00__" + VIRTUAL_ID;
9
+ var DEFAULT_PANEL_PATH = "/__rerender-lens/";
10
+ var serializeMatcher = (m) => m instanceof RegExp ? `new RegExp(${JSON.stringify(m.source)}, ${JSON.stringify(m.flags)})` : JSON.stringify(m);
11
+ function renderSetupModule(options = {}) {
12
+ const { devtools = true, applyInBuild: _applyInBuild, panel, channel, pages: _pages, include, exclude, ...rest } = options;
13
+ const entries = [];
14
+ for (const [k, v] of Object.entries(rest)) if (v !== void 0) entries.push(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);
15
+ if (include) entries.push(`include: [${include.map(serializeMatcher).join(", ")}]`);
16
+ if (exclude) entries.push(`exclude: [${exclude.map(serializeMatcher).join(", ")}]`);
17
+ const notifierOptions = panel ? `{ channel: ${JSON.stringify(channel || "rerender-lens")} }` : "";
18
+ if (devtools || panel) entries.push(`notifier: createDevtoolsNotifier(${notifierOptions})`);
19
+ return [
20
+ `import { init${devtools || panel ? ", createDevtoolsNotifier" : ""} } from 'rerender-lens';`,
21
+ `init({ ${entries.join(", ")} });`,
22
+ ""
23
+ ].join("\n");
24
+ }
25
+ function panelDir() {
26
+ const here = dirname(fileURLToPath(import.meta.url));
27
+ for (const dir of [join(here, "..", "panel"), join(here, "..", "extension"), join(here, "..", "..", "extension")]) {
28
+ if (existsSync(join(dir, "panel.html")) && existsSync(join(dir, "panel.js"))) return dir;
29
+ }
30
+ return null;
31
+ }
32
+ var MIME = { ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8" };
33
+ function panelMiddleware(mount, channel, dir) {
34
+ const base = mount.endsWith("/") ? mount : mount + "/";
35
+ return (req, res, next) => {
36
+ const url = req.url || "/";
37
+ if (!url.startsWith(base.slice(0, -1))) return next();
38
+ const [pathOnly, query = ""] = url.split("?", 2);
39
+ if (pathOnly === base.slice(0, -1) || pathOnly === base) {
40
+ res.statusCode = 302;
41
+ res.setHeader("Location", `${base}panel.html?channel=${encodeURIComponent(channel)}${query ? "&" + query : ""}`);
42
+ res.end();
43
+ return;
44
+ }
45
+ const file = pathOnly.slice(base.length);
46
+ if (!/^panel\.(html|js|css)$/.test(file)) return next();
47
+ if (!dir) {
48
+ res.statusCode = 500;
49
+ res.setHeader("Content-Type", "text/plain");
50
+ res.end("rerender-lens: panel files not found (is the package built?)");
51
+ return;
52
+ }
53
+ res.statusCode = 200;
54
+ res.setHeader("Content-Type", MIME[file.slice(file.lastIndexOf("."))] || "application/octet-stream");
55
+ res.setHeader("Cache-Control", "no-store");
56
+ res.end(readFileSync(join(dir, file)));
57
+ };
58
+ }
59
+ function rerenderLens(options = {}) {
60
+ const defaults = { trackAllMemoized: true };
61
+ const merged = { ...defaults, ...options };
62
+ const pages = merged.pages;
63
+ const wants = (path) => {
64
+ if (!pages) return true;
65
+ const p = path || "/";
66
+ if (typeof pages === "function") return pages(p);
67
+ return pages.some((x) => x === p || x === "/" && p === "/index.html" || x === "/index.html" && p === "/");
68
+ };
69
+ const mount = typeof merged.panel === "string" ? merged.panel : DEFAULT_PANEL_PATH;
70
+ const plugin = {
71
+ name: "rerender-lens",
72
+ enforce: "pre",
73
+ resolveId(id) {
74
+ return id === VIRTUAL_ID ? RESOLVED_ID : void 0;
75
+ },
76
+ load(id) {
77
+ return id === RESOLVED_ID ? renderSetupModule(merged) : void 0;
78
+ },
79
+ transformIndexHtml: {
80
+ order: "pre",
81
+ handler(_html, ctx) {
82
+ if (!wants(ctx?.path)) return [];
83
+ return [{ tag: "script", attrs: { type: "module", src: VIRTUAL_URL }, injectTo: "head-prepend" }];
84
+ }
85
+ }
86
+ };
87
+ if (!merged.applyInBuild) plugin.apply = "serve";
88
+ if (merged.panel) {
89
+ plugin.configureServer = (server) => {
90
+ server.middlewares.use(panelMiddleware(mount, merged.channel || "rerender-lens", panelDir()));
91
+ const announce = () => {
92
+ const local = server.resolvedUrls?.local?.[0];
93
+ server.config?.logger?.info(` \u279C rerender-lens panel: ${local ? local.replace(/\/$/, "") : ""}${mount}`);
94
+ };
95
+ if (server.httpServer) server.httpServer.once("listening", () => setTimeout(announce, 0));
96
+ };
97
+ }
98
+ return plugin;
99
+ }
100
+
101
+ export { DEFAULT_PANEL_PATH, VIRTUAL_ID, VIRTUAL_URL, panelDir, panelMiddleware, renderSetupModule, rerenderLens };
102
+ //# sourceMappingURL=vite.js.map
103
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vite.ts"],"names":[],"mappings":"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","file":"vite.js","sourcesContent":["/**\n * Vite plugin: `rerenderLens()` in `vite.config.ts` starts rerender-lens in dev before React loads,\n * with the DevTools notifier, so the extension panel works without touching the app.\n *\n * With `panel: true` it also serves the panel itself at `/__rerender-lens/` (no extension needed): the\n * app publishes on a same-origin `BroadcastChannel`, the panel tab listens and sends commands back.\n *\n * The plugin serves a virtual module that imports `rerender-lens` and calls `init`, and injects a\n * `<script type=\"module\">` for it at the top of `<head>`. Module scripts execute in document order,\n * so it runs before the app entry (and after Vite's Fast Refresh preamble, which is fine: `init`\n * wraps whatever DevTools hook exists). Nothing happens in `vite build`.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Options } from './types';\n\n/** `Options` minus the parts that cannot be serialized into a module (functions). Matchers may be strings or RegExps. */\nexport type VitePluginOptions = Omit<Options, 'notifier' | 'console' | 'include' | 'exclude'> & {\n include?: (string | RegExp)[];\n exclude?: (string | RegExp)[];\n /** Also post reports to the DevTools bridge for the extension. Default true. */\n devtools?: boolean;\n /** Apply in `vite build` too (for staging builds you want to inspect). Default false. */\n applyInBuild?: boolean;\n /**\n * Serve the panel from the dev server (default path `/__rerender-lens/`) and publish reports on a\n * BroadcastChannel so it works without the extension. `true`, or a mount path. Default false.\n */\n panel?: boolean | string;\n /** BroadcastChannel name used with `panel`. Default `rerender-lens`. */\n channel?: string;\n /** Which HTML pages get the setup script: an allow-list of paths (`/`, `/admin.html`) or a predicate. Default all. */\n pages?: string[] | ((path: string) => boolean);\n};\n\nexport const VIRTUAL_ID = 'virtual:rerender-lens';\nconst RESOLVED_ID = '\\0' + VIRTUAL_ID;\n/** URL Vite serves the virtual module under (the `\\0` prefix is spelled `__x00__`). */\nexport const VIRTUAL_URL = '/@id/__x00__' + VIRTUAL_ID;\nexport const DEFAULT_PANEL_PATH = '/__rerender-lens/';\n\nconst serializeMatcher = (m: string | RegExp): string => (m instanceof RegExp ? `new RegExp(${JSON.stringify(m.source)}, ${JSON.stringify(m.flags)})` : JSON.stringify(m));\n\n/** Source of the virtual module. Exported for tests and for other bundlers' loaders. */\nexport function renderSetupModule(options: VitePluginOptions = {}): string {\n const { devtools = true, applyInBuild: _applyInBuild, panel, channel, pages: _pages, include, exclude, ...rest } = options;\n const entries: string[] = [];\n for (const [k, v] of Object.entries(rest)) if (v !== undefined) entries.push(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);\n if (include) entries.push(`include: [${include.map(serializeMatcher).join(', ')}]`);\n if (exclude) entries.push(`exclude: [${exclude.map(serializeMatcher).join(', ')}]`);\n const notifierOptions = panel ? `{ channel: ${JSON.stringify(channel || 'rerender-lens')} }` : '';\n if (devtools || panel) entries.push(`notifier: createDevtoolsNotifier(${notifierOptions})`);\n return [\n `import { init${devtools || panel ? ', createDevtoolsNotifier' : ''} } from 'rerender-lens';`,\n `init({ ${entries.join(', ')} });`,\n '',\n ].join('\\n');\n}\n\n/** Minimal shape of a Vite plugin, so `vite` stays an optional peer. */\nexport interface RerenderLensVitePlugin {\n name: string;\n apply?: 'serve' | 'build';\n enforce?: 'pre' | 'post';\n resolveId(id: string): string | undefined;\n load(id: string): string | undefined;\n transformIndexHtml: {\n order: 'pre';\n handler(html: string, ctx?: { path?: string; filename?: string }): { tag: string; attrs: Record<string, string>; injectTo: 'head-prepend' }[];\n };\n configureServer?(server: ViteServerLike): void;\n}\n\n/** The parts of Vite's dev server the plugin uses. */\nexport interface ViteServerLike {\n /** Connect-style; mounting without a path keeps `req.url` intact (a mount path would strip the prefix). */\n middlewares: { use(handler: (req: IncomingLike, res: ResponseLike, next: () => void) => void): void };\n config?: { logger?: { info(msg: string): void }; server?: { port?: number; host?: string | boolean } };\n resolvedUrls?: { local?: string[] } | null;\n httpServer?: { once(event: 'listening', cb: () => void): void } | null;\n}\ninterface IncomingLike {\n url?: string;\n method?: string;\n}\ninterface ResponseLike {\n statusCode: number;\n setHeader(name: string, value: string): void;\n end(body?: string | Uint8Array): void;\n}\n\n/** Directory holding panel.html/js/css: `panel/` next to `dist/` in the package, or the extension folder in this repo. */\nexport function panelDir(): string | null {\n const here = dirname(fileURLToPath(import.meta.url));\n for (const dir of [join(here, '..', 'panel'), join(here, '..', 'extension'), join(here, '..', '..', 'extension')]) {\n if (existsSync(join(dir, 'panel.html')) && existsSync(join(dir, 'panel.js'))) return dir;\n }\n return null;\n}\n\nconst MIME: Record<string, string> = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8' };\n\n/** Middleware serving the panel under `mount` (exported for tests). */\nexport function panelMiddleware(mount: string, channel: string, dir: string | null): (req: IncomingLike, res: ResponseLike, next: () => void) => void {\n const base = mount.endsWith('/') ? mount : mount + '/';\n return (req, res, next) => {\n const url = req.url || '/';\n if (!url.startsWith(base.slice(0, -1))) return next();\n const [pathOnly, query = ''] = url.split('?', 2);\n if (pathOnly === base.slice(0, -1) || pathOnly === base) {\n // The panel boots in channel mode from its query string.\n res.statusCode = 302;\n res.setHeader('Location', `${base}panel.html?channel=${encodeURIComponent(channel)}${query ? '&' + query : ''}`);\n res.end();\n return;\n }\n const file = pathOnly!.slice(base.length);\n if (!/^panel\\.(html|js|css)$/.test(file)) return next();\n if (!dir) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'text/plain');\n res.end('rerender-lens: panel files not found (is the package built?)');\n return;\n }\n res.statusCode = 200;\n res.setHeader('Content-Type', MIME[file.slice(file.lastIndexOf('.'))] || 'application/octet-stream');\n res.setHeader('Cache-Control', 'no-store');\n res.end(readFileSync(join(dir, file)));\n };\n}\n\nexport function rerenderLens(options: VitePluginOptions = {}): RerenderLensVitePlugin {\n const defaults: VitePluginOptions = { trackAllMemoized: true };\n const merged = { ...defaults, ...options };\n const pages = merged.pages;\n const wants = (path: string | undefined): boolean => {\n if (!pages) return true;\n const p = path || '/';\n if (typeof pages === 'function') return pages(p);\n return pages.some((x) => x === p || (x === '/' && p === '/index.html') || (x === '/index.html' && p === '/'));\n };\n const mount = typeof merged.panel === 'string' ? merged.panel : DEFAULT_PANEL_PATH;\n const plugin: RerenderLensVitePlugin = {\n name: 'rerender-lens',\n enforce: 'pre',\n resolveId(id) {\n return id === VIRTUAL_ID ? RESOLVED_ID : undefined;\n },\n load(id) {\n return id === RESOLVED_ID ? renderSetupModule(merged) : undefined;\n },\n transformIndexHtml: {\n order: 'pre',\n handler(_html, ctx) {\n if (!wants(ctx?.path)) return [];\n return [{ tag: 'script', attrs: { type: 'module', src: VIRTUAL_URL }, injectTo: 'head-prepend' }];\n },\n },\n };\n if (!merged.applyInBuild) plugin.apply = 'serve';\n if (merged.panel) {\n plugin.configureServer = (server) => {\n server.middlewares.use(panelMiddleware(mount, merged.channel || 'rerender-lens', panelDir()));\n const announce = (): void => {\n const local = server.resolvedUrls?.local?.[0];\n server.config?.logger?.info(` ➜ rerender-lens panel: ${local ? local.replace(/\\/$/, '') : ''}${mount}`);\n };\n if (server.httpServer) server.httpServer.once('listening', () => setTimeout(announce, 0));\n };\n }\n return plugin;\n}\n"]}