agent-react-devtools 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +9 -0
- package/dist/connect.d.ts +11 -1
- package/dist/connect.js +10 -7
- package/dist/connect.js.map +1 -1
- package/dist/vite.js +11 -0
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/skills/react-devtools/SKILL.md +10 -0
- package/skills/react-devtools/references/setup.md +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# agent-react-devtools
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 370ef23: fix: prevent race condition where React loads before devtools hook is installed
|
|
8
|
+
|
|
9
|
+
The connect module's dynamic `import('react-devtools-core')` yielded control before `initialize()` could install the hook, allowing react-dom to load first and miss the connection. Added top-level `await` to block dependent modules until the hook is ready, and updated the Vite plugin to enable `top-level-await` in esbuild's dep optimizer.
|
|
10
|
+
|
|
3
11
|
## 0.2.1
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -198,6 +198,15 @@ For Expo, the connection works automatically with the Expo dev client.
|
|
|
198
198
|
|
|
199
199
|
To use a custom port, set the `REACT_DEVTOOLS_PORT` environment variable.
|
|
200
200
|
|
|
201
|
+
## Using with agent-browser
|
|
202
|
+
|
|
203
|
+
When using `agent-browser` to drive the app (e.g. for profiling interactions), you **must use headed mode**. Headless Chromium does not properly execute the devtools connect script:
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
agent-browser --session devtools --headed open http://localhost:5173/
|
|
207
|
+
agent-react-devtools status # Should show "Apps: 1 connected"
|
|
208
|
+
```
|
|
209
|
+
|
|
201
210
|
## Using with AI Coding Assistants
|
|
202
211
|
|
|
203
212
|
Add the skill to your AI coding assistant for richer context:
|
package/dist/connect.d.ts
CHANGED
|
@@ -5,9 +5,19 @@
|
|
|
5
5
|
*
|
|
6
6
|
* This must be imported before React loads. It:
|
|
7
7
|
* 1. Removes the Vite plugin-react hook stub
|
|
8
|
-
* 2. Initializes react-devtools-core
|
|
8
|
+
* 2. Initializes react-devtools-core (installs the real __REACT_DEVTOOLS_GLOBAL_HOOK__)
|
|
9
9
|
* 3. Connects via WebSocket to the agent-react-devtools daemon
|
|
10
10
|
*
|
|
11
|
+
* Steps 1–2 run synchronously at module evaluation time via a static import
|
|
12
|
+
* of react-devtools-core. This is critical — a dynamic import would yield
|
|
13
|
+
* control and let React load before the hook is installed. The static import
|
|
14
|
+
* also ensures esbuild's CJS-to-ESM interop provides proper named exports
|
|
15
|
+
* (dynamic imports to CJS chunks only expose a default export).
|
|
16
|
+
*
|
|
17
|
+
* react-devtools-core is a required peer dependency. If not installed, this
|
|
18
|
+
* module will fail to load — which is the correct behavior since there's
|
|
19
|
+
* nothing useful it can do without it.
|
|
20
|
+
*
|
|
11
21
|
* Export `ready` — a promise that resolves once the WebSocket opens
|
|
12
22
|
* (or after a 2s timeout / error, so the app is never blocked).
|
|
13
23
|
*/
|
package/dist/connect.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/connect.ts
|
|
2
|
+
import { initialize, connectToDevTools } from "react-devtools-core";
|
|
2
3
|
function getMeta(name) {
|
|
3
4
|
if (typeof document === "undefined") return null;
|
|
4
5
|
const meta = document.querySelector(`meta[name="${name}"]`);
|
|
@@ -16,17 +17,18 @@ function noop() {
|
|
|
16
17
|
}
|
|
17
18
|
var isSSR = typeof window === "undefined";
|
|
18
19
|
var isProd = typeof import.meta !== "undefined" && import.meta.env?.PROD === true || typeof process !== "undefined" && false;
|
|
20
|
+
if (!isSSR && !isProd) {
|
|
21
|
+
try {
|
|
22
|
+
delete window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
initialize();
|
|
26
|
+
}
|
|
19
27
|
var ready = isSSR || isProd ? noop() : connect();
|
|
20
|
-
|
|
28
|
+
function connect() {
|
|
21
29
|
try {
|
|
22
30
|
const port = getPort();
|
|
23
31
|
const host = getHost();
|
|
24
|
-
try {
|
|
25
|
-
delete window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
26
|
-
} catch {
|
|
27
|
-
}
|
|
28
|
-
const { initialize, connectToDevTools } = await import("react-devtools-core");
|
|
29
|
-
initialize();
|
|
30
32
|
return new Promise((resolve) => {
|
|
31
33
|
try {
|
|
32
34
|
const ws = new WebSocket(`ws://${host}:${port}`);
|
|
@@ -39,6 +41,7 @@ async function connect() {
|
|
|
39
41
|
}
|
|
40
42
|
});
|
|
41
43
|
} catch {
|
|
44
|
+
return Promise.resolve();
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
export {
|
package/dist/connect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/connect.ts"],"sourcesContent":["/**\n * Browser-side connection module for agent-react-devtools.\n *\n * Usage: `import 'agent-react-devtools/connect'`\n *\n * This must be imported before React loads. It:\n * 1. Removes the Vite plugin-react hook stub\n * 2. Initializes react-devtools-core\n * 3. Connects via WebSocket to the agent-react-devtools daemon\n *\n * Export `ready` — a promise that resolves once the WebSocket opens\n * (or after a 2s timeout / error, so the app is never blocked).\n */\n\nfunction getMeta(name: string): string | null {\n if (typeof document === 'undefined') return null;\n const meta = document.querySelector(`meta[name=\"${name}\"]`);\n return meta?.getAttribute('content') || null;\n}\n\nfunction getPort(): number {\n const val = parseInt(getMeta('agent-react-devtools-port') || '', 10);\n return isNaN(val) ? 8097 : val;\n}\n\nfunction getHost(): string {\n return getMeta('agent-react-devtools-host') || 'localhost';\n}\n\nfunction noop(): Promise<void> {\n return Promise.resolve();\n}\n\n// SSR guard\nconst isSSR = typeof window === 'undefined';\n\n// Production guard — check bundler-injected signals first, then Node.js process.env\nconst isProd =\n (typeof import.meta !== 'undefined' &&\n (import.meta as any).env?.PROD === true) ||\n (typeof process !== 'undefined' &&\n process.env?.NODE_ENV === 'production');\n\
|
|
1
|
+
{"version":3,"sources":["../src/connect.ts"],"sourcesContent":["/**\n * Browser-side connection module for agent-react-devtools.\n *\n * Usage: `import 'agent-react-devtools/connect'`\n *\n * This must be imported before React loads. It:\n * 1. Removes the Vite plugin-react hook stub\n * 2. Initializes react-devtools-core (installs the real __REACT_DEVTOOLS_GLOBAL_HOOK__)\n * 3. Connects via WebSocket to the agent-react-devtools daemon\n *\n * Steps 1–2 run synchronously at module evaluation time via a static import\n * of react-devtools-core. This is critical — a dynamic import would yield\n * control and let React load before the hook is installed. The static import\n * also ensures esbuild's CJS-to-ESM interop provides proper named exports\n * (dynamic imports to CJS chunks only expose a default export).\n *\n * react-devtools-core is a required peer dependency. If not installed, this\n * module will fail to load — which is the correct behavior since there's\n * nothing useful it can do without it.\n *\n * Export `ready` — a promise that resolves once the WebSocket opens\n * (or after a 2s timeout / error, so the app is never blocked).\n */\n\nimport { initialize, connectToDevTools } from 'react-devtools-core';\n\nfunction getMeta(name: string): string | null {\n if (typeof document === 'undefined') return null;\n const meta = document.querySelector(`meta[name=\"${name}\"]`);\n return meta?.getAttribute('content') || null;\n}\n\nfunction getPort(): number {\n const val = parseInt(getMeta('agent-react-devtools-port') || '', 10);\n return isNaN(val) ? 8097 : val;\n}\n\nfunction getHost(): string {\n return getMeta('agent-react-devtools-host') || 'localhost';\n}\n\nfunction noop(): Promise<void> {\n return Promise.resolve();\n}\n\n// SSR guard\nconst isSSR = typeof window === 'undefined';\n\n// Production guard — check bundler-injected signals first, then Node.js process.env\nconst isProd =\n (typeof import.meta !== 'undefined' &&\n (import.meta as any).env?.PROD === true) ||\n (typeof process !== 'undefined' &&\n process.env?.NODE_ENV === 'production');\n\n// Install the devtools hook synchronously before React loads.\n// This MUST happen at module evaluation time — if deferred to an async\n// callback, react-dom may initialize first and miss the hook entirely.\nif (!isSSR && !isProd) {\n // Remove Vite's plugin-react hook stub so react-devtools-core can install the full hook\n try {\n delete (window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__;\n } catch {\n // Property may be non-configurable (browser extension) — ignore\n }\n\n initialize();\n}\n\nexport const ready: Promise<void> = isSSR || isProd ? noop() : connect();\n\nfunction connect(): Promise<void> {\n try {\n const port = getPort();\n const host = getHost();\n\n return new Promise<void>((resolve) => {\n try {\n const ws = new WebSocket(`ws://${host}:${port}`);\n connectToDevTools({ port, websocket: ws });\n ws.addEventListener('open', () => resolve());\n ws.addEventListener('error', () => resolve());\n setTimeout(resolve, 2000);\n } catch {\n resolve();\n }\n });\n } catch {\n return Promise.resolve();\n }\n}\n"],"mappings":";AAwBA,SAAS,YAAY,yBAAyB;AAE9C,SAAS,QAAQ,MAA6B;AAC5C,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,OAAO,SAAS,cAAc,cAAc,IAAI,IAAI;AAC1D,SAAO,MAAM,aAAa,SAAS,KAAK;AAC1C;AAEA,SAAS,UAAkB;AACzB,QAAM,MAAM,SAAS,QAAQ,2BAA2B,KAAK,IAAI,EAAE;AACnE,SAAO,MAAM,GAAG,IAAI,OAAO;AAC7B;AAEA,SAAS,UAAkB;AACzB,SAAO,QAAQ,2BAA2B,KAAK;AACjD;AAEA,SAAS,OAAsB;AAC7B,SAAO,QAAQ,QAAQ;AACzB;AAGA,IAAM,QAAQ,OAAO,WAAW;AAGhC,IAAM,SACH,OAAO,gBAAgB,eACrB,YAAoB,KAAK,SAAS,QACpC,OAAO,YAAY,eAClB;AAKJ,IAAI,CAAC,SAAS,CAAC,QAAQ;AAErB,MAAI;AACF,WAAQ,OAAe;AAAA,EACzB,QAAQ;AAAA,EAER;AAEA,aAAW;AACb;AAEO,IAAM,QAAuB,SAAS,SAAS,KAAK,IAAI,QAAQ;AAEvE,SAAS,UAAyB;AAChC,MAAI;AACF,UAAM,OAAO,QAAQ;AACrB,UAAM,OAAO,QAAQ;AAErB,WAAO,IAAI,QAAc,CAAC,YAAY;AACpC,UAAI;AACF,cAAM,KAAK,IAAI,UAAU,QAAQ,IAAI,IAAI,IAAI,EAAE;AAC/C,0BAAkB,EAAE,MAAM,WAAW,GAAG,CAAC;AACzC,WAAG,iBAAiB,QAAQ,MAAM,QAAQ,CAAC;AAC3C,WAAG,iBAAiB,SAAS,MAAM,QAAQ,CAAC;AAC5C,mBAAW,SAAS,GAAI;AAAA,MAC1B,QAAQ;AACN,gBAAQ;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AACN,WAAO,QAAQ,QAAQ;AAAA,EACzB;AACF;","names":[]}
|
package/dist/vite.js
CHANGED
|
@@ -5,6 +5,17 @@ function reactDevtools(options) {
|
|
|
5
5
|
return {
|
|
6
6
|
name: "agent-react-devtools",
|
|
7
7
|
apply: "serve",
|
|
8
|
+
config() {
|
|
9
|
+
return {
|
|
10
|
+
optimizeDeps: {
|
|
11
|
+
esbuildOptions: {
|
|
12
|
+
supported: {
|
|
13
|
+
"top-level-await": true
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
},
|
|
8
19
|
transformIndexHtml: {
|
|
9
20
|
order: "pre",
|
|
10
21
|
handler() {
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin.ts"],"sourcesContent":["import type { Plugin, HtmlTagDescriptor } from 'vite';\n\nexport interface ReactDevtoolsOptions {\n /** WebSocket port the daemon listens on. Default: 8097 */\n port?: number;\n /** WebSocket host. Default: 'localhost' */\n host?: string;\n}\n\nexport function reactDevtools(options?: ReactDevtoolsOptions): Plugin {\n const port = options?.port ?? 8097;\n const host = options?.host ?? 'localhost';\n\n return {\n name: 'agent-react-devtools',\n apply: 'serve',\n transformIndexHtml: {\n order: 'pre',\n handler() {\n const tags: HtmlTagDescriptor[] = [];\n\n if (host !== 'localhost') {\n tags.push({\n tag: 'meta',\n attrs: { name: 'agent-react-devtools-host', content: host },\n injectTo: 'head-prepend',\n });\n }\n\n if (port !== 8097) {\n tags.push({\n tag: 'meta',\n attrs: { name: 'agent-react-devtools-port', content: String(port) },\n injectTo: 'head-prepend',\n });\n }\n\n tags.push({\n tag: 'script',\n attrs: { type: 'module' },\n children: `import 'agent-react-devtools/connect';`,\n injectTo: 'head-prepend',\n });\n\n return tags;\n },\n },\n };\n}\n"],"mappings":";AASO,SAAS,cAAc,SAAwC;AACpE,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,OAAO,SAAS,QAAQ;AAE9B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,oBAAoB;AAAA,MAClB,OAAO;AAAA,MACP,UAAU;AACR,cAAM,OAA4B,CAAC;AAEnC,YAAI,SAAS,aAAa;AACxB,eAAK,KAAK;AAAA,YACR,KAAK;AAAA,YACL,OAAO,EAAE,MAAM,6BAA6B,SAAS,KAAK;AAAA,YAC1D,UAAU;AAAA,UACZ,CAAC;AAAA,QACH;AAEA,YAAI,SAAS,MAAM;AACjB,eAAK,KAAK;AAAA,YACR,KAAK;AAAA,YACL,OAAO,EAAE,MAAM,6BAA6B,SAAS,OAAO,IAAI,EAAE;AAAA,YAClE,UAAU;AAAA,UACZ,CAAC;AAAA,QACH;AAEA,aAAK,KAAK;AAAA,UACR,KAAK;AAAA,UACL,OAAO,EAAE,MAAM,SAAS;AAAA,UACxB,UAAU;AAAA,UACV,UAAU;AAAA,QACZ,CAAC;AAED,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin.ts"],"sourcesContent":["import type { Plugin, HtmlTagDescriptor } from 'vite';\n\nexport interface ReactDevtoolsOptions {\n /** WebSocket port the daemon listens on. Default: 8097 */\n port?: number;\n /** WebSocket host. Default: 'localhost' */\n host?: string;\n}\n\nexport function reactDevtools(options?: ReactDevtoolsOptions): Plugin {\n const port = options?.port ?? 8097;\n const host = options?.host ?? 'localhost';\n\n return {\n name: 'agent-react-devtools',\n apply: 'serve',\n config() {\n // The connect module uses top-level await to block React from loading\n // before the devtools hook is installed. Vite's dep optimizer uses\n // esbuild which defaults to es2020 (no TLA support), so we enable it.\n return {\n optimizeDeps: {\n esbuildOptions: {\n supported: {\n 'top-level-await': true,\n },\n },\n },\n };\n },\n transformIndexHtml: {\n order: 'pre',\n handler() {\n const tags: HtmlTagDescriptor[] = [];\n\n if (host !== 'localhost') {\n tags.push({\n tag: 'meta',\n attrs: { name: 'agent-react-devtools-host', content: host },\n injectTo: 'head-prepend',\n });\n }\n\n if (port !== 8097) {\n tags.push({\n tag: 'meta',\n attrs: { name: 'agent-react-devtools-port', content: String(port) },\n injectTo: 'head-prepend',\n });\n }\n\n tags.push({\n tag: 'script',\n attrs: { type: 'module' },\n children: `import 'agent-react-devtools/connect';`,\n injectTo: 'head-prepend',\n });\n\n return tags;\n },\n },\n };\n}\n"],"mappings":";AASO,SAAS,cAAc,SAAwC;AACpE,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,OAAO,SAAS,QAAQ;AAE9B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAIP,aAAO;AAAA,QACL,cAAc;AAAA,UACZ,gBAAgB;AAAA,YACd,WAAW;AAAA,cACT,mBAAmB;AAAA,YACrB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,oBAAoB;AAAA,MAClB,OAAO;AAAA,MACP,UAAU;AACR,cAAM,OAA4B,CAAC;AAEnC,YAAI,SAAS,aAAa;AACxB,eAAK,KAAK;AAAA,YACR,KAAK;AAAA,YACL,OAAO,EAAE,MAAM,6BAA6B,SAAS,KAAK;AAAA,YAC1D,UAAU;AAAA,UACZ,CAAC;AAAA,QACH;AAEA,YAAI,SAAS,MAAM;AACjB,eAAK,KAAK;AAAA,YACR,KAAK;AAAA,YACL,OAAO,EAAE,MAAM,6BAA6B,SAAS,OAAO,IAAI,EAAE;AAAA,YAClE,UAAU;AAAA,UACZ,CAAC;AAAA,QACH;AAEA,aAAK,KAAK;AAAA,UACR,KAAK;AAAA,UACL,OAAO,EAAE,MAAM,SAAS;AAAA,UACxB,UAAU;AAAA,UACV,UAAU;AAAA,QACZ,CAAC;AAED,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -124,10 +124,20 @@ agent-react-devtools profile slow --limit 5
|
|
|
124
124
|
# Compare render counts and durations to the previous run
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
## Using with agent-browser
|
|
128
|
+
|
|
129
|
+
When using `agent-browser` to drive the app while profiling or debugging, you **must use headed mode** (`--headed`). Headless Chromium does not execute ES module scripts the same way as a real browser, which prevents the devtools connect script from running properly.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
agent-browser --session devtools --headed open http://localhost:5173/
|
|
133
|
+
agent-react-devtools status # Should show 1 connected app
|
|
134
|
+
```
|
|
135
|
+
|
|
127
136
|
## Important Rules
|
|
128
137
|
|
|
129
138
|
- **Labels reset** when the app reloads or components unmount/remount. Always re-check with `get tree` or `find` after a page reload.
|
|
130
139
|
- **`status` first** — if status shows 0 connected apps, the React app is not connected. The user may need to run `npx agent-react-devtools init` in their project first.
|
|
140
|
+
- **Headed browser required** — if using `agent-browser`, always use `--headed` mode. Headless Chromium does not properly load the devtools connect script.
|
|
131
141
|
- **Profile while interacting** — profiling only captures renders that happen between `profile start` and `profile stop`. Make sure the relevant interaction happens during that window.
|
|
132
142
|
- **Use `--depth`** on large trees — a deep tree can produce a lot of output. Start with `--depth 3` or `--depth 4` and go deeper only on the subtree you care about.
|
|
133
143
|
|
|
@@ -98,3 +98,16 @@ If `Apps: 0 connected`:
|
|
|
98
98
|
1. Check the app is running in dev mode
|
|
99
99
|
2. Check the console for WebSocket connection errors
|
|
100
100
|
3. Ensure no other DevTools instance is using port 8097
|
|
101
|
+
4. If using `agent-browser`, make sure you're using **headed mode** (`--headed`) — headless Chromium does not properly execute the devtools connect script
|
|
102
|
+
|
|
103
|
+
## Using with agent-browser
|
|
104
|
+
|
|
105
|
+
When automating the browser with `agent-browser`, you must use headed mode. Headless Chromium handles ES module script execution differently, which prevents the connect script from installing the devtools hook before React loads.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Headed mode is required for devtools to connect
|
|
109
|
+
agent-browser --session devtools --headed open http://localhost:5173/
|
|
110
|
+
|
|
111
|
+
# Verify connection
|
|
112
|
+
agent-react-devtools status
|
|
113
|
+
```
|