@tanstack/devtools 0.10.13 → 0.11.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/bin/intent.js +20 -0
- package/dist/dev.js +1 -1
- package/dist/devtools/{W6LG6674.js → AKRRB3KC.js} +41 -8
- package/dist/devtools/{7Z2ESJHO.js → UZDPUP5E.js} +37 -7
- package/dist/index.d.ts +14 -6
- package/dist/index.js +1 -1
- package/dist/mount-impl/{EMNOPRXX.js → IGHTIX6Y.js} +1 -1
- package/dist/mount-impl/{Z6LKUI5N.js → YYPMJI4G.js} +1 -1
- package/dist/server.js +1 -1
- package/package.json +9 -4
- package/skills/devtools-app-setup/SKILL.md +359 -0
- package/skills/devtools-marketplace/SKILL.md +390 -0
- package/skills/devtools-plugin-panel/SKILL.md +429 -0
- package/skills/devtools-plugin-panel/references/panel-api.md +136 -0
- package/skills/devtools-production/SKILL.md +459 -0
- package/src/components/tab-content.tsx +9 -5
- package/src/context/devtools-context.tsx +7 -9
- package/src/context/devtools-store.ts +3 -2
- package/src/devtools.tsx +9 -1
- package/src/index.ts +2 -0
- package/src/styles/use-styles.ts +0 -1
- package/src/tabs/index.tsx +1 -1
- package/src/tabs/plugin-registry.ts +18 -0
- package/src/tabs/plugins-tab.tsx +9 -4
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devtools-production
|
|
3
|
+
description: >
|
|
4
|
+
Handle devtools in production vs development. removeDevtoolsOnBuild,
|
|
5
|
+
devDependency vs regular dependency, conditional imports, NoOp plugin
|
|
6
|
+
variants for tree-shaking, non-Vite production exclusion patterns.
|
|
7
|
+
type: lifecycle
|
|
8
|
+
library: '@tanstack/devtools'
|
|
9
|
+
library_version: '0.10.12'
|
|
10
|
+
requires: devtools-app-setup
|
|
11
|
+
sources:
|
|
12
|
+
- docs/production.md
|
|
13
|
+
- docs/vite-plugin.md
|
|
14
|
+
- packages/devtools-vite/src/plugin.ts
|
|
15
|
+
- packages/devtools-vite/src/remove-devtools.ts
|
|
16
|
+
- packages/devtools/package.json
|
|
17
|
+
- packages/devtools/tsup.config.ts
|
|
18
|
+
- packages/devtools-utils/src/react/plugin.tsx
|
|
19
|
+
- packages/devtools-utils/src/react/panel.tsx
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# TanStack Devtools Production Handling
|
|
23
|
+
|
|
24
|
+
> **Prerequisite:** Read the **devtools-app-setup** skill first. The initial setup decisions (framework adapter, Vite plugin, dependency type) directly determine which production strategy applies.
|
|
25
|
+
|
|
26
|
+
## How Production Stripping Works
|
|
27
|
+
|
|
28
|
+
TanStack Devtools has two independent mechanisms for keeping devtools out of production bundles. Understanding both is essential because they serve different project types.
|
|
29
|
+
|
|
30
|
+
### Mechanism 1: Vite Plugin Auto-Stripping (Vite projects)
|
|
31
|
+
|
|
32
|
+
The `@tanstack/devtools-vite` plugin includes a sub-plugin named `@tanstack/devtools:remove-devtools-on-build`. When `removeDevtoolsOnBuild` is `true` (the default), this plugin runs during `vite build` and any non-`serve` command where the mode is `production`.
|
|
33
|
+
|
|
34
|
+
It uses Babel to parse every source file, find imports from these packages, and remove them along with any JSX elements they produce:
|
|
35
|
+
|
|
36
|
+
- `@tanstack/react-devtools`
|
|
37
|
+
- `@tanstack/preact-devtools`
|
|
38
|
+
- `@tanstack/solid-devtools`
|
|
39
|
+
- `@tanstack/devtools`
|
|
40
|
+
|
|
41
|
+
The stripping is AST-based. It removes the import declaration, then finds and removes any JSX elements whose tag name matches one of the imported identifiers. It also traces plugin references inside the `plugins` prop array and removes their imports if they become unused.
|
|
42
|
+
|
|
43
|
+
Source: `packages/devtools-vite/src/remove-devtools.ts`
|
|
44
|
+
|
|
45
|
+
This means for a standard Vite project, the default setup from **devtools-app-setup** already handles production correctly with zero additional configuration:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// This import and JSX element are completely removed from the production build
|
|
49
|
+
import { TanStackDevtools } from '@tanstack/react-devtools'
|
|
50
|
+
|
|
51
|
+
function App() {
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<YourApp />
|
|
55
|
+
<TanStackDevtools
|
|
56
|
+
plugins={
|
|
57
|
+
[
|
|
58
|
+
/* ... */
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
/>
|
|
62
|
+
</>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Mechanism 2: Conditional Exports (package.json)
|
|
68
|
+
|
|
69
|
+
The `@tanstack/devtools` core package uses Node.js conditional exports to serve different bundles based on the environment:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"exports": {
|
|
74
|
+
"workerd": { "import": "./dist/server.js" },
|
|
75
|
+
"browser": {
|
|
76
|
+
"development": { "import": "./dist/dev.js" },
|
|
77
|
+
"import": "./dist/index.js"
|
|
78
|
+
},
|
|
79
|
+
"node": { "import": "./dist/server.js" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Key points:
|
|
85
|
+
|
|
86
|
+
- `browser` + `development` condition resolves to `dev.js` (dev-only extras).
|
|
87
|
+
- `browser` without `development` resolves to `index.js` (production build).
|
|
88
|
+
- `node` and `workerd` resolve to `server.js` (server-safe, no DOM).
|
|
89
|
+
|
|
90
|
+
These are built via `tsup-preset-solid` with `dev_entry: true` and `server_entry: true` in `packages/devtools/tsup.config.ts`.
|
|
91
|
+
|
|
92
|
+
## The Two Workflows
|
|
93
|
+
|
|
94
|
+
### Development-Only Workflow (Default, Recommended)
|
|
95
|
+
|
|
96
|
+
This is the standard path from **devtools-app-setup**. Devtools are present during `vite dev` and stripped automatically on `vite build`.
|
|
97
|
+
|
|
98
|
+
**Install as dev dependencies:**
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm install -D @tanstack/react-devtools @tanstack/devtools-vite
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Vite config -- default behavior:**
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { devtools } from '@tanstack/devtools-vite'
|
|
108
|
+
import react from '@vitejs/plugin-react'
|
|
109
|
+
|
|
110
|
+
export default {
|
|
111
|
+
plugins: [
|
|
112
|
+
devtools(), // removeDevtoolsOnBuild defaults to true
|
|
113
|
+
react(),
|
|
114
|
+
],
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Application code -- no guards needed:**
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import { TanStackDevtools } from '@tanstack/react-devtools'
|
|
122
|
+
|
|
123
|
+
function App() {
|
|
124
|
+
return (
|
|
125
|
+
<>
|
|
126
|
+
<YourApp />
|
|
127
|
+
<TanStackDevtools
|
|
128
|
+
plugins={
|
|
129
|
+
[
|
|
130
|
+
/* ... */
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
/>
|
|
134
|
+
</>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The Vite plugin handles everything. The import and JSX are removed from the production build. Since the packages are dev dependencies, they are not even available in a production `node_modules` after `npm install --production`.
|
|
140
|
+
|
|
141
|
+
### Production Workflow (Intentional)
|
|
142
|
+
|
|
143
|
+
When you deliberately want devtools accessible in a deployed application. This requires three changes from the default setup.
|
|
144
|
+
|
|
145
|
+
**1. Install as regular dependencies (not `-D`):**
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm install @tanstack/react-devtools @tanstack/devtools-vite
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
This ensures the packages are available in production `node_modules`.
|
|
152
|
+
|
|
153
|
+
**2. Disable auto-stripping in the Vite config:**
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { devtools } from '@tanstack/devtools-vite'
|
|
157
|
+
import react from '@vitejs/plugin-react'
|
|
158
|
+
|
|
159
|
+
export default {
|
|
160
|
+
plugins: [
|
|
161
|
+
devtools({
|
|
162
|
+
removeDevtoolsOnBuild: false,
|
|
163
|
+
}),
|
|
164
|
+
react(),
|
|
165
|
+
],
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**3. Application code remains the same:**
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { TanStackDevtools } from '@tanstack/react-devtools'
|
|
173
|
+
|
|
174
|
+
function App() {
|
|
175
|
+
return (
|
|
176
|
+
<>
|
|
177
|
+
<YourApp />
|
|
178
|
+
<TanStackDevtools
|
|
179
|
+
plugins={
|
|
180
|
+
[
|
|
181
|
+
/* ... */
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
/>
|
|
185
|
+
</>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
With `removeDevtoolsOnBuild: false`, the Vite build plugin skips the AST stripping pass entirely, so all devtools code ships to production.
|
|
191
|
+
|
|
192
|
+
You can combine this with `requireUrlFlag` from the shell config to hide the devtools UI unless a URL parameter is present:
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<TanStackDevtools
|
|
196
|
+
config={{
|
|
197
|
+
requireUrlFlag: true,
|
|
198
|
+
urlFlag: 'debug', // visit ?debug to show devtools
|
|
199
|
+
}}
|
|
200
|
+
plugins={
|
|
201
|
+
[
|
|
202
|
+
/* ... */
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
/>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Non-Vite Projects
|
|
209
|
+
|
|
210
|
+
Without the Vite plugin, there is no automatic stripping. You must manually prevent devtools from entering production bundles using one of these strategies.
|
|
211
|
+
|
|
212
|
+
### Strategy A: Conditional Dynamic Import
|
|
213
|
+
|
|
214
|
+
Create a separate file for devtools setup, then conditionally import it:
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
// devtools-setup.tsx
|
|
218
|
+
import { TanStackDevtools } from '@tanstack/react-devtools'
|
|
219
|
+
|
|
220
|
+
export default function Devtools() {
|
|
221
|
+
return (
|
|
222
|
+
<TanStackDevtools
|
|
223
|
+
plugins={
|
|
224
|
+
[
|
|
225
|
+
// your plugins
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
/>
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
// App.tsx
|
|
235
|
+
const Devtools =
|
|
236
|
+
process.env.NODE_ENV === 'development'
|
|
237
|
+
? (await import('./devtools-setup')).default
|
|
238
|
+
: () => null
|
|
239
|
+
|
|
240
|
+
function App() {
|
|
241
|
+
return (
|
|
242
|
+
<>
|
|
243
|
+
<YourApp />
|
|
244
|
+
<Devtools />
|
|
245
|
+
</>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
When `NODE_ENV` is `'production'`, bundlers eliminate the dead `import()` path. The devtools-setup module and all its transitive dependencies are never included in the bundle.
|
|
251
|
+
|
|
252
|
+
### Strategy B: Bundler-Specific Dead Code Elimination
|
|
253
|
+
|
|
254
|
+
For bundlers that support define/replace plugins (webpack `DefinePlugin`, esbuild `define`, Rollup `@rollup/plugin-replace`), wrap the import in a condition that the bundler can statically evaluate:
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
// webpack example with DefinePlugin
|
|
258
|
+
let DevtoolsComponent: React.ComponentType = () => null
|
|
259
|
+
|
|
260
|
+
if (__DEV__) {
|
|
261
|
+
const { TanStackDevtools } = await import('@tanstack/react-devtools')
|
|
262
|
+
DevtoolsComponent = () => (
|
|
263
|
+
<TanStackDevtools
|
|
264
|
+
plugins={
|
|
265
|
+
[
|
|
266
|
+
/* ... */
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
/>
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function App() {
|
|
274
|
+
return (
|
|
275
|
+
<>
|
|
276
|
+
<YourApp />
|
|
277
|
+
<DevtoolsComponent />
|
|
278
|
+
</>
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The key requirement is that the condition must be statically resolvable by the bundler. `process.env.NODE_ENV === 'development'` works for most bundlers. Framework-specific globals like `__DEV__` also work.
|
|
284
|
+
|
|
285
|
+
## NoOp Plugin Variants for Tree-Shaking
|
|
286
|
+
|
|
287
|
+
When building reusable plugin packages with `@tanstack/devtools-utils`, the factory functions return a `[Plugin, NoOpPlugin]` tuple. The `NoOpPlugin` renders an empty fragment and carries no real dependencies. This is the primary mechanism for library authors to make their plugins tree-shakable.
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { createReactPlugin } from '@tanstack/devtools-utils/react'
|
|
291
|
+
|
|
292
|
+
const [QueryPlugin, QueryNoOpPlugin] = createReactPlugin({
|
|
293
|
+
name: 'TanStack Query',
|
|
294
|
+
Component: ({ theme }) => <QueryDevtoolsPanel theme={theme} />,
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
// The library exports both, and consumers choose:
|
|
298
|
+
export { QueryPlugin, QueryNoOpPlugin }
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Consumer code uses the NoOp variant in production:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
import { QueryPlugin, QueryNoOpPlugin } from '@tanstack/query-devtools'
|
|
305
|
+
|
|
306
|
+
const ActivePlugin =
|
|
307
|
+
process.env.NODE_ENV === 'development' ? QueryPlugin : QueryNoOpPlugin
|
|
308
|
+
|
|
309
|
+
function App() {
|
|
310
|
+
return <TanStackDevtools plugins={[ActivePlugin()]} />
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The NoOp pattern exists for every framework adapter:
|
|
315
|
+
|
|
316
|
+
| Framework | Factory | Source |
|
|
317
|
+
| ------------- | -------------------- | ----------------------------------------------- |
|
|
318
|
+
| React | `createReactPlugin` | `packages/devtools-utils/src/react/plugin.tsx` |
|
|
319
|
+
| React (panel) | `createReactPanel` | `packages/devtools-utils/src/react/panel.tsx` |
|
|
320
|
+
| Preact | `createPreactPlugin` | `packages/devtools-utils/src/preact/plugin.tsx` |
|
|
321
|
+
| Solid | `createSolidPlugin` | `packages/devtools-utils/src/solid/plugin.tsx` |
|
|
322
|
+
| Vue | `createVuePlugin` | `packages/devtools-utils/src/vue/plugin.ts` |
|
|
323
|
+
|
|
324
|
+
All return `readonly [Plugin, NoOpPlugin]`. The `NoOpPlugin` always has the same metadata (`name`, `id`, `defaultOpen`) but its render function produces an empty fragment, so the bundler can tree-shake the real panel component and all its dependencies.
|
|
325
|
+
|
|
326
|
+
See the **devtools-framework-adapters** skill for the full factory API details.
|
|
327
|
+
|
|
328
|
+
## Common Mistakes
|
|
329
|
+
|
|
330
|
+
### HIGH: Keeping devtools in production without disabling stripping
|
|
331
|
+
|
|
332
|
+
The Vite plugin's `removeDevtoolsOnBuild` defaults to `true`. If you want devtools in production, you must both disable stripping AND install as a regular dependency. Missing either step causes failure.
|
|
333
|
+
|
|
334
|
+
**Wrong -- devtools stripped despite wanting them in production:**
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
// vite.config.ts
|
|
338
|
+
export default {
|
|
339
|
+
plugins: [
|
|
340
|
+
devtools(), // removeDevtoolsOnBuild defaults to true -- code is stripped
|
|
341
|
+
react(),
|
|
342
|
+
],
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
# package.json has devtools as devDependency
|
|
348
|
+
npm install -D @tanstack/react-devtools
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Correct -- both changes together:**
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
// vite.config.ts
|
|
355
|
+
export default {
|
|
356
|
+
plugins: [devtools({ removeDevtoolsOnBuild: false }), react()],
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
# regular dependency so it's available in production node_modules
|
|
362
|
+
npm install @tanstack/react-devtools
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Missing `removeDevtoolsOnBuild: false` causes the AST stripping to remove all devtools imports and JSX at build time. Missing the regular dependency means `node_modules` may not contain the package in production environments that prune dev dependencies.
|
|
366
|
+
|
|
367
|
+
### HIGH: Non-Vite projects not excluding devtools manually
|
|
368
|
+
|
|
369
|
+
Without the Vite plugin, devtools code is never automatically stripped. If you import `TanStackDevtools` unconditionally, the entire devtools shell and all plugin panels ship to production.
|
|
370
|
+
|
|
371
|
+
**Wrong -- always imports devtools regardless of environment:**
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
import { TanStackDevtools } from '@tanstack/react-devtools'
|
|
375
|
+
|
|
376
|
+
function App() {
|
|
377
|
+
return (
|
|
378
|
+
<>
|
|
379
|
+
<YourApp />
|
|
380
|
+
<TanStackDevtools
|
|
381
|
+
plugins={
|
|
382
|
+
[
|
|
383
|
+
/* ... */
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
/>
|
|
387
|
+
</>
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Correct -- conditional import based on NODE_ENV:**
|
|
393
|
+
|
|
394
|
+
```tsx
|
|
395
|
+
const Devtools =
|
|
396
|
+
process.env.NODE_ENV === 'development'
|
|
397
|
+
? (await import('./devtools-setup')).default
|
|
398
|
+
: () => null
|
|
399
|
+
|
|
400
|
+
function App() {
|
|
401
|
+
return (
|
|
402
|
+
<>
|
|
403
|
+
<YourApp />
|
|
404
|
+
<Devtools />
|
|
405
|
+
</>
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The conditional must be statically evaluable by your bundler so it can eliminate the dead branch. Using a separate file for the devtools setup ensures the entire module subgraph is tree-shaken.
|
|
411
|
+
|
|
412
|
+
### MEDIUM: Not using NoOp variants in plugin libraries
|
|
413
|
+
|
|
414
|
+
When building a reusable plugin package, exporting only the `Plugin` function (ignoring the `NoOpPlugin` from the tuple) means consumers have no lightweight alternative for production builds.
|
|
415
|
+
|
|
416
|
+
**Wrong -- NoOp variant discarded:**
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
const [MyPlugin] = createReactPlugin({
|
|
420
|
+
name: 'Store Inspector',
|
|
421
|
+
Component: StoreInspectorPanel,
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
export { MyPlugin }
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Correct -- both variants exported:**
|
|
428
|
+
|
|
429
|
+
```tsx
|
|
430
|
+
const [MyPlugin, MyNoOpPlugin] = createReactPlugin({
|
|
431
|
+
name: 'Store Inspector',
|
|
432
|
+
Component: StoreInspectorPanel,
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
export { MyPlugin, MyNoOpPlugin }
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Consumers then choose the appropriate variant based on their environment. Without the NoOp export, the only way to exclude the plugin is to not import the package at all, which requires the conditional-import pattern at the application level.
|
|
439
|
+
|
|
440
|
+
## Design Tension
|
|
441
|
+
|
|
442
|
+
Development convenience pulls toward automatic stripping (dev dependencies, Vite plugin handles everything). Production usage pulls toward explicit inclusion (regular dependencies, disabled stripping, URL flag gating). These two paths are mutually exclusive in their dependency and configuration choices. A project must commit to one path. Attempting to mix them -- for example, keeping devtools as a dev dependency while setting `removeDevtoolsOnBuild: false` -- leads to builds that fail silently when the production environment prunes dev dependencies.
|
|
443
|
+
|
|
444
|
+
For staging/preview environments where you want devtools but not in the final production deployment, use `requireUrlFlag` with the development-only workflow intact, rather than switching to the production workflow.
|
|
445
|
+
|
|
446
|
+
## Cross-References
|
|
447
|
+
|
|
448
|
+
- **devtools-app-setup** -- Initial setup decisions (framework, install command, Vite plugin placement) that this skill builds on.
|
|
449
|
+
- **devtools-vite-plugin** -- The `removeDevtoolsOnBuild` option and AST stripping logic live in the Vite plugin. See that skill for all Vite plugin configuration.
|
|
450
|
+
- **devtools-framework-adapters** -- The `[Plugin, NoOpPlugin]` tuple pattern and all framework-specific factory APIs.
|
|
451
|
+
|
|
452
|
+
## Key Source Files
|
|
453
|
+
|
|
454
|
+
- `packages/devtools-vite/src/plugin.ts` -- Vite plugin entry, `removeDevtoolsOnBuild` option, sub-plugin registration
|
|
455
|
+
- `packages/devtools-vite/src/remove-devtools.ts` -- AST-based stripping logic (Babel parse, traverse, codegen)
|
|
456
|
+
- `packages/devtools/package.json` -- Conditional exports (`browser.development` -> `dev.js`, `browser` -> `index.js`, `node`/`workerd` -> `server.js`)
|
|
457
|
+
- `packages/devtools/tsup.config.ts` -- Build config producing `dev.js`, `index.js`, `server.js` via `tsup-preset-solid`
|
|
458
|
+
- `packages/devtools-utils/src/react/plugin.tsx` -- `createReactPlugin` returning `[Plugin, NoOpPlugin]`
|
|
459
|
+
- `packages/devtools-utils/src/react/panel.tsx` -- `createReactPanel` returning `[Panel, NoOpPanel]`
|
|
@@ -4,12 +4,16 @@ import { tabs } from '../tabs'
|
|
|
4
4
|
import { useStyles } from '../styles/use-styles'
|
|
5
5
|
import type { JSX } from 'solid-js'
|
|
6
6
|
|
|
7
|
-
export const TabContent = () => {
|
|
7
|
+
export const TabContent = (props: { isOpen: boolean }) => {
|
|
8
8
|
const { state } = useDevtoolsState()
|
|
9
9
|
const styles = useStyles()
|
|
10
|
-
const component = createMemo<
|
|
11
|
-
(
|
|
12
|
-
)
|
|
10
|
+
const component = createMemo<
|
|
11
|
+
((props: { isOpen: boolean }) => JSX.Element) | null
|
|
12
|
+
>(() => tabs.find((t) => t.id === state().activeTab)?.component || null)
|
|
13
13
|
|
|
14
|
-
return
|
|
14
|
+
return (
|
|
15
|
+
<div class={styles().tabContent}>
|
|
16
|
+
{component()?.({ isOpen: props.isOpen })}
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
15
19
|
}
|
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
import { initialState } from './devtools-store'
|
|
12
12
|
import type { DevtoolsStore } from './devtools-store'
|
|
13
13
|
import type { JSX, Setter } from 'solid-js'
|
|
14
|
+
import type { TanStackDevtoolsTheme } from '@tanstack/devtools-ui'
|
|
14
15
|
|
|
16
|
+
export interface TanStackDevtoolsPluginProps {
|
|
17
|
+
theme: TanStackDevtoolsTheme
|
|
18
|
+
devtoolsOpen: boolean
|
|
19
|
+
}
|
|
15
20
|
export interface TanStackDevtoolsPlugin {
|
|
16
21
|
/**
|
|
17
22
|
* Name to be displayed in the devtools UI.
|
|
@@ -41,10 +46,7 @@ export interface TanStackDevtoolsPlugin {
|
|
|
41
46
|
*/
|
|
42
47
|
name:
|
|
43
48
|
| string
|
|
44
|
-
| ((
|
|
45
|
-
el: HTMLHeadingElement,
|
|
46
|
-
theme: DevtoolsStore['settings']['theme'],
|
|
47
|
-
) => void)
|
|
49
|
+
| ((el: HTMLHeadingElement, props: TanStackDevtoolsPluginProps) => void)
|
|
48
50
|
/**
|
|
49
51
|
* Unique identifier for the plugin.
|
|
50
52
|
* If not provided, it will be generated based on the name.
|
|
@@ -69,11 +71,7 @@ export interface TanStackDevtoolsPlugin {
|
|
|
69
71
|
* }
|
|
70
72
|
* ```
|
|
71
73
|
*/
|
|
72
|
-
render: (
|
|
73
|
-
el: HTMLDivElement,
|
|
74
|
-
theme: DevtoolsStore['settings']['theme'],
|
|
75
|
-
) => void
|
|
76
|
-
|
|
74
|
+
render: (el: HTMLDivElement, props: TanStackDevtoolsPluginProps) => void
|
|
77
75
|
destroy?: (pluginId: string) => void
|
|
78
76
|
}
|
|
79
77
|
export const DevtoolsContext = createContext<{
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TabName } from '../tabs'
|
|
2
2
|
import type { TanStackDevtoolsPlugin } from './devtools-context'
|
|
3
|
+
import type { TanStackDevtoolsTheme } from '@tanstack/devtools-ui'
|
|
3
4
|
|
|
4
5
|
type ModifierKey = 'Alt' | 'Control' | 'Meta' | 'Shift' | 'CtrlOrMeta'
|
|
5
6
|
type KeyboardKey = ModifierKey | (string & {})
|
|
@@ -21,7 +22,7 @@ type TriggerPosition =
|
|
|
21
22
|
| 'middle-right'
|
|
22
23
|
|
|
23
24
|
type TriggerProps = {
|
|
24
|
-
theme:
|
|
25
|
+
theme: TanStackDevtoolsTheme
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export type DevtoolsStore = {
|
|
@@ -71,7 +72,7 @@ export type DevtoolsStore = {
|
|
|
71
72
|
* The theme of the dev tools
|
|
72
73
|
* @default "dark"
|
|
73
74
|
*/
|
|
74
|
-
theme:
|
|
75
|
+
theme: TanStackDevtoolsTheme
|
|
75
76
|
|
|
76
77
|
/**
|
|
77
78
|
* Whether the trigger should be completely hidden or not (you can still open with the hotkey)
|
package/src/devtools.tsx
CHANGED
|
@@ -191,6 +191,14 @@ export default function DevTools() {
|
|
|
191
191
|
|
|
192
192
|
const { theme } = useTheme()
|
|
193
193
|
|
|
194
|
+
createEffect(() => {
|
|
195
|
+
if (typeof document === 'undefined') {
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
document.documentElement.dataset.tanstackDevtoolsTheme = theme()
|
|
200
|
+
})
|
|
201
|
+
|
|
194
202
|
return (
|
|
195
203
|
<ThemeContextProvider theme={theme()}>
|
|
196
204
|
<Portal mount={(pip().pipWindow ?? window).document.body}>
|
|
@@ -211,7 +219,7 @@ export default function DevTools() {
|
|
|
211
219
|
handleDragStart={(e) => handleDragStart(panelRef, e)}
|
|
212
220
|
>
|
|
213
221
|
<Tabs toggleOpen={toggleOpen} />
|
|
214
|
-
<TabContent />
|
|
222
|
+
<TabContent isOpen={isOpen()} />
|
|
215
223
|
</ContentPanel>
|
|
216
224
|
</MainPanel>
|
|
217
225
|
</Show>
|
package/src/index.ts
CHANGED
|
@@ -3,5 +3,7 @@ export { TanStackDevtoolsCore } from './core'
|
|
|
3
3
|
export type { TanStackDevtoolsInit, ClientEventBusConfig } from './core'
|
|
4
4
|
export type {
|
|
5
5
|
TanStackDevtoolsPlugin,
|
|
6
|
+
TanStackDevtoolsPluginProps,
|
|
6
7
|
TanStackDevtoolsConfig,
|
|
7
8
|
} from './context/devtools-context'
|
|
9
|
+
export type { TanStackDevtoolsTheme } from '@tanstack/devtools-ui'
|
package/src/styles/use-styles.ts
CHANGED
|
@@ -505,7 +505,6 @@ const stylesFactory = (theme: DevtoolsStore['settings']['theme']) => {
|
|
|
505
505
|
pluginsTabContent: css`
|
|
506
506
|
width: 100%;
|
|
507
507
|
height: 100%;
|
|
508
|
-
overflow-y: auto;
|
|
509
508
|
|
|
510
509
|
&:not(:last-child) {
|
|
511
510
|
border-right: 5px solid ${t(colors.purple[200], colors.purple[800])};
|
package/src/tabs/index.tsx
CHANGED
|
@@ -208,6 +208,24 @@ const PLUGIN_REGISTRY: Record<string, PluginMetadata> = {
|
|
|
208
208
|
tags: ['TanStack'],
|
|
209
209
|
},
|
|
210
210
|
|
|
211
|
+
// TanStack A11y Devtools
|
|
212
|
+
'@tanstack/devtools-a11y': {
|
|
213
|
+
packageName: '@tanstack/devtools-a11y',
|
|
214
|
+
title: 'Accessibility Devtools',
|
|
215
|
+
description:
|
|
216
|
+
'Audit accessibility issues in real-time with axe-core. Supports WCAG 2.1/2.2, live monitoring, and visual overlays.',
|
|
217
|
+
pluginImport: {
|
|
218
|
+
importName: 'createA11yPlugin',
|
|
219
|
+
type: 'function',
|
|
220
|
+
},
|
|
221
|
+
pluginId: 'devtools-a11y',
|
|
222
|
+
docsUrl: 'https://tanstack.com/devtools/latest/docs/plugins/a11y',
|
|
223
|
+
author: 'TanStack',
|
|
224
|
+
framework: 'react',
|
|
225
|
+
isNew: true,
|
|
226
|
+
tags: ['TanStack', 'a11y'],
|
|
227
|
+
},
|
|
228
|
+
|
|
211
229
|
// ==========================================
|
|
212
230
|
// THIRD-PARTY PLUGINS - Examples
|
|
213
231
|
// ==========================================
|
package/src/tabs/plugins-tab.tsx
CHANGED
|
@@ -6,10 +6,9 @@ import { useStyles } from '../styles/use-styles'
|
|
|
6
6
|
import { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from '../constants'
|
|
7
7
|
import { PluginMarketplace } from './plugin-marketplace'
|
|
8
8
|
|
|
9
|
-
export const PluginsTab = () => {
|
|
9
|
+
export const PluginsTab = (props: { isOpen: boolean }) => {
|
|
10
10
|
const { plugins, activePlugins, toggleActivePlugins } = usePlugins()
|
|
11
11
|
const { expanded, hoverUtils, animationMs, setForceExpand } = useDrawContext()
|
|
12
|
-
|
|
13
12
|
const [pluginRefs, setPluginRefs] = createSignal(
|
|
14
13
|
new Map<string, HTMLDivElement>(),
|
|
15
14
|
)
|
|
@@ -37,7 +36,10 @@ export const PluginsTab = () => {
|
|
|
37
36
|
const ref = pluginRefs().get(plugin.id!)
|
|
38
37
|
|
|
39
38
|
if (ref) {
|
|
40
|
-
plugin.render(ref,
|
|
39
|
+
plugin.render(ref, {
|
|
40
|
+
theme: theme(),
|
|
41
|
+
devtoolsOpen: props.isOpen,
|
|
42
|
+
})
|
|
41
43
|
}
|
|
42
44
|
})
|
|
43
45
|
})
|
|
@@ -86,7 +88,10 @@ export const PluginsTab = () => {
|
|
|
86
88
|
if (pluginHeading) {
|
|
87
89
|
typeof plugin.name === 'string'
|
|
88
90
|
? (pluginHeading.textContent = plugin.name)
|
|
89
|
-
: plugin.name(pluginHeading,
|
|
91
|
+
: plugin.name(pluginHeading, {
|
|
92
|
+
theme: theme(),
|
|
93
|
+
devtoolsOpen: props.isOpen,
|
|
94
|
+
})
|
|
90
95
|
}
|
|
91
96
|
})
|
|
92
97
|
|