From edfbd43b032e3d54544a5e4de37f10990a059ec4 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 6 Dec 2025 11:07:52 -0600 Subject: [PATCH 1/4] feat: add preact devtools --- docs/config.json | 44 +++ docs/framework/preact/adapter.md | 16 + docs/framework/preact/basic-setup.md | 69 ++++ .../framework/preact/guides/custom-plugins.md | 204 +++++++++++ .../reference/functions/tanstackdevtools.md | 24 -- docs/framework/solid/reference/index.md | 16 - .../tanstackdevtoolssolidplugin.md | 63 ---- .../reference/classes/tanstackdevtoolscore.md | 14 +- .../interfaces/tanstackdevtoolsinit.md | 11 +- .../interfaces/tanstackdevtoolsplugin.md | 55 ++- .../type-aliases/tanstackdevtoolsconfig.md | 2 +- examples/preact/basic/.eslintrc.cjs | 13 + examples/preact/basic/.gitignore | 27 ++ examples/preact/basic/README.md | 6 + examples/preact/basic/index.html | 40 +++ examples/preact/basic/package.json | 38 ++ examples/preact/basic/public/emblem-light.svg | 13 + .../basic/src/button-with-props-only.tsx | 5 + examples/preact/basic/src/button.tsx | 3 + examples/preact/basic/src/feature.tsx | 12 + examples/preact/basic/src/index.tsx | 190 ++++++++++ .../preact/basic/src/package-json-panel.tsx | 340 ++++++++++++++++++ examples/preact/basic/src/plugin.ts | 25 ++ examples/preact/basic/src/setup.tsx | 20 ++ examples/preact/basic/tsconfig.json | 24 ++ examples/preact/basic/vite.config.ts | 21 ++ examples/preact/custom-devtools/.eslintrc.cjs | 11 + examples/preact/custom-devtools/.gitignore | 27 ++ examples/preact/custom-devtools/README.md | 6 + examples/preact/custom-devtools/index.html | 16 + examples/preact/custom-devtools/package.json | 32 ++ .../custom-devtools/public/emblem-light.svg | 13 + examples/preact/custom-devtools/src/App.tsx | 30 ++ .../src/CustomDevtoolsPanel.tsx | 23 ++ .../preact/custom-devtools/src/counter.ts | 29 ++ .../preact/custom-devtools/src/eventClient.ts | 20 ++ examples/preact/custom-devtools/src/index.tsx | 22 ++ examples/preact/custom-devtools/tsconfig.json | 24 ++ .../preact/custom-devtools/vite.config.ts | 7 + package.json | 1 + packages/devtools-utils/package.json | 16 +- packages/devtools-utils/src/preact/index.ts | 3 + packages/devtools-utils/src/preact/panel.tsx | 60 ++++ packages/devtools-utils/src/preact/plugin.tsx | 31 ++ packages/devtools-utils/tsconfig.json | 1 + packages/devtools-utils/vite.config.preact.ts | 26 ++ packages/preact-devtools/CHANGELOG.md | 8 + packages/preact-devtools/README.md | 58 +++ packages/preact-devtools/eslint.config.js | 19 + packages/preact-devtools/package.json | 62 ++++ packages/preact-devtools/src/devtools.tsx | 308 ++++++++++++++++ packages/preact-devtools/src/index.ts | 11 + packages/preact-devtools/tsconfig.docs.json | 10 + packages/preact-devtools/tsconfig.json | 9 + packages/preact-devtools/vite.config.ts | 27 ++ packages/react-devtools/package.json | 2 +- pnpm-lock.yaml | 252 ++++++++++++- vitest.workspace.js | 1 + 58 files changed, 2329 insertions(+), 131 deletions(-) create mode 100644 docs/framework/preact/adapter.md create mode 100644 docs/framework/preact/basic-setup.md create mode 100644 docs/framework/preact/guides/custom-plugins.md delete mode 100644 docs/framework/solid/reference/functions/tanstackdevtools.md delete mode 100644 docs/framework/solid/reference/index.md delete mode 100644 docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md create mode 100644 examples/preact/basic/.eslintrc.cjs create mode 100644 examples/preact/basic/.gitignore create mode 100644 examples/preact/basic/README.md create mode 100644 examples/preact/basic/index.html create mode 100644 examples/preact/basic/package.json create mode 100644 examples/preact/basic/public/emblem-light.svg create mode 100644 examples/preact/basic/src/button-with-props-only.tsx create mode 100644 examples/preact/basic/src/button.tsx create mode 100644 examples/preact/basic/src/feature.tsx create mode 100644 examples/preact/basic/src/index.tsx create mode 100644 examples/preact/basic/src/package-json-panel.tsx create mode 100644 examples/preact/basic/src/plugin.ts create mode 100644 examples/preact/basic/src/setup.tsx create mode 100644 examples/preact/basic/tsconfig.json create mode 100644 examples/preact/basic/vite.config.ts create mode 100644 examples/preact/custom-devtools/.eslintrc.cjs create mode 100644 examples/preact/custom-devtools/.gitignore create mode 100644 examples/preact/custom-devtools/README.md create mode 100644 examples/preact/custom-devtools/index.html create mode 100644 examples/preact/custom-devtools/package.json create mode 100644 examples/preact/custom-devtools/public/emblem-light.svg create mode 100644 examples/preact/custom-devtools/src/App.tsx create mode 100644 examples/preact/custom-devtools/src/CustomDevtoolsPanel.tsx create mode 100644 examples/preact/custom-devtools/src/counter.ts create mode 100644 examples/preact/custom-devtools/src/eventClient.ts create mode 100644 examples/preact/custom-devtools/src/index.tsx create mode 100644 examples/preact/custom-devtools/tsconfig.json create mode 100644 examples/preact/custom-devtools/vite.config.ts create mode 100644 packages/devtools-utils/src/preact/index.ts create mode 100644 packages/devtools-utils/src/preact/panel.tsx create mode 100644 packages/devtools-utils/src/preact/plugin.tsx create mode 100644 packages/devtools-utils/vite.config.preact.ts create mode 100644 packages/preact-devtools/CHANGELOG.md create mode 100644 packages/preact-devtools/README.md create mode 100644 packages/preact-devtools/eslint.config.js create mode 100644 packages/preact-devtools/package.json create mode 100644 packages/preact-devtools/src/devtools.tsx create mode 100644 packages/preact-devtools/src/index.ts create mode 100644 packages/preact-devtools/tsconfig.docs.json create mode 100644 packages/preact-devtools/tsconfig.json create mode 100644 packages/preact-devtools/vite.config.ts diff --git a/docs/config.json b/docs/config.json index b0053346..e03fc3c7 100644 --- a/docs/config.json +++ b/docs/config.json @@ -56,6 +56,19 @@ } ] }, + { + "label": "preact", + "children": [ + { + "label": "Basic setup", + "to": "framework/preact/basic-setup" + }, + { + "label": "Preact adapter", + "to": "framework/preact/adapter" + } + ] + }, { "label": "solid", "children": [ @@ -84,6 +97,15 @@ } ] }, + { + "label": "preact", + "children": [ + { + "label": "Custom plugins", + "to": "framework/preact/guides/custom-plugins" + } + ] + }, { "label": "solid", "children": [] @@ -108,6 +130,15 @@ } ] }, + { + "label": "preact", + "children": [ + { + "label": "Preact Hooks", + "to": "framework/preact/reference/index" + } + ] + }, { "label": "solid", "children": [ @@ -140,6 +171,19 @@ } ] }, + { + "label": "preact", + "children": [ + { + "label": "Basic", + "to": "framework/preact/examples/basic" + }, + { + "label": "Custom devtools", + "to": "framework/preact/examples/custom-devtools" + } + ] + }, { "label": "solid", "children": [ diff --git a/docs/framework/preact/adapter.md b/docs/framework/preact/adapter.md new file mode 100644 index 00000000..e490fa97 --- /dev/null +++ b/docs/framework/preact/adapter.md @@ -0,0 +1,16 @@ +--- +title: TanStack Devtools Preact Adapter +id: adapter +--- + +If you are using TanStack Devtools in a Preact application, we recommend using the Preact Adapter. The Preact Adapter provides a set of easy-to-use hooks on top of the core Devtools utilities. If you find yourself wanting to use the core Devtools classes/functions directly, the Preact Adapter will also re-export everything from the core package. + +## Installation + +```sh +npm install @tanstack/preact-devtools +``` + +## Preact Hooks + +TODO diff --git a/docs/framework/preact/basic-setup.md b/docs/framework/preact/basic-setup.md new file mode 100644 index 00000000..efb501b6 --- /dev/null +++ b/docs/framework/preact/basic-setup.md @@ -0,0 +1,69 @@ +--- +title: Basic setup +id: basic-setup +--- + +TanStack devtools provides you with an easy to use and modular client that allows you to compose multiple devtools into one easy to use panel. + +## Setup + +Install the [TanStack Devtools](https://www.npmjs.com/package/@tanstack/preact-devtools) library, this will install the devtools core as well as provide you framework specific adapters. + +```bash +npm i @tanstack/preact-devtools +``` + +Next in the root of your application import the `TanStackDevtools` from the required framework adapter (in this case @tanstack/preact-devtools). + +```tsx +import { TanStackDevtools } from '@tanstack/preact-devtools' +import { render } from 'preact' + +import App from './App' + +render( + <> + + + + , + document.getElementById('root')!, +) +``` + +Import the desired devtools and provide it to the `TanStackDevtools` component along with a label for the menu. + +Currently TanStack offers: + +- `QueryDevtools` +- `RouterDevtools` +- `PacerDevtools` +- `FormDevtools` [coming soon](https://github.com/TanStack/form/pull/1692) + +```tsx +import { render } from 'preact' + +import { TanStackDevtools } from '@tanstack/preact-devtools' + +import App from './App' + +render( + <> + + + , + }, + ]} + /> + , + document.getElementById('root')!, +) +``` + +Finally add any additional configuration you desire to the `TanStackDevtools` component, more information can be found under the [TanStack Devtools Configuration](../../configuration.md) section. + +A complete working example can be found in our [basic example](https://tanstack.com/devtools/latest/docs/framework/preact/examples/basic). diff --git a/docs/framework/preact/guides/custom-plugins.md b/docs/framework/preact/guides/custom-plugins.md new file mode 100644 index 00000000..2bf63cf6 --- /dev/null +++ b/docs/framework/preact/guides/custom-plugins.md @@ -0,0 +1,204 @@ +--- +title: Custom plugins +id: custom-plugins +--- + +TanStack devtools allows you to create your own custom plugins by emitting and listening to our event bus. + +## Prerequisite + +This guide will walk you through a simple example where our library is a counter with a count history. A working example can be found in our [custom-plugin example](https://tanstack.com/devtools/latest/docs/framework/preact/examples/custom-plugin). + +This is our library code: + +counter.ts +```tsx +export function createCounter() { + let count = 0 + const history = [] + + return { + getCount: () => count, + increment: () => { + count++ + history.push(count) + }, + decrement: () => { + count-- + history.push(count) + }, + }; +} +``` + +## Event Client Setup + +Install the [TanStack Devtools Event Client](https://www.npmjs.com/package/@tanstack/devtools-event-client) utils. + +```bash +npm i @tanstack/devtools-event-client +``` + +First you will need to setup the `EventClient`. + +eventClient.ts +```tsx +import { EventClient } from '@tanstack/devtools-event-client' + + +type EventMap = { + // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The value is the expected type of the event payload + 'custom-devtools:counter-state': { count: number, history: number[] } +} + +class CustomEventClient extends EventClient { + constructor() { + super({ + // The pluginId must match that of the event map key + pluginId: 'custom-devtools', + }) + } +} + +// This is where the magic happens, it'll be used throughout your application. +export const DevtoolsEventClient = new CustomEventClient() +``` + +## Event Client Integration + +Now we need to hook our `EventClient` into the application code. This can be done in many way's, a useEffect that emits the current state, or a subscription to an observer, all that matters is that when you want to emit the current state you do the following. + +Our new library code will looks as follows: + +counter.ts +```tsx +import { DevtoolsEventClient } from './eventClient.ts' + +export function createCounter() { + let count = 0 + const history: Array = [] + + return { + getCount: () => count, + increment: () => { + count++ + history.push(count) + + // The emit eventSuffix must match that of the EventMap defined in eventClient.ts + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + decrement: () => { + count-- + history.push(count) + + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + } +} +``` + +> [!IMPORTANT] +> `EventClient` is framework agnostic so this process will be the same regardless of framework or even in vanilla JavaScript. + +## Consuming The Event Client + +Now we need to create our devtools panel, for a simple approach write the devtools in the framework that the adapter is, be aware that this will make the plugin framework specific. + +> Because TanStack is framework agnostic we have taken a more complicated approach that will be explained in coming docs (if framework agnosticism is not a concern to you, you can ignore this). + +DevtoolsPanel.tsx +```tsx +import { useEffect, useState } from 'preact/hooks' +import { DevtoolsEventClient } from './eventClient.ts' + +export function DevtoolPanel() { + const [state, setState] = useState<{ count: number; history: number[] } | undefined>() + + useEffect(() => { + // subscribe to the emitted event + const cleanup = DevtoolsEventClient.on("counter-state", e => setState(e.payload)) + return cleanup + }, []) + + return ( +
+
{state?.count}
+
{JSON.stringify(state?.history)}
+
+ ) +} +``` + +## Application Integration + +This step follows what's shown in [basic-setup](../basic-setup.md) for a more documented guide go check it out. As well as the complete [custom-devtools example](https://tanstack.com/devtools/latest/docs/framework/preact/examples/custom-devtools) in our examples section. + +Main.tsx +```tsx +import { render } from 'preact' +import { DevtoolPanel } from './DevtoolPanel' + +render( + <> + + + , + }, + ]} + /> + , + document.getElementById('root')!, +) + +``` + +## Debugging + +Both the `TanStackDevtools` component and the TanStack `EventClient` come with built in debug mode which will log to the console the emitted event as well as the EventClient status. + +TanStackDevtool's debugging mode can be activated like so: +```tsx +, + }, + ]} +/> +``` + +Where as the EventClient's debug mode can be activated by: +```tsx +class CustomEventClient extends EventClient { + constructor() { + super({ + pluginId: 'custom-devtools', + debug: true, + }) + } +} +``` + +Activating the debug mode will log to the console the current events that emitter has emitted or listened to. The EventClient will have appended `[tanstack-devtools:${pluginId}]` and the client will have appended `[tanstack-devtools:client-bus]`. + +Heres an example of both: +``` +🌴 [tanstack-devtools:client-bus] Initializing client event bus + +🌴 [tanstack-devtools:custom-devtools-plugin] Registered event to bus custom-devtools:counter-state +``` diff --git a/docs/framework/solid/reference/functions/tanstackdevtools.md b/docs/framework/solid/reference/functions/tanstackdevtools.md deleted file mode 100644 index 6ef8a24b..00000000 --- a/docs/framework/solid/reference/functions/tanstackdevtools.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: TanStackDevtools -title: TanStackDevtools ---- - - - -# Function: TanStackDevtools() - -```ts -function TanStackDevtools(props): any -``` - -Defined in: [index.ts:4](https://github.com/TanStack/devtools/blob/main/packages/solid-devtools/src/index.ts#L4) - -## Parameters - -### props - -`TanStackDevtoolsInit` & `object` - -## Returns - -`any` diff --git a/docs/framework/solid/reference/index.md b/docs/framework/solid/reference/index.md deleted file mode 100644 index ba505f55..00000000 --- a/docs/framework/solid/reference/index.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -id: "@tanstack/solid-devtools" -title: "@tanstack/solid-devtools" ---- - - - -# @tanstack/solid-devtools - -## Type Aliases - -- [TanStackDevtoolsSolidPlugin](type-aliases/tanstackdevtoolssolidplugin.md) - -## Functions - -- [TanStackDevtools](functions/tanstackdevtools.md) diff --git a/docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md b/docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md deleted file mode 100644 index 86e6ad8b..00000000 --- a/docs/framework/solid/reference/type-aliases/tanstackdevtoolssolidplugin.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -id: TanStackDevtoolsSolidPlugin -title: TanStackDevtoolsSolidPlugin ---- - - - -# Type Alias: TanStackDevtoolsSolidPlugin - -```ts -type TanStackDevtoolsSolidPlugin = Omit & object; -``` - -Defined in: [core.tsx:21](https://github.com/TanStack/devtools/blob/main/packages/solid-devtools/src/core.tsx#L21) - -## Type declaration - -### name - -```ts -name: string | 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: () => , - } -``` - -### render - -```ts -render: SolidPluginRender; -``` - -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: , - } -``` diff --git a/docs/reference/classes/tanstackdevtoolscore.md b/docs/reference/classes/tanstackdevtoolscore.md index 7c93c312..9152cb07 100644 --- a/docs/reference/classes/tanstackdevtoolscore.md +++ b/docs/reference/classes/tanstackdevtoolscore.md @@ -7,7 +7,7 @@ title: TanStackDevtoolsCore # Class: TanStackDevtoolsCore -Defined in: [devtools/src/core.tsx:42](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L42) +Defined in: [devtools/src/core.tsx:43](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L43) ## Constructors @@ -17,13 +17,13 @@ Defined in: [devtools/src/core.tsx:42](https://github.com/TanStack/devtools/blob new TanStackDevtoolsCore(init): TanStackDevtoolsCore ``` -Defined in: [devtools/src/core.tsx:53](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L53) +Defined in: [devtools/src/core.tsx:55](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L55) #### Parameters ##### init -[`TanStackDevtoolsInit`](./interfaces/tanstackdevtoolsinit.md) +[`TanStackDevtoolsInit`](../interfaces/tanstackdevtoolsinit.md) #### Returns @@ -37,7 +37,7 @@ Defined in: [devtools/src/core.tsx:53](https://github.com/TanStack/devtools/blob mount(el): void ``` -Defined in: [devtools/src/core.tsx:62](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L62) +Defined in: [devtools/src/core.tsx:64](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L64) #### Type Parameters @@ -61,13 +61,13 @@ Defined in: [devtools/src/core.tsx:62](https://github.com/TanStack/devtools/blob setConfig(config): void ``` -Defined in: [devtools/src/core.tsx:94](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L94) +Defined in: [devtools/src/core.tsx:107](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L107) #### Parameters ##### config -`Partial`\<[`TanStackDevtoolsInit`](./interfaces/tanstackdevtoolsinit.md)\> +`Partial`\<[`TanStackDevtoolsInit`](../interfaces/tanstackdevtoolsinit.md)\> #### Returns @@ -81,7 +81,7 @@ Defined in: [devtools/src/core.tsx:94](https://github.com/TanStack/devtools/blob unmount(): void ``` -Defined in: [devtools/src/core.tsx:85](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L85) +Defined in: [devtools/src/core.tsx:98](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L98) #### Returns diff --git a/docs/reference/interfaces/tanstackdevtoolsinit.md b/docs/reference/interfaces/tanstackdevtoolsinit.md index b42e757e..eeb4e5f6 100644 --- a/docs/reference/interfaces/tanstackdevtoolsinit.md +++ b/docs/reference/interfaces/tanstackdevtoolsinit.md @@ -7,7 +7,7 @@ title: TanStackDevtoolsInit # Interface: TanStackDevtoolsInit -Defined in: [devtools/src/core.tsx:12](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L12) +Defined in: [devtools/src/core.tsx:13](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L13) ## Properties @@ -15,17 +15,20 @@ Defined in: [devtools/src/core.tsx:12](https://github.com/TanStack/devtools/blob ```ts optional config: Partial<{ + customTrigger: (el, props) => void; defaultOpen: boolean; hideUntilHover: boolean; openHotkey: KeyboardKey[]; panelLocation: "top" | "bottom"; position: TriggerPosition; requireUrlFlag: boolean; + theme: "light" | "dark"; + triggerHidden: boolean; urlFlag: string; }>; ``` -Defined in: [devtools/src/core.tsx:18](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L18) +Defined in: [devtools/src/core.tsx:19](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L19) 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, @@ -39,7 +42,7 @@ the settings are persisted in local storage and changed through the settings pan optional eventBusConfig: ClientEventBusConfig; ``` -Defined in: [devtools/src/core.tsx:39](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L39) +Defined in: [devtools/src/core.tsx:40](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L40) *** @@ -49,7 +52,7 @@ Defined in: [devtools/src/core.tsx:39](https://github.com/TanStack/devtools/blob optional plugins: TanStackDevtoolsPlugin[]; ``` -Defined in: [devtools/src/core.tsx:38](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L38) +Defined in: [devtools/src/core.tsx:39](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/core.tsx#L39) Array of plugins to be used in the devtools. Each plugin has a `render` function that gives you the dom node to mount into diff --git a/docs/reference/interfaces/tanstackdevtoolsplugin.md b/docs/reference/interfaces/tanstackdevtoolsplugin.md index 919639e2..bce835d0 100644 --- a/docs/reference/interfaces/tanstackdevtoolsplugin.md +++ b/docs/reference/interfaces/tanstackdevtoolsplugin.md @@ -7,17 +7,56 @@ title: TanStackDevtoolsPlugin # Interface: TanStackDevtoolsPlugin -Defined in: [devtools/src/context/devtools-context.tsx:14](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L14) +Defined in: [devtools/src/context/devtools-context.tsx:15](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L15) ## Properties +### defaultOpen? + +```ts +optional defaultOpen: boolean; +``` + +Defined in: [devtools/src/context/devtools-context.tsx:58](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L58) + +Whether the plugin should be open by default when there are no active plugins. +If true, this plugin will be added to activePlugins on initial load when activePlugins is empty. + +#### Default + +```ts +false +``` + +*** + +### destroy()? + +```ts +optional destroy: (pluginId) => void; +``` + +Defined in: [devtools/src/context/devtools-context.tsx:77](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L77) + +#### Parameters + +##### pluginId + +`string` + +#### Returns + +`void` + +*** + ### id? ```ts optional id: string; ``` -Defined in: [devtools/src/context/devtools-context.tsx:46](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L46) +Defined in: [devtools/src/context/devtools-context.tsx:52](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L52) Unique identifier for the plugin. If not provided, it will be generated based on the name. @@ -27,10 +66,10 @@ If not provided, it will be generated based on the name. ### name ```ts -name: string | (el) => void; +name: string | (el, theme) => void; ``` -Defined in: [devtools/src/context/devtools-context.tsx:41](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L41) +Defined in: [devtools/src/context/devtools-context.tsx:42](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L42) Name to be displayed in the devtools UI. If a string, it will be used as the plugin name. @@ -62,10 +101,10 @@ or ### render() ```ts -render: (el) => void; +render: (el, theme) => void; ``` -Defined in: [devtools/src/context/devtools-context.tsx:60](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L60) +Defined in: [devtools/src/context/devtools-context.tsx:72](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L72) Render the plugin UI by using the provided element. This function will be called when the plugin tab is clicked and expected to be mounted. @@ -78,6 +117,10 @@ when the plugin tab is clicked and expected to be mounted. The mount element for the plugin. +##### theme + +`"light"` | `"dark"` + #### Returns `void` diff --git a/docs/reference/type-aliases/tanstackdevtoolsconfig.md b/docs/reference/type-aliases/tanstackdevtoolsconfig.md index ff1f00d4..dbe96c72 100644 --- a/docs/reference/type-aliases/tanstackdevtoolsconfig.md +++ b/docs/reference/type-aliases/tanstackdevtoolsconfig.md @@ -11,4 +11,4 @@ title: TanStackDevtoolsConfig type TanStackDevtoolsConfig = DevtoolsStore["settings"]; ``` -Defined in: [devtools/src/context/devtools-context.tsx:124](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L124) +Defined in: [devtools/src/context/devtools-context.tsx:182](https://github.com/TanStack/devtools/blob/main/packages/devtools/src/context/devtools-context.tsx#L182) diff --git a/examples/preact/basic/.eslintrc.cjs b/examples/preact/basic/.eslintrc.cjs new file mode 100644 index 00000000..9ff0b9fc --- /dev/null +++ b/examples/preact/basic/.eslintrc.cjs @@ -0,0 +1,13 @@ +// @ts-check + +/** @type {import('eslint').Linter.Config} */ +const config = { + settings: { + extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'], + rules: { + 'react/no-children-prop': 'off', + }, + }, +} + +module.exports = config diff --git a/examples/preact/basic/.gitignore b/examples/preact/basic/.gitignore new file mode 100644 index 00000000..4673b022 --- /dev/null +++ b/examples/preact/basic/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +pnpm-lock.yaml +yarn.lock +package-lock.json + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/preact/basic/README.md b/examples/preact/basic/README.md new file mode 100644 index 00000000..1cf88926 --- /dev/null +++ b/examples/preact/basic/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` +- `npm run dev` diff --git a/examples/preact/basic/index.html b/examples/preact/basic/index.html new file mode 100644 index 00000000..1c96ec87 --- /dev/null +++ b/examples/preact/basic/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + Basic Example - TanStack Devtools + A basic example of using TanStack Devtools with React. + + + +
+ + + diff --git a/examples/preact/basic/package.json b/examples/preact/basic/package.json new file mode 100644 index 00000000..ad8f091b --- /dev/null +++ b/examples/preact/basic/package.json @@ -0,0 +1,38 @@ +{ + "name": "@tanstack/devtools-example-preact-basic", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3005", + "build": "vite build", + "preview": "vite preview", + "test:types": "tsc" + }, + "dependencies": { + "@tanstack/devtools-client": "0.0.4", + "@tanstack/devtools-event-client": "0.3.5", + "@tanstack/preact-devtools": "workspace:*", + "preact": "^10.28.0", + "zod": "^4.1.11" + }, + "devDependencies": { + "@preact/preset-vite": "^2.10.2", + "@tanstack/devtools-ui": "0.4.4", + "@tanstack/devtools-vite": "0.3.11", + "sonda": "0.9.0", + "vite": "^7.1.7", + "vite-plugin-inspect": "11.3.3" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/examples/preact/basic/public/emblem-light.svg b/examples/preact/basic/public/emblem-light.svg new file mode 100644 index 00000000..a58e69ad --- /dev/null +++ b/examples/preact/basic/public/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/preact/basic/src/button-with-props-only.tsx b/examples/preact/basic/src/button-with-props-only.tsx new file mode 100644 index 00000000..0b79cfee --- /dev/null +++ b/examples/preact/basic/src/button-with-props-only.tsx @@ -0,0 +1,5 @@ +import { Button } from './button' + +export const ButtonWithProps = (props: { children: React.ReactNode }) => { + return + With props + + ) +} diff --git a/examples/preact/basic/src/index.tsx b/examples/preact/basic/src/index.tsx new file mode 100644 index 00000000..f56045ba --- /dev/null +++ b/examples/preact/basic/src/index.tsx @@ -0,0 +1,190 @@ +import { render, createContext } from 'preact' +import { useContext, useState, useEffect } from 'preact/hooks' +import Devtools from './setup' +import { queryPlugin } from './plugin' +import { Button } from './button' +import { Feature } from './feature' + +type Post = { + id: number + title: string + body: string +} + +function Posts({ + setPostId, +}: { + setPostId: (id: number) => void +}) { + const [posts, setPosts] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch('https://jsonplaceholder.typicode.com/posts') + .then((res) => res.json()) + .then((data) => { + setPosts(data) + setLoading(false) + }) + .catch((err) => { + setError(err) + setLoading(false) + }) + }, []) + + return ( +
+

Posts

+
+ {loading ? ( + 'Loading...' + ) : error ? ( + Error: {error.message} + ) : ( + <> +
+ {posts.map((post) => ( +

+ setPostId(post.id)} + href="#" + > + {post.title} + +

+ ))} +
+ + )} +
+
+ ) +} + +const getPostById = async (id: number): Promise => { + const response = await fetch( + `https://jsonplaceholder.typicode.com/posts/${id}`, + ) + return await response.json() +} + +function Post({ + postId, + setPostId, +}: { + postId: number + setPostId: (id: number) => void +}) { + const [post, setPost] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + if (postId > 0) { + setLoading(true) + getPostById(postId) + .then((data) => { + setPost(data) + setLoading(false) + }) + .catch((err) => { + setError(err) + setLoading(false) + }) + } + }, [postId]) + + return ( +
+ + {!postId || loading ? ( + 'Loading...' + ) : error ? ( + Error: {error.message} + ) : post ? ( + <> +

{post.title}

+
+

{post.body}

+
+ + ) : null} +
+ ) +} + +const Context = createContext<{ + count: number + setCount: (count: number) => void +}>({ count: 0, setCount: () => {} }) + +setTimeout(() => { + queryPlugin.emit('test', { + title: 'Test Event', + description: + 'This is a test event from the TanStack Query Devtools plugin.', + }) +}, 1000) + +queryPlugin.on('test', (event) => { + console.log('Received test event:', event) +}) + +function Mounted() { + const c = useContext(Context) + console.log(c) + return ( +

{ + c.setCount(c.count + 1) + }} + > + {c.count} +


+

+ ) +} + +function App() { + const [state, setState] = useState(1) + const [win, setWin] = useState(null) + const [postId, setPostId] = useState(-1) + return ( +
+ +

TanStack Devtools Preact Basic Example

+ current count: {state} + + + {win && render(, win.document.body)} + +

+ As you visit the posts below, you will notice them in a loading + state the first time you load them. However, after you return to + this list and click on any posts you have already visited again, you + will see them load instantly and background refresh right before + your eyes!{' '} + + (You may need to throttle your network speed to simulate longer + loading sequences) + +

+ {postId > -1 ? ( + + ) : ( + + )} + +
+
+ ) +} + +render(, document.getElementById('root')!) diff --git a/examples/preact/basic/src/package-json-panel.tsx b/examples/preact/basic/src/package-json-panel.tsx new file mode 100644 index 00000000..ed61e10e --- /dev/null +++ b/examples/preact/basic/src/package-json-panel.tsx @@ -0,0 +1,340 @@ +import { devtoolsEventClient } from '@tanstack/devtools-client' +import { useEffect, useState } from 'preact/hooks' + +export const PackageJsonPanel = () => { + const [packageJson, setPackageJson] = useState(null) + const [outdatedDeps, setOutdatedDeps] = useState< + Record< + string, + { + current: string + wanted: string + latest: string + type?: 'dependencies' | 'devDependencies' + } + > + >({}) + + useEffect(() => { + devtoolsEventClient.emit('mounted', undefined as any) + const cleanupOutdated = devtoolsEventClient.on( + 'outdated-deps-read', + (event) => { + setOutdatedDeps(event.payload.outdatedDeps || {}) + }, + ) + const cleanupPackageJson = devtoolsEventClient.on( + 'package-json-read', + (event) => { + console.log('package-json-read', event) + setPackageJson(event.payload.packageJson) + }, + ) + return () => { + cleanupOutdated() + cleanupPackageJson() + } + }, []) + + const hasOutdated = Object.keys(outdatedDeps).length > 0 + + // Helpers + const stripRange = (v?: string) => (v ?? '').replace(/^[~^><=v\s]*/, '') + const parseSemver = (v?: string) => { + const s = stripRange(v) + const m = s.match(/^(\d+)\.(\d+)\.(\d+)/) + if (!m) return null + return { major: +m[1], minor: +m[2], patch: +m[3] } + } + const diffType = ( + current?: string, + latest?: string, + ): 'major' | 'minor' | 'patch' | null => { + const c = parseSemver(current) + const l = parseSemver(latest) + if (!c || !l) return null + if (l.major > c.major) return 'major' + if (l.major === c.major && l.minor > c.minor) return 'minor' + if (l.major === c.major && l.minor === c.minor && l.patch > c.patch) + return 'patch' + return null + } + const diffColor: Record<'major' | 'minor' | 'patch', string> = { + major: '#ef4444', + minor: '#f59e0b', + patch: '#10b981', + } + + const containerStyle = { padding: 10 } + const metaStyle = { + display: 'grid', + gridTemplateColumns: 'auto 1fr', + gap: 6, + marginBottom: 8, + } + const sectionStyle = { + margin: '8px 0', + padding: '8px', + border: '1px solid #444', + borderRadius: 6, + } + const tableStyle = { + width: '100%', + borderCollapse: 'collapse', + } + const thtd = { + borderBottom: '1px solid #333', + padding: '4px 6px', + textAlign: 'left', + } + const badge = (text: string, color: string) => ( + + {text} + + ) + const btn = ( + label: string, + onClick: () => void, + variant: 'primary' | 'ghost' = 'primary', + ) => ( + + ) + + const VersionCell = ({ + dep, + specified, + }: { + dep: string + specified: string + }) => { + const info = outdatedDeps[dep] as + | { + current: string + wanted: string + latest: string + type?: 'dependencies' | 'devDependencies' + } + | undefined + const current = info?.current ?? specified + const latest = info?.latest + const dt = info ? diffType(current, latest) : null + return ( +
+ {current} + {dt && latest ? ( + + → + {badge(`latest ${latest}`, diffColor[dt])} + + ) : null} +
+ ) + } + + const UpgradeRowActions = ({ name }: { name: string }) => { + const info = outdatedDeps[name] as + | { + current: string + wanted: string + latest: string + type?: 'dependencies' | 'devDependencies' + } + | undefined + if (!info) return null + return ( +
+ {btn('Wanted', () => + (devtoolsEventClient as any).emit('upgrade-dependency', { + name, + target: info.wanted, + } as any), + )} + {btn( + 'Latest', + () => + (devtoolsEventClient as any).emit('upgrade-dependency', { + name, + target: info.latest, + } as any), + 'ghost', + )} +
+ ) + } + + const makeLists = (names?: Array) => { + const entries = Object.entries(outdatedDeps).filter( + ([n]) => !names || names.includes(n), + ) + const wantedList = entries.map(([name, info]) => ({ + name, + target: info.wanted, + })) + const latestList = entries.map(([name, info]) => ({ + name, + target: info.latest, + })) + return { wantedList, latestList } + } + + const BulkActions = ({ names }: { names?: Array }) => { + const { wantedList, latestList } = makeLists(names) + if (wantedList.length === 0 && latestList.length === 0) return null + return ( +
+ {btn('All → wanted', () => + (devtoolsEventClient as any).emit('upgrade-dependencies-bulk', { + list: wantedList, + } as any), + )} + {btn( + 'All → latest', + () => + (devtoolsEventClient as any).emit('upgrade-dependencies-bulk', { + list: latestList, + } as any), + 'ghost', + )} +
+ ) + } + + const renderDeps = (title: string, deps?: Record) => { + const names = Object.keys(deps || {}) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const someOutdatedInSection = names.some((n) => !!outdatedDeps[n]) + return ( +
+
+

{title}

+ {someOutdatedInSection ? : null} +
+ + + + + + + + + + + {Object.entries(deps || {}).map(([dep, version]) => { + const info = outdatedDeps[dep] as + | { + current: string + wanted: string + latest: string + type?: 'dependencies' | 'devDependencies' + } + | undefined + const isOutdated = !!info && info.current !== info.latest + return ( + + + + + + + ) + })} + +
PackageVersionStatusActions
{dep} + + + {isOutdated + ? badge('Outdated', '#e11d48') + : badge('OK', '#10b981')} + + {isOutdated ? : null} +
+
+ ) + } + + return ( +
+

Package.json

+ {packageJson ? ( +
+
+

+ Package info +

+
+
+ Name +
+
{packageJson.name}
+
+ Version +
+
v{packageJson.version}
+
+ Description +
+
{packageJson.description}
+
+ Author +
+
{packageJson.author}
+
+ License +
+
{packageJson.license}
+
+ Repository +
+
{packageJson.repository?.url || packageJson.repository}
+
+
+ {renderDeps('Dependencies', packageJson.dependencies)} + {renderDeps('Dev Dependencies', packageJson.devDependencies)} +
+

+ Outdated (All) +

+ {hasOutdated ? ( + + ) : ( +

All dependencies are up to date.

+ )} +
+
+ ) : ( +

No package.json data available

+ )} +
+ ) +} diff --git a/examples/preact/basic/src/plugin.ts b/examples/preact/basic/src/plugin.ts new file mode 100644 index 00000000..8bac08e3 --- /dev/null +++ b/examples/preact/basic/src/plugin.ts @@ -0,0 +1,25 @@ +import { EventClient } from '@tanstack/devtools-event-client' + +interface EventMap { + 'query-devtools:test': { + title: string + description: string + } + 'query-devtools:init': { + title: string + description: string + } + 'query-devtools:query': { + title: string + description: string + } +} +class QueryDevtoolsPlugin extends EventClient { + constructor() { + super({ + pluginId: 'query-devtools', + }) + } +} + +export const queryPlugin = new QueryDevtoolsPlugin() diff --git a/examples/preact/basic/src/setup.tsx b/examples/preact/basic/src/setup.tsx new file mode 100644 index 00000000..54cbc4f0 --- /dev/null +++ b/examples/preact/basic/src/setup.tsx @@ -0,0 +1,20 @@ +import { TanStackDevtools } from '@tanstack/preact-devtools' +import { PackageJsonPanel } from './package-json-panel' + +export default function DevtoolsExample() { + return ( + <> + , + }, + ]} + /> + + ) +} diff --git a/examples/preact/basic/tsconfig.json b/examples/preact/basic/tsconfig.json new file mode 100644 index 00000000..857b1ed4 --- /dev/null +++ b/examples/preact/basic/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "vite.config.ts"] +} diff --git a/examples/preact/basic/vite.config.ts b/examples/preact/basic/vite.config.ts new file mode 100644 index 00000000..f0f0f741 --- /dev/null +++ b/examples/preact/basic/vite.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' +import { devtools } from '@tanstack/devtools-vite' +import Inspect from 'vite-plugin-inspect' +import sonda from 'sonda/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + devtools({ + removeDevtoolsOnBuild: true, + }), + + Inspect(), + sonda(), + preact(), + ], + build: { + sourcemap: true, + }, +}) diff --git a/examples/preact/custom-devtools/.eslintrc.cjs b/examples/preact/custom-devtools/.eslintrc.cjs new file mode 100644 index 00000000..35853b61 --- /dev/null +++ b/examples/preact/custom-devtools/.eslintrc.cjs @@ -0,0 +1,11 @@ +// @ts-check + +/** @type {import('eslint').Linter.Config} */ +const config = { + extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'], + rules: { + 'react/no-children-prop': 'off', + }, +} + +module.exports = config diff --git a/examples/preact/custom-devtools/.gitignore b/examples/preact/custom-devtools/.gitignore new file mode 100644 index 00000000..4673b022 --- /dev/null +++ b/examples/preact/custom-devtools/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +pnpm-lock.yaml +yarn.lock +package-lock.json + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/preact/custom-devtools/README.md b/examples/preact/custom-devtools/README.md new file mode 100644 index 00000000..1cf88926 --- /dev/null +++ b/examples/preact/custom-devtools/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` +- `npm run dev` diff --git a/examples/preact/custom-devtools/index.html b/examples/preact/custom-devtools/index.html new file mode 100644 index 00000000..b688bebf --- /dev/null +++ b/examples/preact/custom-devtools/index.html @@ -0,0 +1,16 @@ + + + + + + + + + Custom Devtools - TanStack Devtools + + + +
+ + + diff --git a/examples/preact/custom-devtools/package.json b/examples/preact/custom-devtools/package.json new file mode 100644 index 00000000..a7b2ffc2 --- /dev/null +++ b/examples/preact/custom-devtools/package.json @@ -0,0 +1,32 @@ +{ + "name": "@tanstack/devtools-custom-devtools-preact", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3001", + "build": "vite build", + "preview": "vite preview", + "test:types": "tsc" + }, + "dependencies": { + "@tanstack/devtools-event-client": "0.3.5", + "@tanstack/preact-devtools": "workspace:*", + "preact": "^10.28.0" + }, + "devDependencies": { + "@preact/preset-vite": "^2.10.2", + "vite": "^7.1.7" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/examples/preact/custom-devtools/public/emblem-light.svg b/examples/preact/custom-devtools/public/emblem-light.svg new file mode 100644 index 00000000..a58e69ad --- /dev/null +++ b/examples/preact/custom-devtools/public/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/preact/custom-devtools/src/App.tsx b/examples/preact/custom-devtools/src/App.tsx new file mode 100644 index 00000000..6a284df4 --- /dev/null +++ b/examples/preact/custom-devtools/src/App.tsx @@ -0,0 +1,30 @@ +import { useState } from 'preact/hooks' + +import { createCounter } from './counter' + +const counterInstance = createCounter() + +export default function App() { + const [count, setCount] = useState(counterInstance.getCount()) + + const increment = () => { + counterInstance.increment() + setCount(counterInstance.getCount()) + } + + const decrement = () => { + counterInstance.decrement() + setCount(counterInstance.getCount()) + } + + return ( +
+

Custom plugins

+

Count: {count}

+
+ + +
+
+ ) +} diff --git a/examples/preact/custom-devtools/src/CustomDevtoolsPanel.tsx b/examples/preact/custom-devtools/src/CustomDevtoolsPanel.tsx new file mode 100644 index 00000000..e4a975b5 --- /dev/null +++ b/examples/preact/custom-devtools/src/CustomDevtoolsPanel.tsx @@ -0,0 +1,23 @@ +import { useEffect, useState } from 'preact/hooks' +import { DevtoolsEventClient } from './eventClient.ts' + +export default function CustomDevtoolPanel() { + const [state, setState] = useState< + { count: number; history: Array } | undefined + >() + + useEffect(() => { + // subscribe to the emitted event + const cleanup = DevtoolsEventClient.on('counter-state', (e) => + setState(e.payload), + ) + return cleanup + }, []) + + return ( +
+
counter state: {state?.count}
+
counter history: {JSON.stringify(state?.history)}
+
+ ) +} diff --git a/examples/preact/custom-devtools/src/counter.ts b/examples/preact/custom-devtools/src/counter.ts new file mode 100644 index 00000000..9d8db1e0 --- /dev/null +++ b/examples/preact/custom-devtools/src/counter.ts @@ -0,0 +1,29 @@ +import { DevtoolsEventClient } from './eventClient.ts' + +export function createCounter() { + let count = 0 + const history: Array = [] + + return { + getCount: () => count, + increment: () => { + count++ + history.push(count) + + // The emit eventSuffix must match that of the EventMap defined in eventClient.ts + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + decrement: () => { + count-- + history.push(count) + + DevtoolsEventClient.emit('counter-state', { + count, + history, + }) + }, + } +} diff --git a/examples/preact/custom-devtools/src/eventClient.ts b/examples/preact/custom-devtools/src/eventClient.ts new file mode 100644 index 00000000..d884641d --- /dev/null +++ b/examples/preact/custom-devtools/src/eventClient.ts @@ -0,0 +1,20 @@ +import { EventClient } from '@tanstack/devtools-event-client' + +type EventMap = { + // The key of the event map is a combination of {pluginId}:{eventSuffix} + // The value is the expected type of the event payload + 'custom-devtools:counter-state': { count: number; history: Array } +} + +class CustomEventClient extends EventClient { + constructor() { + super({ + // The pluginId must match that of the event map key + pluginId: 'custom-devtools', + debug: true, + }) + } +} + +// This is where the magic happens, it'll be used throughout your application. +export const DevtoolsEventClient = new CustomEventClient() diff --git a/examples/preact/custom-devtools/src/index.tsx b/examples/preact/custom-devtools/src/index.tsx new file mode 100644 index 00000000..1ea17de2 --- /dev/null +++ b/examples/preact/custom-devtools/src/index.tsx @@ -0,0 +1,22 @@ +import { render } from 'preact' +import { TanStackDevtools } from '@tanstack/preact-devtools' + +import App from './App' +import CustomDevtoolPanel from './CustomDevtoolsPanel' + +render( + <> + + + , + }, + ]} + /> + , + document.getElementById('root')!, +) diff --git a/examples/preact/custom-devtools/tsconfig.json b/examples/preact/custom-devtools/tsconfig.json new file mode 100644 index 00000000..ef94db5e --- /dev/null +++ b/examples/preact/custom-devtools/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/examples/preact/custom-devtools/vite.config.ts b/examples/preact/custom-devtools/vite.config.ts new file mode 100644 index 00000000..cb76bf8a --- /dev/null +++ b/examples/preact/custom-devtools/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' + +export default defineConfig({ + plugins: [preact()], +}) + diff --git a/package.json b/package.json index 9155550d..c8d90d6a 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "overrides": { "@tanstack/devtools": "workspace:*", "@tanstack/react-devtools": "workspace:*", + "@tanstack/preact-devtools": "workspace:*", "@tanstack/solid-devtools": "workspace:*", "@tanstack/devtools-vite": "workspace:*" } diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index 7067a2e9..b0922ed2 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -25,6 +25,12 @@ "default": "./dist/react/esm/index.js" } }, + "./preact": { + "import": { + "types": "./dist/preact/esm/index.d.ts", + "default": "./dist/preact/esm/index.js" + } + }, "./solid": { "browser": { "development": { @@ -52,15 +58,19 @@ }, "peerDependencies": { "@types/react": ">=17.0.0", + "preact": ">=10.0.0", "react": ">=17.0.0", "solid-js": ">=1.9.7", "vue": ">=3.2.0" }, "peerDependenciesMeta": { - "react": { + "@types/react": { "optional": true }, - "@types/react": { + "preact": { + "optional": true + }, + "react": { "optional": true }, "solid-js": { @@ -82,7 +92,7 @@ "test:lib:dev": "pnpm test:lib --watch", "test:types": "tsc", "test:build": "publint --strict", - "build": "vite build && tsup " + "build": "vite build && vite build --config vite.config.preact.ts && tsup " }, "devDependencies": { "tsup": "^8.5.0", diff --git a/packages/devtools-utils/src/preact/index.ts b/packages/devtools-utils/src/preact/index.ts new file mode 100644 index 00000000..90646448 --- /dev/null +++ b/packages/devtools-utils/src/preact/index.ts @@ -0,0 +1,3 @@ +export * from './panel' +export * from './plugin' + diff --git a/packages/devtools-utils/src/preact/panel.tsx b/packages/devtools-utils/src/preact/panel.tsx new file mode 100644 index 00000000..3e5af1c0 --- /dev/null +++ b/packages/devtools-utils/src/preact/panel.tsx @@ -0,0 +1,60 @@ +/** @jsxImportSource preact */ + +import { useEffect, useRef } from 'preact/hooks' + +export interface DevtoolsPanelProps { + theme?: 'light' | 'dark' +} + +/** + * Creates a Preact component that dynamically imports and mounts a devtools panel. SSR friendly. + * @param devtoolsPackageName The name of the devtools package to be imported, e.g., '@tanstack/devtools-preact' + * @param importName The name of the export to be imported from the devtools package (e.g., 'default' or 'DevtoolsCore') + * @returns A Preact component that mounts the devtools + * @example + * ```tsx + * // if the export is default + * const [PreactDevtoolsPanel, NoOpPreactDevtoolsPanel] = createPreactPanel('@tanstack/devtools-preact') + * ``` + * + * @example + * ```tsx + * // if the export is named differently + * const [PreactDevtoolsPanel, NoOpPreactDevtoolsPanel] = createPreactPanel('@tanstack/devtools-preact', 'DevtoolsCore') + * ``` + */ +export function createPreactPanel< + TComponentProps extends DevtoolsPanelProps | undefined, + TCoreDevtoolsClass extends { + mount: (el: HTMLElement, theme: 'light' | 'dark') => void + unmount: () => void + }, +>(CoreClass: new () => TCoreDevtoolsClass) { + function Panel(props: TComponentProps) { + const devToolRef = useRef(null) + const devtools = useRef(null) + useEffect(() => { + if (devtools.current) return + + devtools.current = new CoreClass() + + if (devToolRef.current) { + devtools.current.mount(devToolRef.current, props?.theme ?? 'dark') + } + + return () => { + if (devToolRef.current) { + devtools.current?.unmount() + } + } + }, [props?.theme]) + + return
+ } + + function NoOpPanel(_props: TComponentProps) { + return <> + } + return [Panel, NoOpPanel] as const +} + diff --git a/packages/devtools-utils/src/preact/plugin.tsx b/packages/devtools-utils/src/preact/plugin.tsx new file mode 100644 index 00000000..e472d71c --- /dev/null +++ b/packages/devtools-utils/src/preact/plugin.tsx @@ -0,0 +1,31 @@ +/** @jsxImportSource preact */ + +import type { JSX } from 'preact' +import type { DevtoolsPanelProps } from './panel' + +export function createPreactPlugin({ + Component, + ...config +}: { + name: string + id?: string + defaultOpen?: boolean + Component: (props: DevtoolsPanelProps) => JSX.Element +}) { + function Plugin() { + return { + ...config, + render: (_el: HTMLElement, theme: 'light' | 'dark') => ( + + ), + } + } + function NoOpPlugin() { + return { + ...config, + render: (_el: HTMLElement, _theme: 'light' | 'dark') => <>, + } + } + return [Plugin, NoOpPlugin] as const +} + diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json index acf9bbd2..951ea176 100644 --- a/packages/devtools-utils/tsconfig.json +++ b/packages/devtools-utils/tsconfig.json @@ -9,6 +9,7 @@ "tsup.config.ts", "eslint.config.js", "vite.config.ts", + "vite.config.preact.ts", "tests", "vite.config.solid.ts" ] diff --git a/packages/devtools-utils/vite.config.preact.ts b/packages/devtools-utils/vite.config.preact.ts new file mode 100644 index 00000000..1132fc37 --- /dev/null +++ b/packages/devtools-utils/vite.config.preact.ts @@ -0,0 +1,26 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { tanstackViteConfig } from '@tanstack/vite-config' +import packageJson from './package.json' + +const config = defineConfig({ + plugins: [], + test: { + name: packageJson.name, + dir: './', + watch: false, + environment: 'jsdom', + setupFiles: ['./tests/test-setup.ts'], + globals: true, + }, +}) + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: ['./src/preact/index.ts'], + srcDir: './src/preact', + outDir: './dist/preact', + cjs: false, + }), +) + diff --git a/packages/preact-devtools/CHANGELOG.md b/packages/preact-devtools/CHANGELOG.md new file mode 100644 index 00000000..5f9f50ef --- /dev/null +++ b/packages/preact-devtools/CHANGELOG.md @@ -0,0 +1,8 @@ +# @tanstack/preact-devtools + +## 0.8.2 + +### Patch Changes + +- Initial release of Preact devtools adapter + diff --git a/packages/preact-devtools/README.md b/packages/preact-devtools/README.md new file mode 100644 index 00000000..ed3fdc19 --- /dev/null +++ b/packages/preact-devtools/README.md @@ -0,0 +1,58 @@ +# @tanstack/preact-devtools + +This package is still under active development and might have breaking changes in the future. Please use it with caution. + +## Usage + +```tsx +import { TanStackDevtools } from '@tanstack/preact-devtools' +import { PreactQueryDevtoolsPanel } from '@tanstack/preact-query-devtools' +import { TanStackRouterDevtoolsPanel } from '@tanstack/preact-router-devtools' +import { QueryClient, QueryClientProvider } from '@tanstack/preact-query' +const queryClient = new QueryClient() +function App() { + return ( + +

My App

+ , + }, + { + name: 'TanStack Router', + render: , + }, + ]} + /> +
+ ) +} +``` + +## Creating plugins + +In order to create a plugin for TanStack Devtools, you can use the `plugins` prop of the `TanStackDevtools` component. Here's an example of how to create a simple plugin: + +```tsx +import { TanStackDevtools } from '@tanstack/preact-devtools' + +function App() { + return ( +
+

My App

+ , + }, + ]} + /> +
+ ) +} +``` + diff --git a/packages/preact-devtools/eslint.config.js b/packages/preact-devtools/eslint.config.js new file mode 100644 index 00000000..57ef959f --- /dev/null +++ b/packages/preact-devtools/eslint.config.js @@ -0,0 +1,19 @@ +// @ts-check + +import pluginReactHooks from 'eslint-plugin-react-hooks' +import rootConfig from '../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + ...rootConfig, + { + plugins: { + 'react-hooks': pluginReactHooks, + }, + rules: { + 'react-hooks/exhaustive-deps': 'error', + 'react-hooks/rules-of-hooks': 'error', + }, + }, +] + diff --git a/packages/preact-devtools/package.json b/packages/preact-devtools/package.json new file mode 100644 index 00000000..41628442 --- /dev/null +++ b/packages/preact-devtools/package.json @@ -0,0 +1,62 @@ +{ + "name": "@tanstack/preact-devtools", + "version": "0.8.2", + "description": "TanStack Devtools is a set of tools for building advanced devtools for your Preact application.", + "author": "Tanner Linsley", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TanStack/devtools.git", + "directory": "packages/preact-devtools" + }, + "homepage": "https://tanstack.com/devtools", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "preact", + "devtools" + ], + "type": "module", + "types": "dist/esm/index.d.ts", + "module": "dist/esm/index.js", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + } + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "files": [ + "dist", + "src" + ], + "scripts": { + "clean": "premove ./build ./dist", + "test:eslint": "eslint ./src", + "test:lib": "vitest --passWithNoTests", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc", + "test:build": "publint --strict", + "build": "vite build" + }, + "dependencies": { + "@tanstack/devtools": "workspace:*" + }, + "devDependencies": { + "@preact/preset-vite": "^2.10.2", + "eslint-plugin-react-hooks": "^7.0.1", + "preact": "^10.28.0" + }, + "peerDependencies": { + "preact": ">=10.0.0" + } +} + diff --git a/packages/preact-devtools/src/devtools.tsx b/packages/preact-devtools/src/devtools.tsx new file mode 100644 index 00000000..6d3d8e78 --- /dev/null +++ b/packages/preact-devtools/src/devtools.tsx @@ -0,0 +1,308 @@ +import { useEffect, useMemo, useRef, useState } from 'preact/hooks' +import { render } from 'preact' +import { TanStackDevtoolsCore } from '@tanstack/devtools' +import type { JSX } from 'preact' +import type { + ClientEventBusConfig, + TanStackDevtoolsConfig, + TanStackDevtoolsPlugin, +} from '@tanstack/devtools' + +type PluginRender = + | JSX.Element + | ((el: HTMLElement, theme: 'dark' | 'light') => JSX.Element) + +type TriggerProps = { + theme: 'dark' | 'light' +} + +type TriggerRender = + | JSX.Element + | ((el: HTMLElement, props: TriggerProps) => JSX.Element) + +export type TanStackDevtoolsPreactPlugin = Omit< + TanStackDevtoolsPlugin, + 'render' | 'name' +> & { + /** + * The render function can be a Preact element or a function that returns a Preact element. + * If it's a function, it will be called to render the plugin, otherwise it will be rendered directly. + * + * Example: + * ```jsx + * { + * render: () => , + * } + * ``` + * or + * ```jsx + * { + * render: , + * } + * ``` + */ + render: PluginRender + /** + * 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: + * ```jsx + * { + * name: "Your Plugin", + * render: () => , + * } + * ``` + * or + * ```jsx + * { + * name:

Your Plugin title

, + * render: () => , + * } + * ``` + */ + name: string | PluginRender +} + +type TanStackDevtoolsPreactConfig = Omit< + Partial, + 'customTrigger' +> & { + /** + * Optional custom trigger component for the devtools. + * It can be a Preact element or a function that renders one. + * + * Example: + * ```jsx + * { + * customTrigger: , + * } + * ``` + */ + customTrigger?: TriggerRender +} + +export interface TanStackDevtoolsPreactInit { + /** + * Array of plugins to be used in the devtools. + * Each plugin should have a `render` function that returns a Preact 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?: TanStackDevtoolsPreactConfig + /** + * Configuration for the TanStack Devtools client event bus. + */ + eventBusConfig?: ClientEventBusConfig +} + +// Simple portal component for Preact +function Portal({ + children, + container, +}: { + children: JSX.Element + container: HTMLElement +}) { + useEffect(() => { + render(children, container) + return () => { + render(null, container) + } + }, [children, container]) + + return null +} + +const convertRender = ( + Component: PluginRender, + setComponents: (value: Record | ((prev: Record) => Record)) => void, + e: HTMLElement, + theme: 'dark' | 'light', +) => { + const element = + typeof Component === 'function' ? Component(e, theme) : Component + + setComponents((prev) => ({ + ...prev, + [e.getAttribute('id') as string]: element, + })) +} + +const convertTrigger = ( + Component: TriggerRender, + setComponent: (component: JSX.Element | null) => void, + e: HTMLElement, + props: TriggerProps, +) => { + const element = + typeof Component === 'function' ? Component(e, props) : Component + setComponent(element) +} + +export const TanStackDevtools = ({ + plugins, + config, + eventBusConfig, +}: TanStackDevtoolsPreactInit): JSX.Element | null => { + const devToolRef = useRef(null) + + const [pluginContainers, setPluginContainers] = useState< + Record + >({}) + const [titleContainers, setTitleContainers] = useState< + Record + >({}) + const [triggerContainer, setTriggerContainer] = useState( + null, + ) + + const [PluginComponents, setPluginComponents] = useState< + Record + >({}) + const [TitleComponents, setTitleComponents] = useState< + Record + >({}) + const [TriggerComponent, setTriggerComponent] = useState( + null, + ) + + const pluginsMap: Array = useMemo( + () => + plugins?.map((plugin) => { + return { + ...plugin, + name: + 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, + ) + }, + render: (e, theme) => { + const id = e.getAttribute('id')! + const target = e.ownerDocument.getElementById(id) + + if (target) { + setPluginContainers((prev) => ({ + ...prev, + [id]: e, + })) + } + + convertRender(plugin.render, setPluginComponents, e, theme) + }, + } + }) ?? [], + [plugins], + ) + + const [devtools] = useState(() => { + const { customTrigger, ...coreConfig } = config || {} + return new TanStackDevtoolsCore({ + config: { + ...coreConfig, + customTrigger: customTrigger + ? (el, props) => { + setTriggerContainer(el) + convertTrigger(customTrigger, setTriggerComponent, el, props) + } + : undefined, + }, + eventBusConfig, + plugins: pluginsMap, + }) + }) + + useEffect(() => { + devtools.setConfig({ + plugins: pluginsMap, + }) + }, [devtools, pluginsMap]) + + useEffect(() => { + if (devToolRef.current) { + devtools.mount(devToolRef.current) + } + + return () => devtools.unmount() + }, [devtools]) + + const hasPlugins = + Object.values(pluginContainers).length > 0 && + Object.values(PluginComponents).length > 0 + const hasTitles = + Object.values(titleContainers).length > 0 && + Object.values(TitleComponents).length > 0 + + return ( + <> +
+ + {hasPlugins + ? Object.entries(pluginContainers).map(([key, pluginContainer]) => { + const component = PluginComponents[key] + return component ? ( + + {component} + + ) : null + }) + : null} + + {hasTitles + ? Object.entries(titleContainers).map(([key, titleContainer]) => { + const component = TitleComponents[key] + return component ? ( + + {component} + + ) : null + }) + : null} + + {triggerContainer && TriggerComponent ? ( + {TriggerComponent} + ) : null} + + ) +} + diff --git a/packages/preact-devtools/src/index.ts b/packages/preact-devtools/src/index.ts new file mode 100644 index 00000000..cd1cdd06 --- /dev/null +++ b/packages/preact-devtools/src/index.ts @@ -0,0 +1,11 @@ +'use client' + +import * as Devtools from './devtools' + +export const TanStackDevtools = Devtools.TanStackDevtools + +export type { + TanStackDevtoolsPreactPlugin, + TanStackDevtoolsPreactInit, +} from './devtools' + diff --git a/packages/preact-devtools/tsconfig.docs.json b/packages/preact-devtools/tsconfig.docs.json new file mode 100644 index 00000000..21a85de7 --- /dev/null +++ b/packages/preact-devtools/tsconfig.docs.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "paths": { + "@tanstack/devtools": ["../devtools/src"] + } + }, + "include": ["src"] +} + diff --git a/packages/preact-devtools/tsconfig.json b/packages/preact-devtools/tsconfig.json new file mode 100644 index 00000000..27be25e0 --- /dev/null +++ b/packages/preact-devtools/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "preact" + }, + "include": ["src", "eslint.config.js", "vite.config.ts", "tests"] +} + diff --git a/packages/preact-devtools/vite.config.ts b/packages/preact-devtools/vite.config.ts new file mode 100644 index 00000000..407ee9db --- /dev/null +++ b/packages/preact-devtools/vite.config.ts @@ -0,0 +1,27 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { tanstackViteConfig } from '@tanstack/vite-config' +import preact from '@preact/preset-vite' +import packageJson from './package.json' +import type { Plugin } from 'vitest/config' + +const config = defineConfig({ + plugins: [preact() as any satisfies Plugin], + test: { + name: packageJson.name, + dir: './tests', + watch: false, + environment: 'jsdom', + // setupFiles: ['./tests/test-setup.ts'], + globals: true, + }, +}) + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: ['./src/index.ts'], + srcDir: './src', + cjs: false, + }), +) + diff --git a/packages/react-devtools/package.json b/packages/react-devtools/package.json index 0ecd0979..5fc870e5 100644 --- a/packages/react-devtools/package.json +++ b/packages/react-devtools/package.json @@ -55,7 +55,7 @@ "@types/react": "^19.1.13", "@vitejs/plugin-react": "^4.7.0", "eslint-plugin-react-compiler": "19.1.0-rc.2", - "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-hooks": "^7.0.1", "react": "19.1.1" }, "peerDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fda11e8..4ae61a7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,6 +86,62 @@ importers: examples/angular/ssr: {} + examples/preact/basic: + dependencies: + '@tanstack/devtools-client': + specifier: 0.0.4 + version: link:../../../packages/devtools-client + '@tanstack/devtools-event-client': + specifier: 0.3.5 + version: link:../../../packages/event-bus-client + '@tanstack/preact-devtools': + specifier: workspace:* + version: link:../../../packages/preact-devtools + preact: + specifier: ^10.28.0 + version: 10.28.0 + zod: + specifier: ^4.1.11 + version: 4.1.13 + devDependencies: + '@preact/preset-vite': + specifier: ^2.10.2 + version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/devtools-ui': + specifier: 0.4.4 + version: link:../../../packages/devtools-ui + '@tanstack/devtools-vite': + specifier: 0.3.11 + version: link:../../../packages/devtools-vite + sonda: + specifier: 0.9.0 + version: 0.9.0 + vite: + specifier: ^7.1.7 + version: 7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite-plugin-inspect: + specifier: 11.3.3 + version: 11.3.3(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + + examples/preact/custom-devtools: + dependencies: + '@tanstack/devtools-event-client': + specifier: 0.3.5 + version: link:../../../packages/event-bus-client + '@tanstack/preact-devtools': + specifier: workspace:* + version: link:../../../packages/preact-devtools + preact: + specifier: ^10.28.0 + version: 10.28.0 + devDependencies: + '@preact/preset-vite': + specifier: ^2.10.2 + version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + vite: + specifier: ^7.1.7 + version: 7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + examples/react/basic: dependencies: '@tanstack/devtools-client': @@ -590,6 +646,9 @@ importers: '@types/react': specifier: '>=17.0.0' version: 19.2.7 + preact: + specifier: '>=10.0.0' + version: 10.28.0 react: specifier: '>=17.0.0' version: 19.1.1 @@ -681,6 +740,22 @@ importers: specifier: workspace:* version: link:../event-bus + packages/preact-devtools: + dependencies: + '@tanstack/devtools': + specifier: workspace:* + version: link:../devtools + devDependencies: + '@preact/preset-vite': + specifier: ^2.10.2 + version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + eslint-plugin-react-hooks: + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + preact: + specifier: ^10.28.0 + version: 10.28.0 + packages/react-devtools: dependencies: '@tanstack/devtools': @@ -706,8 +781,8 @@ importers: specifier: 19.1.0-rc.2 version: 19.1.0-rc.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.39.1(jiti@2.6.1)) + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) react: specifier: 19.1.1 version: 19.1.1 @@ -940,6 +1015,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -952,6 +1033,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typescript@7.28.0': resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} engines: {node: '>=6.9.0'} @@ -2378,6 +2465,29 @@ packages: '@poppinss/exception@1.2.2': resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==} + '@preact/preset-vite@2.10.2': + resolution: {integrity: sha512-K9wHlJOtkE+cGqlyQ5v9kL3Ge0Ql4LlIZjkUTL+1zf3nNdF88F9UZN6VTV8jdzBX9Fl7WSzeNMSDG7qECPmSmg==} + peerDependencies: + '@babel/core': 7.x + vite: 2.x || 3.x || 4.x || 5.x || 6.x || 7.x + + '@prefresh/babel-plugin@0.5.2': + resolution: {integrity: sha512-AOl4HG6dAxWkJ5ndPHBgBa49oo/9bOiJuRDKHLSTyH+Fd9x00shTXpdiTj1W41l6oQIwUOAgJeHMn4QwIDpHkA==} + + '@prefresh/core@1.5.9': + resolution: {integrity: sha512-IKBKCPaz34OFVC+adiQ2qaTF5qdztO2/4ZPf4KsRTgjKosWqxVXmEbxCiUydYZRY8GVie+DQlKzQr9gt6HQ+EQ==} + peerDependencies: + preact: ^10.0.0 || ^11.0.0-0 + + '@prefresh/utils@1.2.1': + resolution: {integrity: sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw==} + + '@prefresh/vite@2.4.11': + resolution: {integrity: sha512-/XjURQqdRiCG3NpMmWqE9kJwrg9IchIOWHzulCfqg2sRe/8oQ1g5De7xrk9lbqPIQLn7ntBkKdqWXIj4E9YXyg==} + peerDependencies: + preact: ^10.4.0 || ^11.0.0-0 + vite: '>=2.0.0' + '@prisma/client@6.19.0': resolution: {integrity: sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g==} engines: {node: '>=18.18'} @@ -2567,6 +2677,10 @@ packages: rollup: optional: true + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + '@rollup/pluginutils@5.1.4': resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} engines: {node: '>=14.0.0'} @@ -3902,6 +4016,11 @@ packages: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} + babel-plugin-transform-hook-names@1.0.2: + resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} + peerDependencies: + '@babel/core': ^7.12.10 + babel-preset-solid@1.9.5: resolution: {integrity: sha512-85I3osODJ1LvZbv8wFozROV1vXq32BubqHXAGu73A//TRs3NLI1OFP83AQBUTSQHwgZQmARjHlJciym3we+V+w==} peerDependencies: @@ -4864,9 +4983,9 @@ packages: typescript: optional: true - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 @@ -6140,6 +6259,9 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true + node-html-parser@6.1.13: + resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} + node-machine-id@1.1.12: resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} @@ -6511,6 +6633,9 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} + preact@10.28.0: + resolution: {integrity: sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==} + precinct@12.2.0: resolution: {integrity: sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w==} engines: {node: '>=18'} @@ -6985,6 +7110,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-code-frame@1.3.0: + resolution: {integrity: sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -7087,6 +7215,10 @@ packages: stack-trace@0.0.10: resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + stack-trace@1.0.0-pre2: + resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==} + engines: {node: '>=16'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -7707,6 +7839,11 @@ packages: '@testing-library/jest-dom': optional: true + vite-prerender-plugin@0.5.12: + resolution: {integrity: sha512-EiwhbMn+flg14EysbLTmZSzq8NGTxhytgK3bf4aGRF1evWLGwZiHiUJ1KZDvbxgKbMf2pG6fJWGEa3UZXOnR1g==} + peerDependencies: + vite: 5.x || 6.x || 7.x + vite-tsconfig-paths@5.1.4: resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} peerDependencies: @@ -8053,6 +8190,12 @@ packages: peerDependencies: zod: ^3.18.0 + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -8339,6 +8482,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -8349,6 +8499,17 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -9696,6 +9857,42 @@ snapshots: '@poppinss/exception@1.2.2': {} + '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) + '@prefresh/vite': 2.4.11(preact@10.28.0)(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@rollup/pluginutils': 4.2.1 + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5) + debug: 4.4.3 + picocolors: 1.1.1 + vite: 7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite-prerender-plugin: 0.5.12(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + transitivePeerDependencies: + - preact + - supports-color + + '@prefresh/babel-plugin@0.5.2': {} + + '@prefresh/core@1.5.9(preact@10.28.0)': + dependencies: + preact: 10.28.0 + + '@prefresh/utils@1.2.1': {} + + '@prefresh/vite@2.4.11(preact@10.28.0)(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + dependencies: + '@babel/core': 7.28.5 + '@prefresh/babel-plugin': 0.5.2 + '@prefresh/core': 1.5.9(preact@10.28.0) + '@prefresh/utils': 1.2.1 + '@rollup/pluginutils': 4.2.1 + preact: 10.28.0 + vite: 7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + '@prisma/client@6.19.0(prisma@6.19.0(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3)': optionalDependencies: prisma: 6.19.0(magicast@0.3.5)(typescript@5.9.3) @@ -9849,6 +10046,11 @@ snapshots: optionalDependencies: rollup: 4.46.2 + '@rollup/pluginutils@4.2.1': + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + '@rollup/pluginutils@5.1.4(rollup@4.46.2)': dependencies: '@types/estree': 1.0.8 @@ -11545,6 +11747,10 @@ snapshots: cosmiconfig: 7.1.0 resolve: 1.22.10 + babel-plugin-transform-hook-names@1.0.2(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + babel-preset-solid@1.9.5(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 @@ -12526,9 +12732,16 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-hooks@5.2.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 eslint: 9.39.1(jiti@2.6.1) + hermes-parser: 0.25.1 + zod: 4.1.13 + zod-validation-error: 4.0.2(zod@4.1.13) + transitivePeerDependencies: + - supports-color eslint-plugin-react-naming-convention@1.53.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: @@ -13900,6 +14113,11 @@ snapshots: node-gyp-build@4.8.4: {} + node-html-parser@6.1.13: + dependencies: + css-select: 5.2.2 + he: 1.2.0 + node-machine-id@1.1.12: {} node-mock-http@1.0.2: {} @@ -14311,6 +14529,8 @@ snapshots: dependencies: xtend: 4.0.2 + preact@10.28.0: {} + precinct@12.2.0: dependencies: '@dependents/detective-less': 5.0.1 @@ -14848,6 +15068,10 @@ snapshots: signal-exit@4.1.0: {} + simple-code-frame@1.3.0: + dependencies: + kolorist: 1.8.0 + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 @@ -14944,6 +15168,8 @@ snapshots: stack-trace@0.0.10: {} + stack-trace@1.0.0-pre2: {} + stackback@0.0.2: {} stackframe@1.3.4: {} @@ -15687,6 +15913,16 @@ snapshots: transitivePeerDependencies: - supports-color + vite-prerender-plugin@0.5.12(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)): + dependencies: + kolorist: 1.8.0 + magic-string: 0.30.21 + node-html-parser: 6.1.13 + simple-code-frame: 1.3.0 + source-map: 0.7.6 + stack-trace: 1.0.0-pre2 + vite: 7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@22.15.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)): dependencies: debug: 4.4.3 @@ -15990,6 +16226,10 @@ snapshots: dependencies: zod: 3.25.76 + zod-validation-error@4.0.2(zod@4.1.13): + dependencies: + zod: 4.1.13 + zod@3.25.76: {} zod@4.1.13: {} diff --git a/vitest.workspace.js b/vitest.workspace.js index 7cd9a21d..61cabd60 100644 --- a/vitest.workspace.js +++ b/vitest.workspace.js @@ -3,5 +3,6 @@ import { defineWorkspace } from 'vitest/config' export default defineWorkspace([ './packages/devtools/vite.config.ts', './packages/react-devtools/vite.config.ts', + './packages/preact-devtools/vite.config.ts', './packages/solid-devtools/vite.config.ts', ]) From 02b36db07055ea9b6124297fb6913cd1fda28011 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 6 Dec 2025 11:11:46 -0600 Subject: [PATCH 2/4] changeset --- .changeset/every-crabs-rule.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/every-crabs-rule.md diff --git a/.changeset/every-crabs-rule.md b/.changeset/every-crabs-rule.md new file mode 100644 index 00000000..d969b6d1 --- /dev/null +++ b/.changeset/every-crabs-rule.md @@ -0,0 +1,6 @@ +--- +'@tanstack/preact-devtools': minor +'@tanstack/devtools-utils': minor +--- + +feat: add preact adapter for devtools. Add preact to devtool-utils From d6e20a8d46d33dc2a137791aaac48b294048de32 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 17:12:48 +0000 Subject: [PATCH 3/4] ci: apply automated fixes --- examples/preact/basic/src/index.tsx | 20 ++++++------------- .../preact/custom-devtools/vite.config.ts | 1 - packages/devtools-utils/src/preact/index.ts | 1 - packages/devtools-utils/src/preact/panel.tsx | 1 - packages/devtools-utils/src/preact/plugin.tsx | 1 - packages/devtools-utils/vite.config.preact.ts | 1 - packages/preact-devtools/CHANGELOG.md | 1 - packages/preact-devtools/README.md | 1 - packages/preact-devtools/eslint.config.js | 1 - packages/preact-devtools/package.json | 1 - packages/preact-devtools/src/devtools.tsx | 17 +++++++--------- packages/preact-devtools/src/index.ts | 1 - packages/preact-devtools/tsconfig.docs.json | 1 - packages/preact-devtools/tsconfig.json | 1 - packages/preact-devtools/vite.config.ts | 1 - 15 files changed, 13 insertions(+), 37 deletions(-) diff --git a/examples/preact/basic/src/index.tsx b/examples/preact/basic/src/index.tsx index f56045ba..88d1ffc5 100644 --- a/examples/preact/basic/src/index.tsx +++ b/examples/preact/basic/src/index.tsx @@ -11,11 +11,7 @@ type Post = { body: string } -function Posts({ - setPostId, -}: { - setPostId: (id: number) => void -}) { +function Posts({ setPostId }: { setPostId: (id: number) => void }) { const [posts, setPosts] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -46,10 +42,7 @@ function Posts({
{posts.map((post) => (

- setPostId(post.id)} - href="#" - > + setPostId(post.id)} href="#"> {post.title}

@@ -166,11 +159,10 @@ function App() { {win && render(, win.document.body)}

- As you visit the posts below, you will notice them in a loading - state the first time you load them. However, after you return to - this list and click on any posts you have already visited again, you - will see them load instantly and background refresh right before - your eyes!{' '} + As you visit the posts below, you will notice them in a loading state + the first time you load them. However, after you return to this list + and click on any posts you have already visited again, you will see + them load instantly and background refresh right before your eyes!{' '} (You may need to throttle your network speed to simulate longer loading sequences) diff --git a/examples/preact/custom-devtools/vite.config.ts b/examples/preact/custom-devtools/vite.config.ts index cb76bf8a..bfe110c0 100644 --- a/examples/preact/custom-devtools/vite.config.ts +++ b/examples/preact/custom-devtools/vite.config.ts @@ -4,4 +4,3 @@ import preact from '@preact/preset-vite' export default defineConfig({ plugins: [preact()], }) - diff --git a/packages/devtools-utils/src/preact/index.ts b/packages/devtools-utils/src/preact/index.ts index 90646448..37bc5ffd 100644 --- a/packages/devtools-utils/src/preact/index.ts +++ b/packages/devtools-utils/src/preact/index.ts @@ -1,3 +1,2 @@ export * from './panel' export * from './plugin' - diff --git a/packages/devtools-utils/src/preact/panel.tsx b/packages/devtools-utils/src/preact/panel.tsx index 3e5af1c0..fddf6379 100644 --- a/packages/devtools-utils/src/preact/panel.tsx +++ b/packages/devtools-utils/src/preact/panel.tsx @@ -57,4 +57,3 @@ export function createPreactPanel< } return [Panel, NoOpPanel] as const } - diff --git a/packages/devtools-utils/src/preact/plugin.tsx b/packages/devtools-utils/src/preact/plugin.tsx index e472d71c..a5485487 100644 --- a/packages/devtools-utils/src/preact/plugin.tsx +++ b/packages/devtools-utils/src/preact/plugin.tsx @@ -28,4 +28,3 @@ export function createPreactPlugin({ } return [Plugin, NoOpPlugin] as const } - diff --git a/packages/devtools-utils/vite.config.preact.ts b/packages/devtools-utils/vite.config.preact.ts index 1132fc37..201bcd47 100644 --- a/packages/devtools-utils/vite.config.preact.ts +++ b/packages/devtools-utils/vite.config.preact.ts @@ -23,4 +23,3 @@ export default mergeConfig( cjs: false, }), ) - diff --git a/packages/preact-devtools/CHANGELOG.md b/packages/preact-devtools/CHANGELOG.md index 5f9f50ef..e70e8350 100644 --- a/packages/preact-devtools/CHANGELOG.md +++ b/packages/preact-devtools/CHANGELOG.md @@ -5,4 +5,3 @@ ### Patch Changes - Initial release of Preact devtools adapter - diff --git a/packages/preact-devtools/README.md b/packages/preact-devtools/README.md index ed3fdc19..ed6e33d8 100644 --- a/packages/preact-devtools/README.md +++ b/packages/preact-devtools/README.md @@ -55,4 +55,3 @@ function App() { ) } ``` - diff --git a/packages/preact-devtools/eslint.config.js b/packages/preact-devtools/eslint.config.js index 57ef959f..8f561f82 100644 --- a/packages/preact-devtools/eslint.config.js +++ b/packages/preact-devtools/eslint.config.js @@ -16,4 +16,3 @@ export default [ }, }, ] - diff --git a/packages/preact-devtools/package.json b/packages/preact-devtools/package.json index 41628442..7556cb9c 100644 --- a/packages/preact-devtools/package.json +++ b/packages/preact-devtools/package.json @@ -59,4 +59,3 @@ "preact": ">=10.0.0" } } - diff --git a/packages/preact-devtools/src/devtools.tsx b/packages/preact-devtools/src/devtools.tsx index 6d3d8e78..d5136cb1 100644 --- a/packages/preact-devtools/src/devtools.tsx +++ b/packages/preact-devtools/src/devtools.tsx @@ -134,7 +134,11 @@ function Portal({ const convertRender = ( Component: PluginRender, - setComponents: (value: Record | ((prev: Record) => Record)) => void, + setComponents: ( + value: + | Record + | ((prev: Record) => Record), + ) => void, e: HTMLElement, theme: 'dark' | 'light', ) => { @@ -275,10 +279,7 @@ export const TanStackDevtools = ({ ? Object.entries(pluginContainers).map(([key, pluginContainer]) => { const component = PluginComponents[key] return component ? ( - + {component} ) : null @@ -289,10 +290,7 @@ export const TanStackDevtools = ({ ? Object.entries(titleContainers).map(([key, titleContainer]) => { const component = TitleComponents[key] return component ? ( - + {component} ) : null @@ -305,4 +303,3 @@ export const TanStackDevtools = ({ ) } - diff --git a/packages/preact-devtools/src/index.ts b/packages/preact-devtools/src/index.ts index cd1cdd06..b96b9e0e 100644 --- a/packages/preact-devtools/src/index.ts +++ b/packages/preact-devtools/src/index.ts @@ -8,4 +8,3 @@ export type { TanStackDevtoolsPreactPlugin, TanStackDevtoolsPreactInit, } from './devtools' - diff --git a/packages/preact-devtools/tsconfig.docs.json b/packages/preact-devtools/tsconfig.docs.json index 21a85de7..0a7802de 100644 --- a/packages/preact-devtools/tsconfig.docs.json +++ b/packages/preact-devtools/tsconfig.docs.json @@ -7,4 +7,3 @@ }, "include": ["src"] } - diff --git a/packages/preact-devtools/tsconfig.json b/packages/preact-devtools/tsconfig.json index 27be25e0..f548145d 100644 --- a/packages/preact-devtools/tsconfig.json +++ b/packages/preact-devtools/tsconfig.json @@ -6,4 +6,3 @@ }, "include": ["src", "eslint.config.js", "vite.config.ts", "tests"] } - diff --git a/packages/preact-devtools/vite.config.ts b/packages/preact-devtools/vite.config.ts index 407ee9db..2b69079b 100644 --- a/packages/preact-devtools/vite.config.ts +++ b/packages/preact-devtools/vite.config.ts @@ -24,4 +24,3 @@ export default mergeConfig( cjs: false, }), ) - From 2ef9a021bc9d4b9ea73279335ad7c51a6e1bbd56 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 6 Dec 2025 11:18:32 -0600 Subject: [PATCH 4/4] fix example jsx issue --- examples/preact/basic/index.html | 6 +++--- examples/preact/basic/src/button-with-props-only.tsx | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/preact/basic/index.html b/examples/preact/basic/index.html index 1c96ec87..9568dc83 100644 --- a/examples/preact/basic/index.html +++ b/examples/preact/basic/index.html @@ -12,7 +12,7 @@ @@ -23,13 +23,13 @@ Basic Example - TanStack Devtools A basic example of using TanStack Devtools with React.A basic example of using TanStack Devtools with Preact. diff --git a/examples/preact/basic/src/button-with-props-only.tsx b/examples/preact/basic/src/button-with-props-only.tsx index 0b79cfee..bc383c69 100644 --- a/examples/preact/basic/src/button-with-props-only.tsx +++ b/examples/preact/basic/src/button-with-props-only.tsx @@ -1,5 +1,6 @@ +import type { ComponentChildren } from 'preact' import { Button } from './button' -export const ButtonWithProps = (props: { children: React.ReactNode }) => { +export const ButtonWithProps = (props: { children: ComponentChildren }) => { return