react-render-detective 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.
- package/README.md +1 -1
- package/dist/babel.cjs +2 -2
- package/dist/babel.js +1 -1
- package/dist/{chunk-6YPJQ7M5.cjs → chunk-DZ3BZ654.cjs} +88 -4
- package/dist/chunk-DZ3BZ654.cjs.map +1 -0
- package/dist/{chunk-LSIXHWQD.js → chunk-HOTY7J3X.js} +14 -4
- package/dist/chunk-HOTY7J3X.js.map +1 -0
- package/dist/{chunk-QQ4VARSD.cjs → chunk-JD4IJ4MN.cjs} +14 -4
- package/dist/chunk-JD4IJ4MN.cjs.map +1 -0
- package/dist/{chunk-PSCFSJ65.js → chunk-MCIQMYNR.js} +12 -5
- package/dist/chunk-MCIQMYNR.js.map +1 -0
- package/dist/{chunk-BPNLNIHU.cjs → chunk-PHYYA67T.cjs} +12 -5
- package/dist/chunk-PHYYA67T.cjs.map +1 -0
- package/dist/{chunk-MSISKPST.js → chunk-QOGTEVBP.js} +88 -4
- package/dist/chunk-QOGTEVBP.js.map +1 -0
- package/dist/core.cjs +18 -18
- package/dist/core.d.cts +38 -2
- package/dist/core.d.ts +38 -2
- package/dist/core.js +1 -1
- package/dist/index.cjs +40 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -3
- package/dist/index.d.ts +13 -3
- package/dist/index.js +14 -5
- package/dist/index.js.map +1 -1
- package/dist/overlay.cjs +6 -6
- package/dist/overlay.cjs.map +1 -1
- package/dist/overlay.js +4 -4
- package/dist/overlay.js.map +1 -1
- package/dist/{types-BWVaQPHb.d.cts → types-gl13xwmN.d.cts} +5 -0
- package/dist/{types-BWVaQPHb.d.ts → types-gl13xwmN.d.ts} +5 -0
- package/dist/vite.cjs +9 -6
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +8 -5
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6YPJQ7M5.cjs.map +0 -1
- package/dist/chunk-BPNLNIHU.cjs.map +0 -1
- package/dist/chunk-LSIXHWQD.js.map +0 -1
- package/dist/chunk-MSISKPST.js.map +0 -1
- package/dist/chunk-PSCFSJ65.js.map +0 -1
- package/dist/chunk-QQ4VARSD.cjs.map +0 -1
|
@@ -4,6 +4,7 @@ var SELF_PATTERN = /[/\\]react-render-detective[/\\](src|dist)[/\\]/;
|
|
|
4
4
|
var HOC_NAMES = /* @__PURE__ */ new Set(["memo", "forwardRef"]);
|
|
5
5
|
function renderDetectiveBabelPlugin(api) {
|
|
6
6
|
const t = api.types;
|
|
7
|
+
const processed = /* @__PURE__ */ new WeakSet();
|
|
7
8
|
api.assertVersion?.("^7.0.0-0 || ^8.0.0-0");
|
|
8
9
|
const isComponentName = (name) => !!name && /^[A-Z]/.test(name);
|
|
9
10
|
function returnsJsx(path) {
|
|
@@ -57,6 +58,9 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
57
58
|
];
|
|
58
59
|
const source = locationOf(node, state);
|
|
59
60
|
if (source) properties.push(t.objectProperty(t.identifier("source"), t.stringLiteral(source)));
|
|
61
|
+
if (path.getFunctionParent()) {
|
|
62
|
+
properties.push(t.objectProperty(t.identifier("declaredInRender"), t.booleanLiteral(true)));
|
|
63
|
+
}
|
|
60
64
|
state.rrdTouched = true;
|
|
61
65
|
return t.callExpression(ensureImport(path, state), [fn, t.objectExpression(properties)]);
|
|
62
66
|
}
|
|
@@ -90,6 +94,8 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
90
94
|
const id = path.node.id;
|
|
91
95
|
if (!id || !isComponentName(id.name)) return;
|
|
92
96
|
if (!returnsJsx(path)) return;
|
|
97
|
+
if (processed.has(path.node)) return;
|
|
98
|
+
processed.add(path.node);
|
|
93
99
|
const wrapped = wrap(t.cloneNode(id), id.name, path.node, path, state);
|
|
94
100
|
const assignment = t.expressionStatement(t.assignmentExpression("=", t.cloneNode(id), wrapped));
|
|
95
101
|
assignment._rrdGenerated = true;
|
|
@@ -99,7 +105,6 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
99
105
|
} else {
|
|
100
106
|
path.insertAfter(assignment);
|
|
101
107
|
}
|
|
102
|
-
path.skip();
|
|
103
108
|
},
|
|
104
109
|
/** `const Foo = () => <div />` and `const Foo = memo(() => <div />)` */
|
|
105
110
|
VariableDeclarator(path, state) {
|
|
@@ -118,8 +123,9 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
118
123
|
if (!target.isArrowFunctionExpression() && !target.isFunctionExpression()) return;
|
|
119
124
|
if (isAlreadyWrapped(target)) return;
|
|
120
125
|
if (!returnsJsx(target)) return;
|
|
126
|
+
if (processed.has(path.node)) return;
|
|
127
|
+
processed.add(path.node);
|
|
121
128
|
target.replaceWith(wrap(target.node, id.name, path.node, path, state));
|
|
122
|
-
path.skip();
|
|
123
129
|
},
|
|
124
130
|
/** `export default function () { return <div /> }` */
|
|
125
131
|
ExportDefaultDeclaration(path, state) {
|
|
@@ -134,8 +140,9 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
134
140
|
declaration.node.generator,
|
|
135
141
|
declaration.node.async
|
|
136
142
|
);
|
|
143
|
+
if (processed.has(path.node)) return;
|
|
144
|
+
processed.add(path.node);
|
|
137
145
|
path.replaceWith(t.exportDefaultDeclaration(wrap(expression, name, declaration.node, path, state)));
|
|
138
|
-
path.skip();
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
};
|
|
@@ -156,5 +163,5 @@ function componentNameFromFile(path) {
|
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
export { renderDetectiveBabelPlugin };
|
|
159
|
-
//# sourceMappingURL=chunk-
|
|
160
|
-
//# sourceMappingURL=chunk-
|
|
166
|
+
//# sourceMappingURL=chunk-MCIQMYNR.js.map
|
|
167
|
+
//# sourceMappingURL=chunk-MCIQMYNR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/babel/index.ts"],"names":[],"mappings":"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","file":"chunk-MCIQMYNR.js","sourcesContent":["/**\n * Babel plugin: instrument every React component in a file at build time.\n *\n * Why this exists: without it you only learn about components you already\n * suspected, because you have to wrap them by hand. With it, the tool reports\n * on the whole tree — and it can attach a real source location, which React\n * itself no longer exposes at runtime (`_debugSource` was removed in React 19).\n *\n * Build-time only. It must never run in a production build, and it removes\n * itself when `NODE_ENV` is production unless explicitly forced.\n */\nimport type { NodePath, PluginObject, PluginPass, types as BabelTypes } from \"@babel/core\";\n\nexport interface RenderDetectivePluginOptions {\n /** Defaults to `NODE_ENV !== \"production\"`. */\n enabled?: boolean;\n /** Only instrument files whose path matches. Empty = all files. */\n include?: Array<string | RegExp>;\n /** Skip files whose path matches. Applied after `include`. */\n exclude?: Array<string | RegExp>;\n /**\n * Skip files with no `\"use client\"` directive. Turn this on for the Next.js\n * app router: server components never render on the client, and wrapping one\n * in a hook-using HOC would break the build.\n */\n clientOnly?: boolean;\n /** Import specifier for the runtime. Overridable for testing and monorepos. */\n importSource?: string;\n /** Root used to make source locations relative. Defaults to `process.cwd()`. */\n root?: string;\n}\n\ninterface State extends PluginPass {\n opts: RenderDetectivePluginOptions;\n rrdLocal?: BabelTypes.Identifier;\n rrdTouched?: boolean;\n rrdRelativePath?: string;\n}\n\nconst DEFAULT_IMPORT_SOURCE = \"react-render-detective\";\n\n/*\n * Never instrument the detective's own runtime. Its HOC returns a component\n * that renders the component it was given; instrumenting that makes it render\n * itself, and the app dies with a stack overflow before the first paint.\n *\n * node_modules covers the normal install. This covers a source alias — how the\n * bundled example consumes the package, and how anyone working on it locally\n * would too.\n */\nconst SELF_PATTERN = /[/\\\\]react-render-detective[/\\\\](src|dist)[/\\\\]/;\nconst HOC_NAMES = new Set([\"memo\", \"forwardRef\"]);\n\nexport default function renderDetectiveBabelPlugin(\n api: { types: typeof BabelTypes; assertVersion?: (v: number | string) => void },\n): PluginObject {\n const t = api.types;\n /*\n * Guards against re-processing a node we just rewrote. Using `path.skip()`\n * for that was a mistake: it skips the entire subtree, so components declared\n * *inside* another component were never instrumented — exactly the case the\n * remount diagnosis exists to catch.\n */\n const processed = new WeakSet<BabelTypes.Node>();\n // Babel 7 and 8 both work: only `types`, `NodePath` and the visitor shape are\n // used, all stable across the major.\n api.assertVersion?.(\"^7.0.0-0 || ^8.0.0-0\");\n\n /**\n * A component is an uppercase-named function that returns JSX.\n *\n * No all-caps exclusion: it was rejecting `const A = () => <i />`, a perfectly\n * ordinary component, to guard against constants that in practice are values\n * rather than functions returning JSX — and the JSX check already covers that.\n */\n const isComponentName = (name: string | undefined | null): boolean => !!name && /^[A-Z]/.test(name);\n\n function returnsJsx(path: NodePath<BabelTypes.Function>): boolean {\n let found = false;\n const body = path.get(\"body\");\n\n // Concise arrow body: `() => <div />` or `() => cond ? <a/> : <b/>`\n if (!Array.isArray(body) && !body.isBlockStatement()) {\n const node = (body as NodePath).node;\n return containsJsx(node);\n }\n\n path.traverse({\n Function(inner) {\n inner.skip(); // JSX in a nested closure belongs to that closure\n },\n ReturnStatement(ret) {\n if (ret.node.argument && containsJsx(ret.node.argument)) found = true;\n },\n });\n return found;\n }\n\n function containsJsx(node: BabelTypes.Node | null | undefined): boolean {\n if (!node) return false;\n if (t.isJSXElement(node) || t.isJSXFragment(node)) return true;\n if (t.isConditionalExpression(node)) return containsJsx(node.consequent) || containsJsx(node.alternate);\n if (t.isLogicalExpression(node)) return containsJsx(node.left) || containsJsx(node.right);\n if (t.isParenthesizedExpression(node)) return containsJsx(node.expression);\n if (t.isCallExpression(node)) {\n // `memo(() => <div />)` and friends\n return node.arguments.some((a) => containsJsx(a as BabelTypes.Node));\n }\n return false;\n }\n\n function ensureImport(path: NodePath, state: State): BabelTypes.Identifier {\n if (state.rrdLocal) return state.rrdLocal;\n const program = path.findParent((p) => p.isProgram()) as NodePath<BabelTypes.Program>;\n const local = program.scope.generateUidIdentifier(\"rrdTrack\");\n const source = state.opts.importSource ?? DEFAULT_IMPORT_SOURCE;\n program.unshiftContainer(\n \"body\",\n t.importDeclaration([t.importSpecifier(local, t.identifier(\"withRenderDetective\"))], t.stringLiteral(source)),\n );\n state.rrdLocal = local;\n return local;\n }\n\n function locationOf(node: BabelTypes.Node, state: State): string | undefined {\n const line = node.loc?.start.line;\n if (line === undefined || !state.rrdRelativePath) return undefined;\n return `${state.rrdRelativePath}:${line}:${(node.loc?.start.column ?? 0) + 1}`;\n }\n\n /** `__rrdTrack(fn, { name, source })` */\n function wrap(\n fn: BabelTypes.Expression,\n name: string,\n node: BabelTypes.Node,\n path: NodePath,\n state: State,\n ): BabelTypes.CallExpression {\n const properties: BabelTypes.ObjectProperty[] = [\n t.objectProperty(t.identifier(\"name\"), t.stringLiteral(name)),\n ];\n const source = locationOf(node, state);\n if (source) properties.push(t.objectProperty(t.identifier(\"source\"), t.stringLiteral(source)));\n\n /*\n * Whether a component is declared inside another function is a *static*\n * fact, and the compiler is standing right here. The runtime previously\n * guessed at it by timing repeated definitions, which failed the moment a\n * user clicked more than five seconds apart.\n */\n if (path.getFunctionParent()) {\n properties.push(t.objectProperty(t.identifier(\"declaredInRender\"), t.booleanLiteral(true)));\n }\n state.rrdTouched = true;\n return t.callExpression(ensureImport(path, state), [fn, t.objectExpression(properties)]);\n }\n\n /** Already wrapped, by hand or by a previous pass. */\n function isAlreadyWrapped(path: NodePath): boolean {\n const parent = path.parentPath;\n if (!parent?.isCallExpression()) return false;\n const callee = parent.node.callee;\n if (t.isIdentifier(callee) && /rrdTrack|withRenderDetective/.test(callee.name)) return true;\n if (t.isMemberExpression(callee) && t.isIdentifier(callee.property, { name: \"withRenderDetective\" })) return true;\n return false;\n }\n\n return {\n name: \"react-render-detective\",\n\n pre(this: State, file: { opts: { filename?: string | null; root?: string | null } }) {\n const state = this;\n const filename = file.opts.filename ?? \"\";\n const root = state.opts.root ?? file.opts.root ?? process.cwd();\n state.rrdRelativePath = filename.startsWith(root) ? filename.slice(root.length).replace(/^[/\\\\]/, \"\") : filename;\n },\n\n visitor: {\n Program: {\n enter(path: NodePath<BabelTypes.Program>, state: State) {\n const enabled = state.opts.enabled ?? process.env.NODE_ENV !== \"production\";\n const filename = state.file.opts.filename ?? \"\";\n\n const skip =\n !enabled ||\n !filename ||\n /node_modules/.test(filename) ||\n SELF_PATTERN.test(filename) ||\n !matches(filename, state.opts.include, true) ||\n matches(filename, state.opts.exclude, false) ||\n (state.opts.clientOnly === true && !hasUseClient(path.node));\n\n if (skip) path.skip();\n },\n },\n\n /** `function Foo() { return <div /> }` */\n FunctionDeclaration(path: NodePath<BabelTypes.FunctionDeclaration>, state: State) {\n const id = path.node.id;\n if (!id || !isComponentName(id.name)) return;\n if (!returnsJsx(path as NodePath<BabelTypes.Function>)) return;\n\n /*\n * Reassign rather than rewrite the declaration: function declarations\n * hoist, and components are routinely used above their definition. A\n * `const` would turn that into a temporal dead zone error at runtime.\n * ESM export bindings are live, so `export function Foo` still exports\n * the wrapper.\n */\n if (processed.has(path.node)) return;\n processed.add(path.node);\n\n const wrapped = wrap(t.cloneNode(id), id.name, path.node, path, state);\n const assignment = t.expressionStatement(t.assignmentExpression(\"=\", t.cloneNode(id), wrapped));\n (assignment as { _rrdGenerated?: boolean })._rrdGenerated = true;\n\n const statementParent = path.getStatementParent();\n if (path.parentPath.isExportDefaultDeclaration() || path.parentPath.isExportNamedDeclaration()) {\n statementParent?.insertAfter(assignment);\n } else {\n path.insertAfter(assignment);\n }\n },\n\n /** `const Foo = () => <div />` and `const Foo = memo(() => <div />)` */\n VariableDeclarator(path: NodePath<BabelTypes.VariableDeclarator>, state: State) {\n const id = path.node.id;\n const init = path.node.init;\n if (!t.isIdentifier(id) || !init || !isComponentName(id.name)) return;\n\n // Unwrap one level of memo()/forwardRef() so the instrumentation sits\n // inside them: memo compares props before our wrapper ever renders.\n let target: NodePath = path.get(\"init\") as NodePath;\n if (target.isCallExpression()) {\n const callee = target.node.callee;\n const isHoc =\n (t.isIdentifier(callee) && HOC_NAMES.has(callee.name)) ||\n (t.isMemberExpression(callee) && t.isIdentifier(callee.property) && HOC_NAMES.has(callee.property.name));\n if (!isHoc) return;\n const first = (target.get(\"arguments\") as NodePath[])[0];\n if (!first) return;\n target = first;\n }\n\n if (!target.isArrowFunctionExpression() && !target.isFunctionExpression()) return;\n if (isAlreadyWrapped(target)) return;\n if (!returnsJsx(target as NodePath<BabelTypes.Function>)) return;\n\n if (processed.has(path.node)) return;\n processed.add(path.node);\n target.replaceWith(wrap(target.node as BabelTypes.Expression, id.name, path.node, path, state));\n },\n\n /** `export default function () { return <div /> }` */\n ExportDefaultDeclaration(path: NodePath<BabelTypes.ExportDefaultDeclaration>, state: State) {\n const declaration = path.get(\"declaration\");\n if (!declaration.isFunctionDeclaration() || declaration.node.id) return; // named ones are handled above\n if (!returnsJsx(declaration as NodePath<BabelTypes.Function>)) return;\n\n const name = componentNameFromFile(state.rrdRelativePath ?? \"Default\");\n const expression = t.functionExpression(\n null,\n declaration.node.params as BabelTypes.Identifier[],\n declaration.node.body,\n declaration.node.generator,\n declaration.node.async,\n );\n if (processed.has(path.node)) return;\n processed.add(path.node);\n path.replaceWith(t.exportDefaultDeclaration(wrap(expression, name, declaration.node, path, state)));\n },\n },\n };\n}\n\nfunction matches(filename: string, patterns: Array<string | RegExp> | undefined, whenEmpty: boolean): boolean {\n if (!patterns || patterns.length === 0) return whenEmpty;\n return patterns.some((p) => (typeof p === \"string\" ? filename.includes(p) : p.test(filename)));\n}\n\nfunction hasUseClient(program: BabelTypes.Program): boolean {\n return program.directives.some((d) => d.value.value === \"use client\");\n}\n\n/** `src/components/UserCard.tsx` → `UserCard` */\nfunction componentNameFromFile(path: string): string {\n const base = path.split(/[/\\\\]/).pop() ?? \"Default\";\n const stem = base.replace(/\\.[jt]sx?$/, \"\");\n const cleaned = stem === \"index\" ? (path.split(/[/\\\\]/).slice(-2)[0] ?? \"Default\") : stem;\n const pascal = cleaned.replace(/(^|[-_.])(\\w)/g, (_, __, c: string) => c.toUpperCase());\n return /^[A-Z]/.test(pascal) ? pascal : `Component${pascal}`;\n}\n"]}
|
|
@@ -6,6 +6,7 @@ var SELF_PATTERN = /[/\\]react-render-detective[/\\](src|dist)[/\\]/;
|
|
|
6
6
|
var HOC_NAMES = /* @__PURE__ */ new Set(["memo", "forwardRef"]);
|
|
7
7
|
function renderDetectiveBabelPlugin(api) {
|
|
8
8
|
const t = api.types;
|
|
9
|
+
const processed = /* @__PURE__ */ new WeakSet();
|
|
9
10
|
api.assertVersion?.("^7.0.0-0 || ^8.0.0-0");
|
|
10
11
|
const isComponentName = (name) => !!name && /^[A-Z]/.test(name);
|
|
11
12
|
function returnsJsx(path) {
|
|
@@ -59,6 +60,9 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
59
60
|
];
|
|
60
61
|
const source = locationOf(node, state);
|
|
61
62
|
if (source) properties.push(t.objectProperty(t.identifier("source"), t.stringLiteral(source)));
|
|
63
|
+
if (path.getFunctionParent()) {
|
|
64
|
+
properties.push(t.objectProperty(t.identifier("declaredInRender"), t.booleanLiteral(true)));
|
|
65
|
+
}
|
|
62
66
|
state.rrdTouched = true;
|
|
63
67
|
return t.callExpression(ensureImport(path, state), [fn, t.objectExpression(properties)]);
|
|
64
68
|
}
|
|
@@ -92,6 +96,8 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
92
96
|
const id = path.node.id;
|
|
93
97
|
if (!id || !isComponentName(id.name)) return;
|
|
94
98
|
if (!returnsJsx(path)) return;
|
|
99
|
+
if (processed.has(path.node)) return;
|
|
100
|
+
processed.add(path.node);
|
|
95
101
|
const wrapped = wrap(t.cloneNode(id), id.name, path.node, path, state);
|
|
96
102
|
const assignment = t.expressionStatement(t.assignmentExpression("=", t.cloneNode(id), wrapped));
|
|
97
103
|
assignment._rrdGenerated = true;
|
|
@@ -101,7 +107,6 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
101
107
|
} else {
|
|
102
108
|
path.insertAfter(assignment);
|
|
103
109
|
}
|
|
104
|
-
path.skip();
|
|
105
110
|
},
|
|
106
111
|
/** `const Foo = () => <div />` and `const Foo = memo(() => <div />)` */
|
|
107
112
|
VariableDeclarator(path, state) {
|
|
@@ -120,8 +125,9 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
120
125
|
if (!target.isArrowFunctionExpression() && !target.isFunctionExpression()) return;
|
|
121
126
|
if (isAlreadyWrapped(target)) return;
|
|
122
127
|
if (!returnsJsx(target)) return;
|
|
128
|
+
if (processed.has(path.node)) return;
|
|
129
|
+
processed.add(path.node);
|
|
123
130
|
target.replaceWith(wrap(target.node, id.name, path.node, path, state));
|
|
124
|
-
path.skip();
|
|
125
131
|
},
|
|
126
132
|
/** `export default function () { return <div /> }` */
|
|
127
133
|
ExportDefaultDeclaration(path, state) {
|
|
@@ -136,8 +142,9 @@ function renderDetectiveBabelPlugin(api) {
|
|
|
136
142
|
declaration.node.generator,
|
|
137
143
|
declaration.node.async
|
|
138
144
|
);
|
|
145
|
+
if (processed.has(path.node)) return;
|
|
146
|
+
processed.add(path.node);
|
|
139
147
|
path.replaceWith(t.exportDefaultDeclaration(wrap(expression, name, declaration.node, path, state)));
|
|
140
|
-
path.skip();
|
|
141
148
|
}
|
|
142
149
|
}
|
|
143
150
|
};
|
|
@@ -158,5 +165,5 @@ function componentNameFromFile(path) {
|
|
|
158
165
|
}
|
|
159
166
|
|
|
160
167
|
exports.renderDetectiveBabelPlugin = renderDetectiveBabelPlugin;
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
168
|
+
//# sourceMappingURL=chunk-PHYYA67T.cjs.map
|
|
169
|
+
//# sourceMappingURL=chunk-PHYYA67T.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/babel/index.ts"],"names":[],"mappings":"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","file":"chunk-PHYYA67T.cjs","sourcesContent":["/**\n * Babel plugin: instrument every React component in a file at build time.\n *\n * Why this exists: without it you only learn about components you already\n * suspected, because you have to wrap them by hand. With it, the tool reports\n * on the whole tree — and it can attach a real source location, which React\n * itself no longer exposes at runtime (`_debugSource` was removed in React 19).\n *\n * Build-time only. It must never run in a production build, and it removes\n * itself when `NODE_ENV` is production unless explicitly forced.\n */\nimport type { NodePath, PluginObject, PluginPass, types as BabelTypes } from \"@babel/core\";\n\nexport interface RenderDetectivePluginOptions {\n /** Defaults to `NODE_ENV !== \"production\"`. */\n enabled?: boolean;\n /** Only instrument files whose path matches. Empty = all files. */\n include?: Array<string | RegExp>;\n /** Skip files whose path matches. Applied after `include`. */\n exclude?: Array<string | RegExp>;\n /**\n * Skip files with no `\"use client\"` directive. Turn this on for the Next.js\n * app router: server components never render on the client, and wrapping one\n * in a hook-using HOC would break the build.\n */\n clientOnly?: boolean;\n /** Import specifier for the runtime. Overridable for testing and monorepos. */\n importSource?: string;\n /** Root used to make source locations relative. Defaults to `process.cwd()`. */\n root?: string;\n}\n\ninterface State extends PluginPass {\n opts: RenderDetectivePluginOptions;\n rrdLocal?: BabelTypes.Identifier;\n rrdTouched?: boolean;\n rrdRelativePath?: string;\n}\n\nconst DEFAULT_IMPORT_SOURCE = \"react-render-detective\";\n\n/*\n * Never instrument the detective's own runtime. Its HOC returns a component\n * that renders the component it was given; instrumenting that makes it render\n * itself, and the app dies with a stack overflow before the first paint.\n *\n * node_modules covers the normal install. This covers a source alias — how the\n * bundled example consumes the package, and how anyone working on it locally\n * would too.\n */\nconst SELF_PATTERN = /[/\\\\]react-render-detective[/\\\\](src|dist)[/\\\\]/;\nconst HOC_NAMES = new Set([\"memo\", \"forwardRef\"]);\n\nexport default function renderDetectiveBabelPlugin(\n api: { types: typeof BabelTypes; assertVersion?: (v: number | string) => void },\n): PluginObject {\n const t = api.types;\n /*\n * Guards against re-processing a node we just rewrote. Using `path.skip()`\n * for that was a mistake: it skips the entire subtree, so components declared\n * *inside* another component were never instrumented — exactly the case the\n * remount diagnosis exists to catch.\n */\n const processed = new WeakSet<BabelTypes.Node>();\n // Babel 7 and 8 both work: only `types`, `NodePath` and the visitor shape are\n // used, all stable across the major.\n api.assertVersion?.(\"^7.0.0-0 || ^8.0.0-0\");\n\n /**\n * A component is an uppercase-named function that returns JSX.\n *\n * No all-caps exclusion: it was rejecting `const A = () => <i />`, a perfectly\n * ordinary component, to guard against constants that in practice are values\n * rather than functions returning JSX — and the JSX check already covers that.\n */\n const isComponentName = (name: string | undefined | null): boolean => !!name && /^[A-Z]/.test(name);\n\n function returnsJsx(path: NodePath<BabelTypes.Function>): boolean {\n let found = false;\n const body = path.get(\"body\");\n\n // Concise arrow body: `() => <div />` or `() => cond ? <a/> : <b/>`\n if (!Array.isArray(body) && !body.isBlockStatement()) {\n const node = (body as NodePath).node;\n return containsJsx(node);\n }\n\n path.traverse({\n Function(inner) {\n inner.skip(); // JSX in a nested closure belongs to that closure\n },\n ReturnStatement(ret) {\n if (ret.node.argument && containsJsx(ret.node.argument)) found = true;\n },\n });\n return found;\n }\n\n function containsJsx(node: BabelTypes.Node | null | undefined): boolean {\n if (!node) return false;\n if (t.isJSXElement(node) || t.isJSXFragment(node)) return true;\n if (t.isConditionalExpression(node)) return containsJsx(node.consequent) || containsJsx(node.alternate);\n if (t.isLogicalExpression(node)) return containsJsx(node.left) || containsJsx(node.right);\n if (t.isParenthesizedExpression(node)) return containsJsx(node.expression);\n if (t.isCallExpression(node)) {\n // `memo(() => <div />)` and friends\n return node.arguments.some((a) => containsJsx(a as BabelTypes.Node));\n }\n return false;\n }\n\n function ensureImport(path: NodePath, state: State): BabelTypes.Identifier {\n if (state.rrdLocal) return state.rrdLocal;\n const program = path.findParent((p) => p.isProgram()) as NodePath<BabelTypes.Program>;\n const local = program.scope.generateUidIdentifier(\"rrdTrack\");\n const source = state.opts.importSource ?? DEFAULT_IMPORT_SOURCE;\n program.unshiftContainer(\n \"body\",\n t.importDeclaration([t.importSpecifier(local, t.identifier(\"withRenderDetective\"))], t.stringLiteral(source)),\n );\n state.rrdLocal = local;\n return local;\n }\n\n function locationOf(node: BabelTypes.Node, state: State): string | undefined {\n const line = node.loc?.start.line;\n if (line === undefined || !state.rrdRelativePath) return undefined;\n return `${state.rrdRelativePath}:${line}:${(node.loc?.start.column ?? 0) + 1}`;\n }\n\n /** `__rrdTrack(fn, { name, source })` */\n function wrap(\n fn: BabelTypes.Expression,\n name: string,\n node: BabelTypes.Node,\n path: NodePath,\n state: State,\n ): BabelTypes.CallExpression {\n const properties: BabelTypes.ObjectProperty[] = [\n t.objectProperty(t.identifier(\"name\"), t.stringLiteral(name)),\n ];\n const source = locationOf(node, state);\n if (source) properties.push(t.objectProperty(t.identifier(\"source\"), t.stringLiteral(source)));\n\n /*\n * Whether a component is declared inside another function is a *static*\n * fact, and the compiler is standing right here. The runtime previously\n * guessed at it by timing repeated definitions, which failed the moment a\n * user clicked more than five seconds apart.\n */\n if (path.getFunctionParent()) {\n properties.push(t.objectProperty(t.identifier(\"declaredInRender\"), t.booleanLiteral(true)));\n }\n state.rrdTouched = true;\n return t.callExpression(ensureImport(path, state), [fn, t.objectExpression(properties)]);\n }\n\n /** Already wrapped, by hand or by a previous pass. */\n function isAlreadyWrapped(path: NodePath): boolean {\n const parent = path.parentPath;\n if (!parent?.isCallExpression()) return false;\n const callee = parent.node.callee;\n if (t.isIdentifier(callee) && /rrdTrack|withRenderDetective/.test(callee.name)) return true;\n if (t.isMemberExpression(callee) && t.isIdentifier(callee.property, { name: \"withRenderDetective\" })) return true;\n return false;\n }\n\n return {\n name: \"react-render-detective\",\n\n pre(this: State, file: { opts: { filename?: string | null; root?: string | null } }) {\n const state = this;\n const filename = file.opts.filename ?? \"\";\n const root = state.opts.root ?? file.opts.root ?? process.cwd();\n state.rrdRelativePath = filename.startsWith(root) ? filename.slice(root.length).replace(/^[/\\\\]/, \"\") : filename;\n },\n\n visitor: {\n Program: {\n enter(path: NodePath<BabelTypes.Program>, state: State) {\n const enabled = state.opts.enabled ?? process.env.NODE_ENV !== \"production\";\n const filename = state.file.opts.filename ?? \"\";\n\n const skip =\n !enabled ||\n !filename ||\n /node_modules/.test(filename) ||\n SELF_PATTERN.test(filename) ||\n !matches(filename, state.opts.include, true) ||\n matches(filename, state.opts.exclude, false) ||\n (state.opts.clientOnly === true && !hasUseClient(path.node));\n\n if (skip) path.skip();\n },\n },\n\n /** `function Foo() { return <div /> }` */\n FunctionDeclaration(path: NodePath<BabelTypes.FunctionDeclaration>, state: State) {\n const id = path.node.id;\n if (!id || !isComponentName(id.name)) return;\n if (!returnsJsx(path as NodePath<BabelTypes.Function>)) return;\n\n /*\n * Reassign rather than rewrite the declaration: function declarations\n * hoist, and components are routinely used above their definition. A\n * `const` would turn that into a temporal dead zone error at runtime.\n * ESM export bindings are live, so `export function Foo` still exports\n * the wrapper.\n */\n if (processed.has(path.node)) return;\n processed.add(path.node);\n\n const wrapped = wrap(t.cloneNode(id), id.name, path.node, path, state);\n const assignment = t.expressionStatement(t.assignmentExpression(\"=\", t.cloneNode(id), wrapped));\n (assignment as { _rrdGenerated?: boolean })._rrdGenerated = true;\n\n const statementParent = path.getStatementParent();\n if (path.parentPath.isExportDefaultDeclaration() || path.parentPath.isExportNamedDeclaration()) {\n statementParent?.insertAfter(assignment);\n } else {\n path.insertAfter(assignment);\n }\n },\n\n /** `const Foo = () => <div />` and `const Foo = memo(() => <div />)` */\n VariableDeclarator(path: NodePath<BabelTypes.VariableDeclarator>, state: State) {\n const id = path.node.id;\n const init = path.node.init;\n if (!t.isIdentifier(id) || !init || !isComponentName(id.name)) return;\n\n // Unwrap one level of memo()/forwardRef() so the instrumentation sits\n // inside them: memo compares props before our wrapper ever renders.\n let target: NodePath = path.get(\"init\") as NodePath;\n if (target.isCallExpression()) {\n const callee = target.node.callee;\n const isHoc =\n (t.isIdentifier(callee) && HOC_NAMES.has(callee.name)) ||\n (t.isMemberExpression(callee) && t.isIdentifier(callee.property) && HOC_NAMES.has(callee.property.name));\n if (!isHoc) return;\n const first = (target.get(\"arguments\") as NodePath[])[0];\n if (!first) return;\n target = first;\n }\n\n if (!target.isArrowFunctionExpression() && !target.isFunctionExpression()) return;\n if (isAlreadyWrapped(target)) return;\n if (!returnsJsx(target as NodePath<BabelTypes.Function>)) return;\n\n if (processed.has(path.node)) return;\n processed.add(path.node);\n target.replaceWith(wrap(target.node as BabelTypes.Expression, id.name, path.node, path, state));\n },\n\n /** `export default function () { return <div /> }` */\n ExportDefaultDeclaration(path: NodePath<BabelTypes.ExportDefaultDeclaration>, state: State) {\n const declaration = path.get(\"declaration\");\n if (!declaration.isFunctionDeclaration() || declaration.node.id) return; // named ones are handled above\n if (!returnsJsx(declaration as NodePath<BabelTypes.Function>)) return;\n\n const name = componentNameFromFile(state.rrdRelativePath ?? \"Default\");\n const expression = t.functionExpression(\n null,\n declaration.node.params as BabelTypes.Identifier[],\n declaration.node.body,\n declaration.node.generator,\n declaration.node.async,\n );\n if (processed.has(path.node)) return;\n processed.add(path.node);\n path.replaceWith(t.exportDefaultDeclaration(wrap(expression, name, declaration.node, path, state)));\n },\n },\n };\n}\n\nfunction matches(filename: string, patterns: Array<string | RegExp> | undefined, whenEmpty: boolean): boolean {\n if (!patterns || patterns.length === 0) return whenEmpty;\n return patterns.some((p) => (typeof p === \"string\" ? filename.includes(p) : p.test(filename)));\n}\n\nfunction hasUseClient(program: BabelTypes.Program): boolean {\n return program.directives.some((d) => d.value.value === \"use client\");\n}\n\n/** `src/components/UserCard.tsx` → `UserCard` */\nfunction componentNameFromFile(path: string): string {\n const base = path.split(/[/\\\\]/).pop() ?? \"Default\";\n const stem = base.replace(/\\.[jt]sx?$/, \"\");\n const cleaned = stem === \"index\" ? (path.split(/[/\\\\]/).slice(-2)[0] ?? \"Default\") : stem;\n const pascal = cleaned.replace(/(^|[-_.])(\\w)/g, (_, __, c: string) => c.toUpperCase());\n return /^[A-Z]/.test(pascal) ? pascal : `Component${pascal}`;\n}\n"]}
|
|
@@ -324,6 +324,21 @@ function diagnose(input, thresholds) {
|
|
|
324
324
|
function classify(input) {
|
|
325
325
|
const { changedProps, contextChanges, parentRendered, parentUnknown, parentName } = input;
|
|
326
326
|
if (input.phase === "mount") {
|
|
327
|
+
if (input.remounts >= 2) {
|
|
328
|
+
const inline = input.inlineDefinitionSuspected;
|
|
329
|
+
return make(
|
|
330
|
+
"mount",
|
|
331
|
+
inline ? "high" : "medium",
|
|
332
|
+
`${input.componentName} was rebuilt, not re-rendered \u2014 this is remount ${input.remounts + 1}.`,
|
|
333
|
+
[
|
|
334
|
+
`This component has been unmounted and mounted again ${input.remounts}\xD7 \u2014 its DOM and all of its state were discarded each time.`,
|
|
335
|
+
inline ? "Its definition is being re-created repeatedly in a short window, which means it is declared *inside* another component's render body. React sees a new component type on every parent render and cannot reuse anything." : "Its definition is stable, so the remounts come from the element identity changing \u2014 usually a changing `key`, or the component moving between branches of a conditional.",
|
|
336
|
+
"A remount is much more expensive than a re-render, and it resets state."
|
|
337
|
+
],
|
|
338
|
+
true,
|
|
339
|
+
inline ? `Move ${input.componentName} out of its parent's render body to module scope. Defining a component inside another component makes React discard and rebuild the whole subtree on every parent render.` : `Check the \`key\` given to ${input.componentName}, and whether it is being rendered from a different branch each time.`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
327
342
|
return make("mount", "high", `${input.componentName} mounted.`, ["First render of this instance."], false);
|
|
328
343
|
}
|
|
329
344
|
if (input.trackedState.length > 0) {
|
|
@@ -570,6 +585,8 @@ var RingBuffer = class {
|
|
|
570
585
|
|
|
571
586
|
// src/core/store.ts
|
|
572
587
|
var now = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
588
|
+
var INLINE_DEFINITION_WINDOW_MS = 5e3;
|
|
589
|
+
var timestamp = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
573
590
|
var EMPTY_STATE = [];
|
|
574
591
|
var DURATION_SAMPLE = 200;
|
|
575
592
|
var SWEEP_DELAY_MS = 250;
|
|
@@ -593,6 +610,10 @@ var Detective = class {
|
|
|
593
610
|
this.seq = 0;
|
|
594
611
|
this.flushScheduled = false;
|
|
595
612
|
this.nextId = 0;
|
|
613
|
+
/** Mount/unmount history per component name, for remount detection. */
|
|
614
|
+
this.lifecycles = /* @__PURE__ */ new Map();
|
|
615
|
+
/** How often each component *definition* has been wrapped. */
|
|
616
|
+
this.definitions = /* @__PURE__ */ new Map();
|
|
596
617
|
/** Nodes created by `useRenderDiagnostics`, where props/state cannot be separated. */
|
|
597
618
|
this.hookModeNodes = /* @__PURE__ */ new WeakSet();
|
|
598
619
|
this.initialized = false;
|
|
@@ -666,9 +687,66 @@ var Detective = class {
|
|
|
666
687
|
}
|
|
667
688
|
attach(node) {
|
|
668
689
|
this.nodes.set(node.id, node);
|
|
690
|
+
if (node.detached) {
|
|
691
|
+
node.detached = false;
|
|
692
|
+
const existing = this.lifecycles.get(node.name);
|
|
693
|
+
if (existing) existing.unmounts--;
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (node.everAttached) return;
|
|
697
|
+
node.everAttached = true;
|
|
698
|
+
const life = this.lifecycleFor(node.name);
|
|
699
|
+
life.mounts++;
|
|
700
|
+
if (life.unmounts > 0) life.remounts++;
|
|
669
701
|
}
|
|
670
702
|
detach(node) {
|
|
671
703
|
this.nodes.delete(node.id);
|
|
704
|
+
if (node.everAttached && !node.detached) {
|
|
705
|
+
node.detached = true;
|
|
706
|
+
this.lifecycleFor(node.name).unmounts++;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
lifecycleFor(name) {
|
|
710
|
+
let life = this.lifecycles.get(name);
|
|
711
|
+
if (!life) this.lifecycles.set(name, life = { mounts: 0, unmounts: 0, remounts: 0 });
|
|
712
|
+
return life;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Called once per `withRenderDetective` call. A component declared at module
|
|
716
|
+
* scope is wrapped once; one declared *inside* a render body is wrapped again
|
|
717
|
+
* on every render of its parent — which also forces React to throw away and
|
|
718
|
+
* rebuild its entire subtree. That is the signal.
|
|
719
|
+
*/
|
|
720
|
+
noteDefinition(name, source, declaredInRender = false) {
|
|
721
|
+
const key = source ?? name;
|
|
722
|
+
const now2 = timestamp();
|
|
723
|
+
const record = this.definitions.get(key);
|
|
724
|
+
if (!record) {
|
|
725
|
+
this.definitions.set(key, { name, count: 1, firstSeen: now2, lastSeen: now2, declaredInRender });
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
record.count++;
|
|
729
|
+
record.lastSeen = now2;
|
|
730
|
+
if (declaredInRender) record.declaredInRender = true;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Is this component declared inside another component's render body?
|
|
734
|
+
*
|
|
735
|
+
* The build plugin answers this statically and exactly. Without it we fall
|
|
736
|
+
* back to a rate heuristic — repeated definitions of the same component in a
|
|
737
|
+
* short window — which is weaker: hot reload also re-defines components, and
|
|
738
|
+
* a user clicking slowly stretches the window. Hence the plugin's answer wins.
|
|
739
|
+
*/
|
|
740
|
+
inlineDefinitionSuspected(name) {
|
|
741
|
+
for (const record of this.definitions.values()) {
|
|
742
|
+
if (record.name !== name) continue;
|
|
743
|
+
if (record.declaredInRender) return true;
|
|
744
|
+
if (record.count >= 3 && record.lastSeen - record.firstSeen < INLINE_DEFINITION_WINDOW_MS) return true;
|
|
745
|
+
}
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
lifecycleOf(name) {
|
|
749
|
+
return this.lifecycles.get(name) ?? { mounts: 0, unmounts: 0, remounts: 0 };
|
|
672
750
|
}
|
|
673
751
|
/** Hot path. Must stay allocation-free and O(1). */
|
|
674
752
|
recordAttempt(node, props) {
|
|
@@ -825,6 +903,8 @@ var Detective = class {
|
|
|
825
903
|
attempts: rec.attempts,
|
|
826
904
|
committed: true,
|
|
827
905
|
trackedState,
|
|
906
|
+
remounts: this.lifecycleOf(node.name).remounts,
|
|
907
|
+
inlineDefinitionSuspected: this.inlineDefinitionSuspected(node.name),
|
|
828
908
|
priorAvoidableRenders: node.stats.potentiallyAvoidableRenders
|
|
829
909
|
},
|
|
830
910
|
this.config.thresholds
|
|
@@ -906,7 +986,7 @@ var Detective = class {
|
|
|
906
986
|
for (const node of this.nodes.values()) {
|
|
907
987
|
if (name && node.name !== name) continue;
|
|
908
988
|
if (node.stats.renderCount === 0 && node.stats.uncommittedAttempts === 0) continue;
|
|
909
|
-
out.push(toStats(node));
|
|
989
|
+
out.push(toStats(node, this.lifecycleOf(node.name)));
|
|
910
990
|
}
|
|
911
991
|
return out;
|
|
912
992
|
}
|
|
@@ -954,6 +1034,8 @@ var Detective = class {
|
|
|
954
1034
|
reset() {
|
|
955
1035
|
this.clear();
|
|
956
1036
|
this.nodes.clear();
|
|
1037
|
+
this.lifecycles.clear();
|
|
1038
|
+
this.definitions.clear();
|
|
957
1039
|
this.listeners.clear();
|
|
958
1040
|
if (this.sweepHandle !== void 0) clearTimeout(this.sweepHandle);
|
|
959
1041
|
this.sweepHandle = void 0;
|
|
@@ -967,7 +1049,7 @@ function percentile(sorted, p) {
|
|
|
967
1049
|
const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p / 100 * sorted.length) - 1));
|
|
968
1050
|
return sorted[idx];
|
|
969
1051
|
}
|
|
970
|
-
function toStats(node) {
|
|
1052
|
+
function toStats(node, lifecycle) {
|
|
971
1053
|
const sorted = [...node.durations].sort((a, b) => a - b);
|
|
972
1054
|
const s = node.stats;
|
|
973
1055
|
return {
|
|
@@ -976,6 +1058,7 @@ function toStats(node) {
|
|
|
976
1058
|
source: node.source,
|
|
977
1059
|
renderCount: s.renderCount,
|
|
978
1060
|
mountCount: s.mountCount,
|
|
1061
|
+
remountCount: lifecycle.remounts,
|
|
979
1062
|
uncommittedAttempts: s.uncommittedAttempts,
|
|
980
1063
|
devReplays: s.devReplays,
|
|
981
1064
|
totalSelfDuration: s.totalSelfDuration,
|
|
@@ -1000,6 +1083,7 @@ function mergeStats(a, b) {
|
|
|
1000
1083
|
source: a.source ?? b.source,
|
|
1001
1084
|
renderCount,
|
|
1002
1085
|
mountCount: a.mountCount + b.mountCount,
|
|
1086
|
+
remountCount: Math.max(a.remountCount, b.remountCount),
|
|
1003
1087
|
uncommittedAttempts: a.uncommittedAttempts + b.uncommittedAttempts,
|
|
1004
1088
|
devReplays: a.devReplays + b.devReplays,
|
|
1005
1089
|
totalSelfDuration: total,
|
|
@@ -1021,5 +1105,5 @@ function getDetective() {
|
|
|
1021
1105
|
}
|
|
1022
1106
|
|
|
1023
1107
|
export { Detective, RingBuffer, defaultConfig, detectDev, diagnose, diffProps, formatInspected, getDetective, inspect, isPlainObject, isReactElement, matches, mergeConfig, severityFor, shallowEqual, shouldInstrument, valueType };
|
|
1024
|
-
//# sourceMappingURL=chunk-
|
|
1025
|
-
//# sourceMappingURL=chunk-
|
|
1108
|
+
//# sourceMappingURL=chunk-QOGTEVBP.js.map
|
|
1109
|
+
//# sourceMappingURL=chunk-QOGTEVBP.js.map
|