From 2815390b2ca53988675d5ea14250eb239f918d87 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 12 Aug 2025 12:28:50 +0200 Subject: [PATCH 1/5] fix: fix issue with solid start --- packages/solid-devtools/src/client-only.ts | 43 +++++++ packages/solid-devtools/src/core.tsx | 130 ++++++++++++++++++++ packages/solid-devtools/src/devtools.tsx | 133 +-------------------- 3 files changed, 177 insertions(+), 129 deletions(-) create mode 100644 packages/solid-devtools/src/client-only.ts create mode 100644 packages/solid-devtools/src/core.tsx diff --git a/packages/solid-devtools/src/client-only.ts b/packages/solid-devtools/src/client-only.ts new file mode 100644 index 000000000..f5587f9e8 --- /dev/null +++ b/packages/solid-devtools/src/client-only.ts @@ -0,0 +1,43 @@ +import { createMemo, createSignal, onMount, sharedConfig, splitProps, untrack } from "solid-js"; +import { isServer } from "solid-js/web"; +import type { Component, ComponentProps, JSX, Setter } from "solid-js"; + +/** + * + * Read more: https://docs.solidjs.com/solid-start/reference/client/client-only + */ +// not using Suspense +export default function clientOnly>( + fn: () => Promise<{ + default: T; + }>, + options: { lazy?: boolean } = {} +) { + if (isServer) return (props: ComponentProps & { fallback?: JSX.Element }) => props.fallback; + + const [comp, setComp] = createSignal(); + !options.lazy && load(fn, setComp); + return (props: ComponentProps) => { + let Comp: T | undefined; + let m: boolean; + const [, rest] = splitProps(props, ["fallback"]); + options.lazy && load(fn, setComp); + if ((Comp = comp()) && !sharedConfig.context) return Comp(rest); + const [mounted, setMounted] = createSignal(!sharedConfig.context); + onMount(() => setMounted(true)); + return createMemo( + () => ( + (Comp = comp()), (m = mounted()), untrack(() => (Comp && m ? Comp(rest) : props.fallback)) + ) + ); + }; +} + +function load( + fn: () => Promise<{ + default: T; + }>, + setComp: Setter +) { + fn().then(m => setComp(() => m.default)); +} \ No newline at end of file diff --git a/packages/solid-devtools/src/core.tsx b/packages/solid-devtools/src/core.tsx new file mode 100644 index 000000000..af780e886 --- /dev/null +++ b/packages/solid-devtools/src/core.tsx @@ -0,0 +1,130 @@ +import { TanStackDevtoolsCore } from '@tanstack/devtools' +import { createEffect, createSignal, onCleanup, onMount } from 'solid-js' +import { Portal } from 'solid-js/web' +import type { JSX } from 'solid-js' +import type { + ClientEventBusConfig, + TanStackDevtoolsConfig, + TanStackDevtoolsPlugin, +} from '@tanstack/devtools' + +type SolidPluginRender = JSX.Element | (() => JSX.Element) +const convertRender = ( + el: HTMLDivElement | HTMLHeadingElement, + Component: SolidPluginRender, +) => ( + + {typeof Component === 'function' ? : Component} + +) + +export type TanStackDevtoolsSolidPlugin = Omit< + TanStackDevtoolsPlugin, + 'render' | 'name' +> & { + /** + * The render function can be a SolidJS element or a function that returns a SolidJS element. + * If it's a function, it will be called to render the plugin, otherwise it will be rendered directly. + * + * Example: + * ```ts + * { + * render: () => , + * } + * ``` + * or + * ```ts + * { + * render: , + * } + * ``` + */ + render: SolidPluginRender + /** + * Name to be displayed in the devtools UI. + * If a string, it will be used as the plugin name. + * If a function, it will be called with the mount element. + * + * Example: + * ```ts + * { + * name: "Your Plugin", + * render: () => , + * } + * ``` + * or + * ```ts + * { + * name:

Your Plugin title

, + * render: () => , + * } + * ``` + */ + name: string | SolidPluginRender +} +export interface TanstackDevtoolsInit { + /** + * Array of plugins to be used in the devtools. + * Each plugin should have a `render` function that returns a React element or a function + * + * Example: + * ```jsx + * , + * } + * ]} + * /> + * ``` + */ + plugins?: Array + /** + * Configuration for the devtools shell. These configuration options are used to set the + * initial state of the devtools when it is started for the first time. Afterwards, + * the settings are persisted in local storage and changed through the settings panel. + */ + config?: Partial + /** + * Configuration for the TanStack Devtools client event bus. + */ + eventBusConfig?: ClientEventBusConfig +} + +export default function SolidDevtoolsCore({ + config, + plugins, + eventBusConfig, +}: TanstackDevtoolsInit) { + const [devtools] = createSignal( + new TanStackDevtoolsCore({ + config, + eventBusConfig, + plugins: plugins?.map((plugin) => ({ + ...plugin, + name: + typeof plugin.name === 'string' + ? plugin.name + : // The check above confirms that `plugin.name` is of Render type + (el) => convertRender(el, plugin.name as SolidPluginRender), + render: (el: HTMLDivElement) => convertRender(el, plugin.render), + })), + }), + ) + let devToolRef: HTMLDivElement | undefined + createEffect(() => { + devtools().setConfig({ config }) + }) + onMount(() => { + if (devToolRef) { + devtools().mount(devToolRef) + + onCleanup(() => { + devtools().unmount() + }) + } + }) + return
+} diff --git a/packages/solid-devtools/src/devtools.tsx b/packages/solid-devtools/src/devtools.tsx index 739aa6e27..a9e4adb2a 100644 --- a/packages/solid-devtools/src/devtools.tsx +++ b/packages/solid-devtools/src/devtools.tsx @@ -1,130 +1,5 @@ -import { TanStackDevtoolsCore } from '@tanstack/devtools' -import { createEffect, createSignal, onCleanup, onMount } from 'solid-js' -import { Portal } from 'solid-js/web' -import type { JSX } from 'solid-js' -import type { - ClientEventBusConfig, - TanStackDevtoolsConfig, - TanStackDevtoolsPlugin, -} from '@tanstack/devtools' +import clientOnly from "./client-only"; -type SolidPluginRender = JSX.Element | (() => JSX.Element) -const convertRender = ( - el: HTMLDivElement | HTMLHeadingElement, - Component: SolidPluginRender, -) => ( - - {typeof Component === 'function' ? : Component} - -) - -export type TanStackDevtoolsSolidPlugin = Omit< - TanStackDevtoolsPlugin, - 'render' | 'name' -> & { - /** - * The render function can be a SolidJS element or a function that returns a SolidJS element. - * If it's a function, it will be called to render the plugin, otherwise it will be rendered directly. - * - * Example: - * ```ts - * { - * render: () => , - * } - * ``` - * or - * ```ts - * { - * render: , - * } - * ``` - */ - render: SolidPluginRender - /** - * Name to be displayed in the devtools UI. - * If a string, it will be used as the plugin name. - * If a function, it will be called with the mount element. - * - * Example: - * ```ts - * { - * name: "Your Plugin", - * render: () => , - * } - * ``` - * or - * ```ts - * { - * name:

Your Plugin title

, - * render: () => , - * } - * ``` - */ - name: string | SolidPluginRender -} -interface TanstackDevtoolsInit { - /** - * Array of plugins to be used in the devtools. - * Each plugin should have a `render` function that returns a React element or a function - * - * Example: - * ```jsx - * , - * } - * ]} - * /> - * ``` - */ - plugins?: Array - /** - * Configuration for the devtools shell. These configuration options are used to set the - * initial state of the devtools when it is started for the first time. Afterwards, - * the settings are persisted in local storage and changed through the settings panel. - */ - config?: Partial - /** - * Configuration for the TanStack Devtools client event bus. - */ - eventBusConfig?: ClientEventBusConfig -} - -export const TanstackDevtools = ({ - config, - plugins, - eventBusConfig, -}: TanstackDevtoolsInit) => { - const [devtools] = createSignal( - new TanStackDevtoolsCore({ - config, - eventBusConfig, - plugins: plugins?.map((plugin) => ({ - ...plugin, - name: - typeof plugin.name === 'string' - ? plugin.name - : // The check above confirms that `plugin.name` is of Render type - (el) => convertRender(el, plugin.name as SolidPluginRender), - render: (el: HTMLDivElement) => convertRender(el, plugin.render), - })), - }), - ) - let devToolRef: HTMLDivElement | undefined - createEffect(() => { - devtools().setConfig({ config }) - }) - onMount(() => { - if (devToolRef) { - devtools().mount(devToolRef) - - onCleanup(() => { - devtools().unmount() - }) - } - }) - return
-} +export const TanstackDevtools = clientOnly(() => + import("./core").then((m) => m), +); From 93c3f8d1b352dfc63804c27a1975786c24a132eb Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:37:05 +0000 Subject: [PATCH 2/5] ci: apply automated fixes --- packages/solid-devtools/src/client-only.ts | 57 +++++++++++++--------- packages/solid-devtools/src/core.tsx | 2 +- packages/solid-devtools/src/devtools.tsx | 6 +-- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/packages/solid-devtools/src/client-only.ts b/packages/solid-devtools/src/client-only.ts index f5587f9e8..ef33e0561 100644 --- a/packages/solid-devtools/src/client-only.ts +++ b/packages/solid-devtools/src/client-only.ts @@ -1,6 +1,13 @@ -import { createMemo, createSignal, onMount, sharedConfig, splitProps, untrack } from "solid-js"; -import { isServer } from "solid-js/web"; -import type { Component, ComponentProps, JSX, Setter } from "solid-js"; +import { + createMemo, + createSignal, + onMount, + sharedConfig, + splitProps, + untrack, +} from 'solid-js' +import { isServer } from 'solid-js/web' +import type { Component, ComponentProps, JSX, Setter } from 'solid-js' /** * @@ -9,35 +16,39 @@ import type { Component, ComponentProps, JSX, Setter } from "solid-js"; // not using Suspense export default function clientOnly>( fn: () => Promise<{ - default: T; + default: T }>, - options: { lazy?: boolean } = {} + options: { lazy?: boolean } = {}, ) { - if (isServer) return (props: ComponentProps & { fallback?: JSX.Element }) => props.fallback; + if (isServer) + return (props: ComponentProps & { fallback?: JSX.Element }) => + props.fallback - const [comp, setComp] = createSignal(); - !options.lazy && load(fn, setComp); + const [comp, setComp] = createSignal() + !options.lazy && load(fn, setComp) return (props: ComponentProps) => { - let Comp: T | undefined; - let m: boolean; - const [, rest] = splitProps(props, ["fallback"]); - options.lazy && load(fn, setComp); - if ((Comp = comp()) && !sharedConfig.context) return Comp(rest); - const [mounted, setMounted] = createSignal(!sharedConfig.context); - onMount(() => setMounted(true)); + let Comp: T | undefined + let m: boolean + const [, rest] = splitProps(props, ['fallback']) + options.lazy && load(fn, setComp) + if ((Comp = comp()) && !sharedConfig.context) return Comp(rest) + const [mounted, setMounted] = createSignal(!sharedConfig.context) + onMount(() => setMounted(true)) return createMemo( () => ( - (Comp = comp()), (m = mounted()), untrack(() => (Comp && m ? Comp(rest) : props.fallback)) - ) - ); - }; + (Comp = comp()), + (m = mounted()), + untrack(() => (Comp && m ? Comp(rest) : props.fallback)) + ), + ) + } } function load( fn: () => Promise<{ - default: T; + default: T }>, - setComp: Setter + setComp: Setter, ) { - fn().then(m => setComp(() => m.default)); -} \ No newline at end of file + fn().then((m) => setComp(() => m.default)) +} diff --git a/packages/solid-devtools/src/core.tsx b/packages/solid-devtools/src/core.tsx index af780e886..6967e3b34 100644 --- a/packages/solid-devtools/src/core.tsx +++ b/packages/solid-devtools/src/core.tsx @@ -108,7 +108,7 @@ export default function SolidDevtoolsCore({ typeof plugin.name === 'string' ? plugin.name : // The check above confirms that `plugin.name` is of Render type - (el) => convertRender(el, plugin.name as SolidPluginRender), + (el) => convertRender(el, plugin.name as SolidPluginRender), render: (el: HTMLDivElement) => convertRender(el, plugin.render), })), }), diff --git a/packages/solid-devtools/src/devtools.tsx b/packages/solid-devtools/src/devtools.tsx index a9e4adb2a..ac5237fbd 100644 --- a/packages/solid-devtools/src/devtools.tsx +++ b/packages/solid-devtools/src/devtools.tsx @@ -1,5 +1,5 @@ -import clientOnly from "./client-only"; +import clientOnly from './client-only' export const TanstackDevtools = clientOnly(() => - import("./core").then((m) => m), -); + import('./core').then((m) => m), +) From 5c415f86493a5c380d50d8b480263d16ce92ed24 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 12 Aug 2025 12:47:03 +0200 Subject: [PATCH 3/5] chore: fix tests --- examples/solid/start/package.json | 4 +- knip.json | 17 +++++--- packages/solid-devtools/src/core.tsx | 2 +- pnpm-lock.yaml | 64 ++-------------------------- 4 files changed, 18 insertions(+), 69 deletions(-) diff --git a/examples/solid/start/package.json b/examples/solid/start/package.json index d0aa08b9b..bdec38111 100644 --- a/examples/solid/start/package.json +++ b/examples/solid/start/package.json @@ -8,8 +8,8 @@ }, "dependencies": { "@solidjs/start": "^1.1.0", - "@tanstack/solid-devtools": "^0.2.2", - "solid-js": "^1.9.5", + "@tanstack/solid-devtools": "^0.3.0", + "solid-js": "^1.9.7", "vinxi": "^0.5.7" }, "engines": { diff --git a/knip.json b/knip.json index 0fb2b8313..904f84901 100644 --- a/knip.json +++ b/knip.json @@ -1,10 +1,17 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "ignoreDependencies": ["@size-limit/preset-small-lib", "@faker-js/faker"], - "ignoreWorkspaces": ["examples/**"], + "ignoreDependencies": [ + "@size-limit/preset-small-lib", + "@faker-js/faker" + ], + "ignoreWorkspaces": [ + "examples/**" + ], "workspaces": { - "packages/react-devtools": { - "ignore": [] + "packages/solid-devtools": { + "ignore": [ + "**/core.tsx" + ] } } -} +} \ No newline at end of file diff --git a/packages/solid-devtools/src/core.tsx b/packages/solid-devtools/src/core.tsx index 6967e3b34..af780e886 100644 --- a/packages/solid-devtools/src/core.tsx +++ b/packages/solid-devtools/src/core.tsx @@ -108,7 +108,7 @@ export default function SolidDevtoolsCore({ typeof plugin.name === 'string' ? plugin.name : // The check above confirms that `plugin.name` is of Render type - (el) => convertRender(el, plugin.name as SolidPluginRender), + (el) => convertRender(el, plugin.name as SolidPluginRender), render: (el: HTMLDivElement) => convertRender(el, plugin.render), })), }), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69fa79c86..af7d644b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -276,10 +276,10 @@ importers: specifier: ^1.1.0 version: 1.1.7(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vinxi@0.5.8(@netlify/blobs@9.1.2)(@types/node@22.15.2)(db0@0.3.2)(ioredis@5.6.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) '@tanstack/solid-devtools': - specifier: ^0.2.2 - version: 0.2.2(csstype@3.1.3)(solid-js@1.9.7) + specifier: ^0.3.0 + version: link:../../../packages/solid-devtools solid-js: - specifier: ^1.9.5 + specifier: ^1.9.7 version: 1.9.7 vinxi: specifier: ^0.5.7 @@ -2257,22 +2257,6 @@ packages: resolution: {integrity: sha512-q6P0aYj7X65biWDKkKFQ4feQoxF8Bcxd3U3CU5zjBt9sgUrc/w8kEfHoGy0cHtgsTSMLfPrzaAtvp6hTbofZmw==} engines: {node: '>=18'} - '@tanstack/devtools-event-bus@0.2.1': - resolution: {integrity: sha512-JMq3AmrQR2LH9P8Rcj1MTq8Iq/mPk/PyuqSw1L0hO2Wl8G1oz5ue31fS8u8lIgOCVR/mGdJah18p+Pj5OosRJA==} - engines: {node: '>=18'} - - '@tanstack/devtools-ui@0.2.2': - resolution: {integrity: sha512-G2gRyoGpjtr25w9BqQzPbefiJ9WALWYLOVxVr+NoqOLDA680nUO5KJjp7oLZMfnKssWxACgulaQ3DUmGGJuysQ==} - engines: {node: '>=18'} - peerDependencies: - solid-js: '>=1.9.7' - - '@tanstack/devtools@0.3.0': - resolution: {integrity: sha512-uxj3MM2/ZlQDlhnBoUSlJC8oneJ+sPh6hK0kgiKZAEgrlUvZfUNgKGhrmMHZaAGl/WEglhZoxXwvvo9HAFT8yw==} - engines: {node: '>=18'} - peerDependencies: - solid-js: '>=1.9.7' - '@tanstack/directive-functions-plugin@1.121.21': resolution: {integrity: sha512-B9z/HbF7gJBaRHieyX7f2uQ4LpLLAVAEutBZipH6w+CYD6RHRJvSVPzECGHF7icFhNWTiJQL2QR6K07s59yzEw==} engines: {node: '>=12'} @@ -2483,12 +2467,6 @@ packages: resolution: {integrity: sha512-aiYxrC6A8jPojAhHfH10YiwfI626yTBlyzIvdPzVvv3DWkRA7yShSOc6FDuwX+oSPmScJSIX/OAHTzJnStY5GA==} engines: {node: '>=12'} - '@tanstack/solid-devtools@0.2.2': - resolution: {integrity: sha512-jCKXD91fGaqDLrILtstIRw2WZmFT/YvBrHbfhcAt+9E00gfm9a6eI2J1fZJah0y5IZU+VGZ03flcMgZ8yQuD1w==} - engines: {node: '>=18'} - peerDependencies: - solid-js: '>=1.9.7' - '@tanstack/solid-query-devtools@5.83.0': resolution: {integrity: sha512-Z0wQlAWXz/U2bJ/paMRBTDhMoPnB9Te6GmA21sXnI+nDnAAPZRcPxFBiCgYJS3eFsvbkdRGJwoUSQrdIgy0shg==} peerDependencies: @@ -8856,33 +8834,6 @@ snapshots: - typescript - vite - '@tanstack/devtools-event-bus@0.2.1': - dependencies: - ws: 8.18.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@tanstack/devtools-ui@0.2.2(csstype@3.1.3)(solid-js@1.9.7)': - dependencies: - goober: 2.1.16(csstype@3.1.3) - solid-js: 1.9.7 - transitivePeerDependencies: - - csstype - - '@tanstack/devtools@0.3.0(csstype@3.1.3)(solid-js@1.9.7)': - dependencies: - '@solid-primitives/keyboard': 1.3.3(solid-js@1.9.7) - '@tanstack/devtools-event-bus': 0.2.1 - '@tanstack/devtools-ui': 0.2.2(csstype@3.1.3)(solid-js@1.9.7) - clsx: 2.1.1 - goober: 2.1.16(csstype@3.1.3) - solid-js: 1.9.7 - transitivePeerDependencies: - - bufferutil - - csstype - - utf-8-validate - '@tanstack/directive-functions-plugin@1.121.21(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@babel/code-frame': 7.26.2 @@ -9299,15 +9250,6 @@ snapshots: - supports-color - vite - '@tanstack/solid-devtools@0.2.2(csstype@3.1.3)(solid-js@1.9.7)': - dependencies: - '@tanstack/devtools': 0.3.0(csstype@3.1.3)(solid-js@1.9.7) - solid-js: 1.9.7 - transitivePeerDependencies: - - bufferutil - - csstype - - utf-8-validate - '@tanstack/solid-query-devtools@5.83.0(@tanstack/solid-query@5.83.0(solid-js@1.9.7))(solid-js@1.9.7)': dependencies: '@tanstack/query-devtools': 5.81.2 From 1f2e873787dfdad6b50d95607ed26757e3bb94cd Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:47:37 +0000 Subject: [PATCH 4/5] ci: apply automated fixes --- knip.json | 15 ++++----------- packages/solid-devtools/src/core.tsx | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/knip.json b/knip.json index 904f84901..ff4cd666e 100644 --- a/knip.json +++ b/knip.json @@ -1,17 +1,10 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "ignoreDependencies": [ - "@size-limit/preset-small-lib", - "@faker-js/faker" - ], - "ignoreWorkspaces": [ - "examples/**" - ], + "ignoreDependencies": ["@size-limit/preset-small-lib", "@faker-js/faker"], + "ignoreWorkspaces": ["examples/**"], "workspaces": { "packages/solid-devtools": { - "ignore": [ - "**/core.tsx" - ] + "ignore": ["**/core.tsx"] } } -} \ No newline at end of file +} diff --git a/packages/solid-devtools/src/core.tsx b/packages/solid-devtools/src/core.tsx index af780e886..6967e3b34 100644 --- a/packages/solid-devtools/src/core.tsx +++ b/packages/solid-devtools/src/core.tsx @@ -108,7 +108,7 @@ export default function SolidDevtoolsCore({ typeof plugin.name === 'string' ? plugin.name : // The check above confirms that `plugin.name` is of Render type - (el) => convertRender(el, plugin.name as SolidPluginRender), + (el) => convertRender(el, plugin.name as SolidPluginRender), render: (el: HTMLDivElement) => convertRender(el, plugin.render), })), }), From 211ff367dd702317baf14ab2c828e03314d31326 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 12 Aug 2025 12:52:31 +0200 Subject: [PATCH 5/5] chore: add changeset --- .changeset/hip-moons-enter.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hip-moons-enter.md diff --git a/.changeset/hip-moons-enter.md b/.changeset/hip-moons-enter.md new file mode 100644 index 000000000..c636ecc32 --- /dev/null +++ b/.changeset/hip-moons-enter.md @@ -0,0 +1,5 @@ +--- +'@tanstack/solid-devtools': patch +--- + +Fixed issue where solid devtools didn't work with solid start