From 3ba58250ceeee600da941589de965ea40b1c9367 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Mon, 20 Oct 2025 14:01:51 +0200 Subject: [PATCH 1/5] Improve bundling of core devtools to not leak to client --- package.json | 4 +- packages/devtools/package.json | 31 +++----- .../devtools/src/context/devtools-context.tsx | 12 +-- .../devtools/src/context/devtools-store.ts | 26 ------- .../devtools/src/context/initial-state.ts | 27 +++++++ packages/devtools/src/core.tsx | 15 ++-- packages/react-devtools/src/devtools.tsx | 78 ++++++++++--------- 7 files changed, 94 insertions(+), 99 deletions(-) create mode 100644 packages/devtools/src/context/initial-state.ts diff --git a/package.json b/package.json index bc949e0d..233e544d 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ }, "size-limit": [ { - "path": "packages/devtools/dist/index.js", + "path": "packages/devtools/dist/esm/index.js", "limit": "60 KB" }, { @@ -85,4 +85,4 @@ "@tanstack/solid-devtools": "workspace:*", "@tanstack/devtools-vite": "workspace:*" } -} +} \ No newline at end of file diff --git a/packages/devtools/package.json b/packages/devtools/package.json index c1917121..505ca544 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -18,27 +18,16 @@ "devtools" ], "type": "module", - "types": "dist/index.d.ts", - "module": "dist/index.js", + "types": "dist/esm/index.d.ts", + "module": "dist/esm/index.js", "exports": { - "workerd": { - "types": "./dist/index.d.ts", - "import": "./dist/server.js" + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + } }, - "browser": { - "development": { - "types": "./dist/index.d.ts", - "import": "./dist/dev.js" - }, - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "node": { - "types": "./dist/index.d.ts", - "import": "./dist/server.js" - }, - "types": "./dist/index.d.ts", - "import": "./dist/index.js" + "./package.json": "./package.json" }, "sideEffects": false, "engines": { @@ -56,7 +45,7 @@ "test:lib:dev": "pnpm test:lib --watch", "test:types": "tsc", "test:build": "publint --strict", - "build": "tsup" + "build": "vite build" }, "dependencies": { "@solid-primitives/event-listener": "^2.4.3", @@ -77,4 +66,4 @@ "tsup-preset-solid": "^2.2.0", "vite-plugin-solid": "^2.11.8" } -} +} \ No newline at end of file diff --git a/packages/devtools/src/context/devtools-context.tsx b/packages/devtools/src/context/devtools-context.tsx index 813690e8..f7cad1df 100644 --- a/packages/devtools/src/context/devtools-context.tsx +++ b/packages/devtools/src/context/devtools-context.tsx @@ -7,7 +7,7 @@ import { getStorageItem, setStorageItem, } from '../utils/storage' -import { initialState } from './devtools-store' +import { initialState } from './initial-state' import type { DevtoolsStore } from './devtools-store' import type { JSX, Setter } from 'solid-js' @@ -39,11 +39,11 @@ export interface TanStackDevtoolsPlugin { * ``` */ name: - | string - | (( - el: HTMLHeadingElement, - theme: DevtoolsStore['settings']['theme'], - ) => void) + | string + | (( + el: HTMLHeadingElement, + theme: DevtoolsStore['settings']['theme'], + ) => void) /** * Unique identifier for the plugin. * If not provided, it will be generated based on the name. diff --git a/packages/devtools/src/context/devtools-store.ts b/packages/devtools/src/context/devtools-store.ts index 8ff46b39..5a125181 100644 --- a/packages/devtools/src/context/devtools-store.ts +++ b/packages/devtools/src/context/devtools-store.ts @@ -79,29 +79,3 @@ export type DevtoolsStore = { } plugins?: Array } - -export const initialState: DevtoolsStore = { - settings: { - defaultOpen: false, - hideUntilHover: false, - position: 'bottom-right', - panelLocation: 'bottom', - openHotkey: ['Shift', 'A'], - requireUrlFlag: false, - urlFlag: 'tanstack-devtools', - theme: - typeof window !== 'undefined' && - typeof window.matchMedia !== 'undefined' && - window.matchMedia('(prefers-color-scheme: dark)').matches - ? 'dark' - : 'light', - triggerImage: '', - triggerHidden: false, - }, - state: { - activeTab: 'plugins', - height: 400, - activePlugins: [], - persistOpen: false, - }, -} diff --git a/packages/devtools/src/context/initial-state.ts b/packages/devtools/src/context/initial-state.ts new file mode 100644 index 00000000..05bf4683 --- /dev/null +++ b/packages/devtools/src/context/initial-state.ts @@ -0,0 +1,27 @@ +import type { DevtoolsStore } from './devtools-store' + +export const initialState: DevtoolsStore = { + settings: { + defaultOpen: false, + hideUntilHover: false, + position: 'bottom-right', + panelLocation: 'bottom', + openHotkey: ['Shift', 'A'], + requireUrlFlag: false, + urlFlag: 'tanstack-devtools', + theme: + typeof window !== 'undefined' && + typeof window.matchMedia !== 'undefined' && + window.matchMedia('(prefers-color-scheme: dark)').matches + ? 'dark' + : 'light', + triggerImage: '', + triggerHidden: false, + }, + state: { + activeTab: 'plugins', + height: 400, + activePlugins: [], + persistOpen: false, + }, +} diff --git a/packages/devtools/src/core.tsx b/packages/devtools/src/core.tsx index 63ca8307..40cbc304 100644 --- a/packages/devtools/src/core.tsx +++ b/packages/devtools/src/core.tsx @@ -1,9 +1,6 @@ -import { lazy } from 'solid-js' -import { Portal, render } from 'solid-js/web' + import { ClientEventBus } from '@tanstack/devtools-event-bus/client' -import { DevtoolsProvider } from './context/devtools-context' -import { initialState } from './context/devtools-store' -import { PiPProvider } from './context/pip-context' +import { initialState } from './context/initial-state' import type { ClientEventBusConfig } from '@tanstack/devtools-event-bus/client' import type { TanStackDevtoolsConfig, @@ -61,16 +58,18 @@ export class TanStackDevtoolsCore { } } - mount(el: T) { - // tsup-preset-solid statically replaces this variable during build, which eliminates this code from server bundle - if (import.meta.env.SSR) return + async mount(el: T) { if (this.#isMounted) { throw new Error('Devtools is already mounted') } + const { render, Portal } = await import('solid-js/web') + const { lazy } = await import('solid-js') const mountTo = el const dispose = render(() => { this.#Component = lazy(() => import('./devtools')) + const DevtoolsProvider = lazy(() => import('./context/devtools-context').then(m => ({ default: m.DevtoolsProvider }))) + const PiPProvider = lazy(() => import('./context/pip-context').then(m => ({ default: m.PiPProvider }))) const Devtools = this.#Component this.#eventBus = new ClientEventBus(this.#eventBusConfig) this.#eventBus.start() diff --git a/packages/react-devtools/src/devtools.tsx b/packages/react-devtools/src/devtools.tsx index 14adad62..c0a817b1 100644 --- a/packages/react-devtools/src/devtools.tsx +++ b/packages/react-devtools/src/devtools.tsx @@ -111,7 +111,7 @@ export const TanStackDevtools = ({ eventBusConfig, }: TanStackDevtoolsReactInit): ReactElement | null => { const devToolRef = useRef(null) - + const devtoolInstance = useRef(null) const [pluginContainers, setPluginContainers] = useState< Record >({}) @@ -135,23 +135,23 @@ export const TanStackDevtools = ({ typeof plugin.name === 'string' ? plugin.name : (e, theme) => { - const id = e.getAttribute('id')! - const target = e.ownerDocument.getElementById(id) - - if (target) { - setTitleContainers((prev) => ({ - ...prev, - [id]: e, - })) - } - - convertRender( - plugin.name as PluginRender, - setTitleComponents, - e, - theme, - ) - }, + const id = e.getAttribute('id')! + const target = e.ownerDocument.getElementById(id) + + if (target) { + setTitleContainers((prev) => ({ + ...prev, + [id]: e, + })) + } + + convertRender( + plugin.name as PluginRender, + setTitleComponents, + e, + theme, + ) + }, render: (e, theme) => { const id = e.getAttribute('id')! const target = e.ownerDocument.getElementById(id) @@ -170,28 +170,34 @@ export const TanStackDevtools = ({ [plugins], ) - const [devtools] = useState( - () => - new TanStackDevtoolsCore({ - config, - eventBusConfig, - plugins: pluginsMap, - }), - ) + // initialize devtools instance + useEffect(() => { + if (devtoolInstance.current) { + return + } + devtoolInstance.current = new TanStackDevtoolsCore({ + config, + eventBusConfig, + plugins: pluginsMap, + }) + }, [config, eventBusConfig, pluginsMap]) + useEffect(() => { - devtools.setConfig({ + devtoolInstance.current?.setConfig({ plugins: pluginsMap, }) - }, [devtools, pluginsMap]) + }, [devtoolInstance, pluginsMap]) useEffect(() => { - if (devToolRef.current) { - devtools.mount(devToolRef.current) + if (!devToolRef.current) { + return } - return () => devtools.unmount() - }, [devtools]) + devtoolInstance.current?.mount(devToolRef.current) + + return () => devtoolInstance.current?.unmount() + }, [devtoolInstance]) const hasPlugins = Object.values(pluginContainers).length > 0 && @@ -206,14 +212,14 @@ export const TanStackDevtools = ({ {hasPlugins ? Object.entries(pluginContainers).map(([key, pluginContainer]) => - createPortal(<>{PluginComponents[key]}, pluginContainer), - ) + createPortal(<>{PluginComponents[key]}, pluginContainer), + ) : null} {hasTitles ? Object.entries(titleContainers).map(([key, titleContainer]) => - createPortal(<>{TitleComponents[key]}, titleContainer), - ) + createPortal(<>{TitleComponents[key]}, titleContainer), + ) : null} ) From 18c4808aaa32b439abdce64430bbd7d114df31d8 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 12:04:13 +0000 Subject: [PATCH 2/5] ci: apply automated fixes --- package.json | 2 +- packages/devtools/package.json | 2 +- .../devtools/src/context/devtools-context.tsx | 10 ++--- packages/devtools/src/core.tsx | 14 ++++-- packages/react-devtools/src/devtools.tsx | 43 +++++++++---------- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 233e544d..ea609309 100644 --- a/package.json +++ b/package.json @@ -85,4 +85,4 @@ "@tanstack/solid-devtools": "workspace:*", "@tanstack/devtools-vite": "workspace:*" } -} \ No newline at end of file +} diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 505ca544..daf9a8e4 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -66,4 +66,4 @@ "tsup-preset-solid": "^2.2.0", "vite-plugin-solid": "^2.11.8" } -} \ No newline at end of file +} diff --git a/packages/devtools/src/context/devtools-context.tsx b/packages/devtools/src/context/devtools-context.tsx index f7cad1df..1b31c944 100644 --- a/packages/devtools/src/context/devtools-context.tsx +++ b/packages/devtools/src/context/devtools-context.tsx @@ -39,11 +39,11 @@ export interface TanStackDevtoolsPlugin { * ``` */ name: - | string - | (( - el: HTMLHeadingElement, - theme: DevtoolsStore['settings']['theme'], - ) => void) + | string + | (( + el: HTMLHeadingElement, + theme: DevtoolsStore['settings']['theme'], + ) => void) /** * Unique identifier for the plugin. * If not provided, it will be generated based on the name. diff --git a/packages/devtools/src/core.tsx b/packages/devtools/src/core.tsx index 40cbc304..2d728ecb 100644 --- a/packages/devtools/src/core.tsx +++ b/packages/devtools/src/core.tsx @@ -1,4 +1,3 @@ - import { ClientEventBus } from '@tanstack/devtools-event-bus/client' import { initialState } from './context/initial-state' import type { ClientEventBusConfig } from '@tanstack/devtools-event-bus/client' @@ -59,7 +58,6 @@ export class TanStackDevtoolsCore { } async mount(el: T) { - if (this.#isMounted) { throw new Error('Devtools is already mounted') } @@ -68,8 +66,16 @@ export class TanStackDevtoolsCore { const mountTo = el const dispose = render(() => { this.#Component = lazy(() => import('./devtools')) - const DevtoolsProvider = lazy(() => import('./context/devtools-context').then(m => ({ default: m.DevtoolsProvider }))) - const PiPProvider = lazy(() => import('./context/pip-context').then(m => ({ default: m.PiPProvider }))) + const DevtoolsProvider = lazy(() => + import('./context/devtools-context').then((m) => ({ + default: m.DevtoolsProvider, + })), + ) + const PiPProvider = lazy(() => + import('./context/pip-context').then((m) => ({ + default: m.PiPProvider, + })), + ) const Devtools = this.#Component this.#eventBus = new ClientEventBus(this.#eventBusConfig) this.#eventBus.start() diff --git a/packages/react-devtools/src/devtools.tsx b/packages/react-devtools/src/devtools.tsx index c0a817b1..54b4d702 100644 --- a/packages/react-devtools/src/devtools.tsx +++ b/packages/react-devtools/src/devtools.tsx @@ -135,23 +135,23 @@ export const TanStackDevtools = ({ typeof plugin.name === 'string' ? plugin.name : (e, theme) => { - const id = e.getAttribute('id')! - const target = e.ownerDocument.getElementById(id) - - if (target) { - setTitleContainers((prev) => ({ - ...prev, - [id]: e, - })) - } - - convertRender( - plugin.name as PluginRender, - setTitleComponents, - e, - theme, - ) - }, + const id = e.getAttribute('id')! + const target = e.ownerDocument.getElementById(id) + + if (target) { + setTitleContainers((prev) => ({ + ...prev, + [id]: e, + })) + } + + convertRender( + plugin.name as PluginRender, + setTitleComponents, + e, + theme, + ) + }, render: (e, theme) => { const id = e.getAttribute('id')! const target = e.ownerDocument.getElementById(id) @@ -182,7 +182,6 @@ export const TanStackDevtools = ({ }) }, [config, eventBusConfig, pluginsMap]) - useEffect(() => { devtoolInstance.current?.setConfig({ plugins: pluginsMap, @@ -212,14 +211,14 @@ export const TanStackDevtools = ({ {hasPlugins ? Object.entries(pluginContainers).map(([key, pluginContainer]) => - createPortal(<>{PluginComponents[key]}, pluginContainer), - ) + createPortal(<>{PluginComponents[key]}, pluginContainer), + ) : null} {hasTitles ? Object.entries(titleContainers).map(([key, titleContainer]) => - createPortal(<>{TitleComponents[key]}, titleContainer), - ) + createPortal(<>{TitleComponents[key]}, titleContainer), + ) : null} ) From 75d9dd078fbf07bff20f7097b6adb1e3e9ffd5e1 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Mon, 20 Oct 2025 14:58:03 +0200 Subject: [PATCH 3/5] fix plugin order issue --- packages/devtools-vite/src/plugin.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/devtools-vite/src/plugin.ts b/packages/devtools-vite/src/plugin.ts index 93248fef..466c71c8 100644 --- a/packages/devtools-vite/src/plugin.ts +++ b/packages/devtools-vite/src/plugin.ts @@ -91,7 +91,8 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array => { id.includes('node_modules') || id.includes('?raw') || id.includes('dist') || - id.includes('build') + id.includes('build') || + id.includes('?tsr') ) return @@ -416,6 +417,7 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array => { id.includes('?raw') || id.includes('dist') || id.includes('build') || + id.includes('?tsr') || !code.includes('console.') ) return From 6228721b3f0c132fdc2f7f2f8608b45069a9c856 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Mon, 20 Oct 2025 15:34:28 +0200 Subject: [PATCH 4/5] revert --- packages/devtools-vite/src/plugin.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/devtools-vite/src/plugin.ts b/packages/devtools-vite/src/plugin.ts index 466c71c8..93248fef 100644 --- a/packages/devtools-vite/src/plugin.ts +++ b/packages/devtools-vite/src/plugin.ts @@ -91,8 +91,7 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array => { id.includes('node_modules') || id.includes('?raw') || id.includes('dist') || - id.includes('build') || - id.includes('?tsr') + id.includes('build') ) return @@ -417,7 +416,6 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array => { id.includes('?raw') || id.includes('dist') || id.includes('build') || - id.includes('?tsr') || !code.includes('console.') ) return From 6431b17c1c7828e49e6c94936932386eb29fd7d3 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Mon, 20 Oct 2025 15:50:24 +0200 Subject: [PATCH 5/5] remove devtools from production fix --- packages/devtools-vite/src/plugin.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/devtools-vite/src/plugin.ts b/packages/devtools-vite/src/plugin.ts index 93248fef..14de2d59 100644 --- a/packages/devtools-vite/src/plugin.ts +++ b/packages/devtools-vite/src/plugin.ts @@ -178,8 +178,14 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array => { }, { name: '@tanstack/devtools:remove-devtools-on-build', - apply(_, { command }) { - return command === 'build' && removeDevtoolsOnBuild + apply(config, { command }) { + // Check both command and mode to support various hosting providers + // Some providers (Cloudflare, Netlify, Heroku) might not use 'build' command + // but will always set mode to 'production' for production builds + return ( + (command !== 'serve' || config.mode === 'production') && + removeDevtoolsOnBuild + ) }, enforce: 'pre', transform(code, id) {