From b5fc9bd950c5844db0b428cd9ba09177fef1086b Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Mon, 22 Sep 2025 15:46:43 +0200 Subject: [PATCH 01/21] feat: add initial utils package --- packages/devtools-utils/CHANGELOG.md | 0 packages/devtools-utils/README.md | 4 + packages/devtools-utils/eslint.config.js | 10 + packages/devtools-utils/package.json | 55 +++++ packages/devtools-utils/src/index.ts | 1 + packages/devtools-utils/src/plugin.ts | 245 ++++++++++++++++++ packages/devtools-utils/tests/index.test.ts | 259 ++++++++++++++++++++ packages/devtools-utils/tests/test-setup.ts | 1 + packages/devtools-utils/tsconfig.docs.json | 4 + packages/devtools-utils/tsconfig.json | 10 + packages/devtools-utils/vite.config.ts | 23 ++ 11 files changed, 612 insertions(+) create mode 100644 packages/devtools-utils/CHANGELOG.md create mode 100644 packages/devtools-utils/README.md create mode 100644 packages/devtools-utils/eslint.config.js create mode 100644 packages/devtools-utils/package.json create mode 100644 packages/devtools-utils/src/index.ts create mode 100644 packages/devtools-utils/src/plugin.ts create mode 100644 packages/devtools-utils/tests/index.test.ts create mode 100644 packages/devtools-utils/tests/test-setup.ts create mode 100644 packages/devtools-utils/tsconfig.docs.json create mode 100644 packages/devtools-utils/tsconfig.json create mode 100644 packages/devtools-utils/vite.config.ts diff --git a/packages/devtools-utils/CHANGELOG.md b/packages/devtools-utils/CHANGELOG.md new file mode 100644 index 00000000..e69de29b diff --git a/packages/devtools-utils/README.md b/packages/devtools-utils/README.md new file mode 100644 index 00000000..097bb68e --- /dev/null +++ b/packages/devtools-utils/README.md @@ -0,0 +1,4 @@ +# @tanstack/devtools-utils + +This package is still under active development and might have breaking changes in the future. Please use it with caution. + \ No newline at end of file diff --git a/packages/devtools-utils/eslint.config.js b/packages/devtools-utils/eslint.config.js new file mode 100644 index 00000000..e472c69e --- /dev/null +++ b/packages/devtools-utils/eslint.config.js @@ -0,0 +1,10 @@ +// @ts-check + +import rootConfig from '../../eslint.config.js' + +export default [ + ...rootConfig, + { + rules: {}, + }, +] diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json new file mode 100644 index 00000000..c5c78d12 --- /dev/null +++ b/packages/devtools-utils/package.json @@ -0,0 +1,55 @@ +{ + "name": "@tanstack/devtools-utils", + "version": "0.0.0", + "description": "TanStack Devtools utilities for creating your own devtools.", + "author": "Tanner Linsley", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TanStack/devtools.git", + "directory": "packages/devtools" + }, + "homepage": "https://tanstack.com/devtools", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "devtools" + ], + "type": "module", + "types": "dist/esm/index.d.ts", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.js", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "files": [ + "dist/", + "src" + ], + "scripts": { + "clean": "premove ./build ./dist", + "lint:fix": "eslint ./src --fix", + "test:eslint": "eslint ./src", + "test:lib": "vitest", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc", + "test:build": "publint --strict", + "build": "vite build" + } +} \ No newline at end of file diff --git a/packages/devtools-utils/src/index.ts b/packages/devtools-utils/src/index.ts new file mode 100644 index 00000000..6b3402a0 --- /dev/null +++ b/packages/devtools-utils/src/index.ts @@ -0,0 +1 @@ +export { EventClient } from './plugin' diff --git a/packages/devtools-utils/src/plugin.ts b/packages/devtools-utils/src/plugin.ts new file mode 100644 index 00000000..ff155900 --- /dev/null +++ b/packages/devtools-utils/src/plugin.ts @@ -0,0 +1,245 @@ +interface TanStackDevtoolsEvent { + type: TEventName + payload: TPayload + pluginId?: string // Optional pluginId to filter events by plugin +} +declare global { + // eslint-disable-next-line no-var + var __TANSTACK_EVENT_TARGET__: EventTarget | null +} + +type AllDevtoolsEvents> = { + [Key in keyof TEventMap]: TanStackDevtoolsEvent +}[keyof TEventMap] + +export class EventClient< + TEventMap extends Record, + TPluginId extends string = TEventMap extends Record + ? P extends `${infer Id}:${string}` + ? Id + : never + : never, +> { + #pluginId: TPluginId + #eventTarget: () => EventTarget + #debug: boolean + #queuedEvents: Array> + #connected: boolean + #connectIntervalId: number | null + #connectEveryMs: number + #retryCount = 0 + #maxRetries = 5 + #onConnected = () => { + this.debugLog('Connected to event bus') + this.#connected = true + this.debugLog('Emitting queued events', this.#queuedEvents) + this.#queuedEvents.forEach((event) => this.emitEventToBus(event)) + this.#queuedEvents = [] + this.stopConnectLoop() + this.#eventTarget().removeEventListener( + 'tanstack-connect-success', + this.#onConnected, + ) + } + #connectFunction = () => { + this.#eventTarget().addEventListener( + 'tanstack-connect-success', + this.#onConnected, + ) + if (this.#retryCount < this.#maxRetries) { + this.#retryCount++ + this.#eventTarget().dispatchEvent(new CustomEvent('tanstack-connect')) + return + } + + this.#eventTarget().removeEventListener( + 'tanstack-connect', + this.#connectFunction, + ) + this.debugLog('Max retries reached, giving up on connection') + this.stopConnectLoop() + } + + constructor({ + pluginId, + debug = false, + }: { + pluginId: TPluginId + debug?: boolean + }) { + this.#pluginId = pluginId + this.#eventTarget = this.getGlobalTarget + this.#debug = debug + this.debugLog(' Initializing event subscription for plugin', this.#pluginId) + this.#queuedEvents = [] + this.#connected = false + this.#connectIntervalId = null + this.#connectEveryMs = 500 + + if (typeof CustomEvent !== 'undefined') { + this.#connectFunction() + this.startConnectLoop() + } + } + + private startConnectLoop() { + if (this.#connectIntervalId !== null || this.#connected) return + this.debugLog(`Starting connect loop (every ${this.#connectEveryMs}ms)`) + + this.#connectIntervalId = setInterval( + this.#connectFunction, + this.#connectEveryMs, + ) as unknown as number + } + + private stopConnectLoop() { + if (this.#connectIntervalId === null) { + return + } + clearInterval(this.#connectIntervalId) + this.#connectIntervalId = null + this.debugLog('Stopped connect loop') + } + + private debugLog(...args: Array) { + if (this.#debug) { + console.log(`🌴 [tanstack-devtools:${this.#pluginId}-plugin]`, ...args) + } + } + private getGlobalTarget() { + // server one is the global event target + if ( + typeof globalThis !== 'undefined' && + globalThis.__TANSTACK_EVENT_TARGET__ + ) { + this.debugLog('Using global event target') + return globalThis.__TANSTACK_EVENT_TARGET__ + } + // CLient event target is the browser window object + if ( + typeof window !== 'undefined' && + typeof window.addEventListener !== 'undefined' + ) { + this.debugLog('Using window as event target') + + return window + } + // Protect against non-web environments like react-native + const eventTarget = + typeof EventTarget !== 'undefined' ? new EventTarget() : undefined + + // For non-web environments like react-native + if ( + typeof eventTarget === 'undefined' || + typeof eventTarget.addEventListener === 'undefined' + ) { + this.debugLog( + 'No event mechanism available, running in non-web environment', + ) + return { + addEventListener: () => {}, + removeEventListener: () => {}, + dispatchEvent: () => false, + } + } + + this.debugLog('Using new EventTarget as fallback') + return eventTarget + } + + getPluginId() { + return this.#pluginId + } + + private emitEventToBus(event: TanStackDevtoolsEvent) { + this.debugLog('Emitting event to client bus', event) + this.#eventTarget().dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { detail: event }), + ) + } + + emit< + TSuffix extends Extract< + keyof TEventMap, + `${TPluginId & string}:${string}` + > extends `${TPluginId & string}:${infer S}` + ? S + : never, + >( + eventSuffix: TSuffix, + payload: TEventMap[`${TPluginId & string}:${TSuffix}`], + ) { + // wait to connect to the bus + if (!this.#connected) { + this.debugLog('Bus not available, will be pushed as soon as connected') + return this.#queuedEvents.push({ + type: `${this.#pluginId}:${eventSuffix}`, + payload, + pluginId: this.#pluginId, + }) + } + // emit right now + return this.emitEventToBus({ + type: `${this.#pluginId}:${eventSuffix}`, + payload, + pluginId: this.#pluginId, + }) + } + + on< + TSuffix extends Extract< + keyof TEventMap, + `${TPluginId & string}:${string}` + > extends `${TPluginId & string}:${infer S}` + ? S + : never, + >( + eventSuffix: TSuffix, + cb: ( + event: TanStackDevtoolsEvent< + `${TPluginId & string}:${TSuffix}`, + TEventMap[`${TPluginId & string}:${TSuffix}`] + >, + ) => void, + ) { + const eventName = `${this.#pluginId}:${eventSuffix}` as const + const handler = (e: Event) => { + this.debugLog('Received event from bus', (e as CustomEvent).detail) + cb((e as CustomEvent).detail) + } + this.#eventTarget().addEventListener(eventName, handler) + this.debugLog('Registered event to bus', eventName) + return () => { + this.#eventTarget().removeEventListener(eventName, handler) + } + } + + onAll(cb: (event: TanStackDevtoolsEvent) => void) { + const handler = (e: Event) => { + const event = (e as CustomEvent).detail + + cb(event) + } + this.#eventTarget().addEventListener('tanstack-devtools-global', handler) + return () => + this.#eventTarget().removeEventListener( + 'tanstack-devtools-global', + handler, + ) + } + onAllPluginEvents(cb: (event: AllDevtoolsEvents) => void) { + const handler = (e: Event) => { + const event = (e as CustomEvent).detail + if (this.#pluginId && event.pluginId !== this.#pluginId) { + return + } + cb(event) + } + this.#eventTarget().addEventListener('tanstack-devtools-global', handler) + return () => + this.#eventTarget().removeEventListener( + 'tanstack-devtools-global', + handler, + ) + } +} diff --git a/packages/devtools-utils/tests/index.test.ts b/packages/devtools-utils/tests/index.test.ts new file mode 100644 index 00000000..048ffa4a --- /dev/null +++ b/packages/devtools-utils/tests/index.test.ts @@ -0,0 +1,259 @@ +import { describe, expect, it, vi } from 'vitest' +import { ClientEventBus } from '@tanstack/devtools-event-bus/client' +import { EventClient } from '../src' + +// start the client bus for testing +const bus = new ClientEventBus() +bus.start() +// client bus uses window to dispatch events +const clientBusEmitTarget = window +describe('EventClient', () => { + describe('debug config', () => { + it('should emit logs when debug set to true and have the correct plugin name', () => { + const consoleSpy = vi.spyOn(console, 'log') + new EventClient({ + debug: true, + pluginId: 'test', + }) + expect(consoleSpy).toHaveBeenCalledWith( + '🌴 [tanstack-devtools:test-plugin]', + ' Initializing event subscription for plugin', + 'test', + ) + }) + + it("shouldn't emit logs when debug set to false", () => { + const consoleSpy = vi.spyOn(console, 'log') + new EventClient({ + debug: false, + pluginId: 'test', + }) + expect(consoleSpy).not.toHaveBeenCalled() + }) + }) + + describe('getGlobalTarget', () => { + it('if the global target is set it should re-use it for emitting/listening/removing of events', () => { + const target = new EventTarget() + const handleSuccessConnection = vi.fn() + target.addEventListener('tanstack-connect', () => { + target.dispatchEvent(new CustomEvent('tanstack-connect-success')) + }) + globalThis.__TANSTACK_EVENT_TARGET__ = null + + vi.spyOn( + globalThis, + '__TANSTACK_EVENT_TARGET__', + 'get', + ).mockImplementation(() => { + return target + }) + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') + const targetListenSpy = vi.spyOn(target, 'addEventListener') + const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') + const cleanup = client.on('test:event', () => {}) + cleanup() + client.emit('test:event', { foo: 'bar' }) + expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) + expect(targetListenSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + expect(targetRemoveSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + vi.resetAllMocks() + target.removeEventListener('tanstack-connect', handleSuccessConnection) + }) + it('should use the window object if the globalTarget is not set for emitting/listening/removing of events', () => { + const target = window + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') + const targetListenSpy = vi.spyOn(target, 'addEventListener') + const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') + const cleanup = client.on('test:event', () => {}) + cleanup() + client.emit('test:event', { foo: 'bar' }) + expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) + expect(targetListenSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + expect(targetRemoveSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + }) + }) + + describe('on', () => { + it('should register an event with the pluginId (event => test:event)', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + + const eventBusSpy = vi.spyOn(clientBusEmitTarget, 'addEventListener') + client.on('event', () => {}) + expect(eventBusSpy).toHaveBeenCalledWith( + 'test:event', + expect.any(Function), + ) + }) + it('should register an event listener for the specified event and get events when they are emitted', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + const cleanup = client.on('event', eventHandler) + client.emit('event', { foo: 'bar' }) + expect(eventHandler).toHaveBeenCalledWith({ + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + cleanup() + }) + + it("shouldn't get an event if unregistered before it comes", () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + const cleanup = client.on('event', eventHandler) + cleanup() + client.emit('event', { foo: 'bar' }) + expect(eventHandler).not.toHaveBeenCalled() + }) + + it("shouldn't get an event if it's not registered to it", () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.on('event', eventHandler) + client.emit('other_event', { foo: 'bar' }) + expect(eventHandler).not.toHaveBeenCalled() + }) + }) + + describe('emit', () => { + it('should emit an event with the correct type and payload to the event bus', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.on('event', eventHandler) + client.emit('event', { foo: 'bar' }) + expect(eventHandler).toHaveBeenCalledWith({ + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + }) + }) + + describe('onAll', () => { + it('should listen to all events even if they do not come from the registered client', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.onAll(eventHandler) + client.emit('event', { foo: 'bar' }) + expect(eventHandler).toHaveBeenCalledWith({ + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + clientBusEmitTarget.dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { + detail: { + type: 'other-plugin', + payload: { foo: 'bar' }, + }, + }), + ) + + expect(eventHandler).lastCalledWith({ + type: 'other-plugin', + payload: { foo: 'bar' }, + }) + }) + }) + + describe('queued events', () => { + it('emits queued events when connected to the event bus', async () => { + bus.stop() + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.on('event', eventHandler) + client.emit('event', { foo: 'bar' }) + + bus.start() + // wait to connect to the bus + await new Promise((resolve) => setTimeout(resolve, 500)) + expect(eventHandler).toHaveBeenCalledWith({ + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + }) + }) + describe('onAllPluginEvents', () => { + it('should listen to all events that come from the plugin', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.onAllPluginEvents(eventHandler) + client.emit('event', { foo: 'bar' }) + client.emit('event2', { foo: 'bar' }) + expect(eventHandler).nthCalledWith(1, { + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + expect(eventHandler).nthCalledWith(2, { + type: 'test:event2', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + }) + + it('should ignore events that do not come from the plugin', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.onAllPluginEvents(eventHandler) + clientBusEmitTarget.dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { + detail: { + type: 'other-plugin', + payload: { foo: 'bar' }, + }, + }), + ) + expect(eventHandler).not.toHaveBeenCalled() + }) + }) +}) diff --git a/packages/devtools-utils/tests/test-setup.ts b/packages/devtools-utils/tests/test-setup.ts new file mode 100644 index 00000000..a9d0dd31 --- /dev/null +++ b/packages/devtools-utils/tests/test-setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest' diff --git a/packages/devtools-utils/tsconfig.docs.json b/packages/devtools-utils/tsconfig.docs.json new file mode 100644 index 00000000..2880b4df --- /dev/null +++ b/packages/devtools-utils/tsconfig.docs.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["tests", "src"] +} diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json new file mode 100644 index 00000000..1838eb23 --- /dev/null +++ b/packages/devtools-utils/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src", + "eslint.config.js", + "vite.config.ts", + "tests", + "src/server" + ] +} diff --git a/packages/devtools-utils/vite.config.ts b/packages/devtools-utils/vite.config.ts new file mode 100644 index 00000000..79dae7cd --- /dev/null +++ b/packages/devtools-utils/vite.config.ts @@ -0,0 +1,23 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { tanstackViteConfig } from '@tanstack/config/vite' +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/index.ts'], + srcDir: './src', + }), +) From 8f37b72eff3c6d76e3d17128175d5164c397b2ae Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 16:35:01 +0000 Subject: [PATCH 02/21] ci: apply automated fixes --- packages/devtools-utils/README.md | 1 - packages/devtools-utils/package.json | 2 +- pnpm-lock.yaml | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/devtools-utils/README.md b/packages/devtools-utils/README.md index 097bb68e..1dfd3ab3 100644 --- a/packages/devtools-utils/README.md +++ b/packages/devtools-utils/README.md @@ -1,4 +1,3 @@ # @tanstack/devtools-utils This package is still under active development and might have breaking changes in the future. Please use it with caution. - \ No newline at end of file diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index c5c78d12..32693880 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -52,4 +52,4 @@ "test:build": "publint --strict", "build": "vite build" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78c72812..7b62429d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -610,6 +610,8 @@ importers: specifier: ^2.11.6 version: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(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)) + packages/devtools-utils: {} + packages/devtools-vite: dependencies: '@babel/core': From 995d6312e45a3f3df81263a0e2a3e91b41019689 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 11:22:35 +0200 Subject: [PATCH 03/21] add utilities for creating core and panel adapters --- .changeset/rare-owls-find.md | 5 + knip.json | 21 +- packages/devtools-utils/package.json | 40 ++- packages/devtools-utils/src/index.ts | 1 - packages/devtools-utils/src/plugin.ts | 245 ----------------- packages/devtools-utils/src/react/index.ts | 1 + .../devtools-utils/src/react/panel.test.tsx | 17 ++ packages/devtools-utils/src/react/panel.tsx | 54 ++++ .../devtools-utils/src/solid/class.test.tsx | 96 +++++++ packages/devtools-utils/src/solid/class.tsx | 69 +++++ packages/devtools-utils/src/solid/index.ts | 2 + packages/devtools-utils/src/solid/panel.tsx | 33 +++ packages/devtools-utils/tests/index.test.ts | 259 ------------------ packages/devtools-utils/tsconfig.json | 3 + packages/devtools-utils/vite.config.ts | 3 +- pnpm-lock.yaml | 17 +- 16 files changed, 344 insertions(+), 522 deletions(-) create mode 100644 .changeset/rare-owls-find.md delete mode 100644 packages/devtools-utils/src/plugin.ts create mode 100644 packages/devtools-utils/src/react/index.ts create mode 100644 packages/devtools-utils/src/react/panel.test.tsx create mode 100644 packages/devtools-utils/src/react/panel.tsx create mode 100644 packages/devtools-utils/src/solid/class.test.tsx create mode 100644 packages/devtools-utils/src/solid/class.tsx create mode 100644 packages/devtools-utils/src/solid/index.ts create mode 100644 packages/devtools-utils/src/solid/panel.tsx delete mode 100644 packages/devtools-utils/tests/index.test.ts diff --git a/.changeset/rare-owls-find.md b/.changeset/rare-owls-find.md new file mode 100644 index 00000000..40dc6276 --- /dev/null +++ b/.changeset/rare-owls-find.md @@ -0,0 +1,5 @@ +--- +'@tanstack/devtools-utils': patch +--- + +initial release of utils diff --git a/knip.json b/knip.json index ff4cd666..a280a8aa 100644 --- a/knip.json +++ b/knip.json @@ -1,10 +1,23 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "ignoreDependencies": ["@size-limit/preset-small-lib", "@faker-js/faker"], - "ignoreWorkspaces": ["examples/**"], + "ignoreDependencies": [ + "@faker-js/faker" + ], + "ignoreWorkspaces": [ + "examples/**" + ], "workspaces": { + "packages/devtools-utils": { + "ignoreDependencies": [ + "react", + "solid-js", + "@types/react" + ] + }, "packages/solid-devtools": { - "ignore": ["**/core.tsx"] + "ignore": [ + "**/core.tsx" + ] } } -} +} \ No newline at end of file diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index 32693880..c2f282bd 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -18,18 +18,17 @@ "devtools" ], "type": "module", - "types": "dist/esm/index.d.ts", - "main": "dist/cjs/index.cjs", - "module": "dist/esm/index.js", "exports": { - ".": { + "./react": { "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/cjs/index.d.cts", - "default": "./dist/cjs/index.cjs" + "types": "./dist/esm/react/index.d.ts", + "default": "./dist/esm/react/index.js" + } + }, + "./solid": { + "import": { + "types": "./dist/esm/solid/index.d.ts", + "default": "./dist/esm/solid/index.js" } }, "./package.json": "./package.json" @@ -38,6 +37,25 @@ "engines": { "node": ">=18" }, + "dependencies": { + "@tanstack/devtools-ui": "workspace:^" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0", + "solid-js": ">=1.9.7" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "solid-js": { + "optional": true + } + }, "files": [ "dist/", "src" @@ -52,4 +70,4 @@ "test:build": "publint --strict", "build": "vite build" } -} +} \ No newline at end of file diff --git a/packages/devtools-utils/src/index.ts b/packages/devtools-utils/src/index.ts index 6b3402a0..e69de29b 100644 --- a/packages/devtools-utils/src/index.ts +++ b/packages/devtools-utils/src/index.ts @@ -1 +0,0 @@ -export { EventClient } from './plugin' diff --git a/packages/devtools-utils/src/plugin.ts b/packages/devtools-utils/src/plugin.ts deleted file mode 100644 index ff155900..00000000 --- a/packages/devtools-utils/src/plugin.ts +++ /dev/null @@ -1,245 +0,0 @@ -interface TanStackDevtoolsEvent { - type: TEventName - payload: TPayload - pluginId?: string // Optional pluginId to filter events by plugin -} -declare global { - // eslint-disable-next-line no-var - var __TANSTACK_EVENT_TARGET__: EventTarget | null -} - -type AllDevtoolsEvents> = { - [Key in keyof TEventMap]: TanStackDevtoolsEvent -}[keyof TEventMap] - -export class EventClient< - TEventMap extends Record, - TPluginId extends string = TEventMap extends Record - ? P extends `${infer Id}:${string}` - ? Id - : never - : never, -> { - #pluginId: TPluginId - #eventTarget: () => EventTarget - #debug: boolean - #queuedEvents: Array> - #connected: boolean - #connectIntervalId: number | null - #connectEveryMs: number - #retryCount = 0 - #maxRetries = 5 - #onConnected = () => { - this.debugLog('Connected to event bus') - this.#connected = true - this.debugLog('Emitting queued events', this.#queuedEvents) - this.#queuedEvents.forEach((event) => this.emitEventToBus(event)) - this.#queuedEvents = [] - this.stopConnectLoop() - this.#eventTarget().removeEventListener( - 'tanstack-connect-success', - this.#onConnected, - ) - } - #connectFunction = () => { - this.#eventTarget().addEventListener( - 'tanstack-connect-success', - this.#onConnected, - ) - if (this.#retryCount < this.#maxRetries) { - this.#retryCount++ - this.#eventTarget().dispatchEvent(new CustomEvent('tanstack-connect')) - return - } - - this.#eventTarget().removeEventListener( - 'tanstack-connect', - this.#connectFunction, - ) - this.debugLog('Max retries reached, giving up on connection') - this.stopConnectLoop() - } - - constructor({ - pluginId, - debug = false, - }: { - pluginId: TPluginId - debug?: boolean - }) { - this.#pluginId = pluginId - this.#eventTarget = this.getGlobalTarget - this.#debug = debug - this.debugLog(' Initializing event subscription for plugin', this.#pluginId) - this.#queuedEvents = [] - this.#connected = false - this.#connectIntervalId = null - this.#connectEveryMs = 500 - - if (typeof CustomEvent !== 'undefined') { - this.#connectFunction() - this.startConnectLoop() - } - } - - private startConnectLoop() { - if (this.#connectIntervalId !== null || this.#connected) return - this.debugLog(`Starting connect loop (every ${this.#connectEveryMs}ms)`) - - this.#connectIntervalId = setInterval( - this.#connectFunction, - this.#connectEveryMs, - ) as unknown as number - } - - private stopConnectLoop() { - if (this.#connectIntervalId === null) { - return - } - clearInterval(this.#connectIntervalId) - this.#connectIntervalId = null - this.debugLog('Stopped connect loop') - } - - private debugLog(...args: Array) { - if (this.#debug) { - console.log(`🌴 [tanstack-devtools:${this.#pluginId}-plugin]`, ...args) - } - } - private getGlobalTarget() { - // server one is the global event target - if ( - typeof globalThis !== 'undefined' && - globalThis.__TANSTACK_EVENT_TARGET__ - ) { - this.debugLog('Using global event target') - return globalThis.__TANSTACK_EVENT_TARGET__ - } - // CLient event target is the browser window object - if ( - typeof window !== 'undefined' && - typeof window.addEventListener !== 'undefined' - ) { - this.debugLog('Using window as event target') - - return window - } - // Protect against non-web environments like react-native - const eventTarget = - typeof EventTarget !== 'undefined' ? new EventTarget() : undefined - - // For non-web environments like react-native - if ( - typeof eventTarget === 'undefined' || - typeof eventTarget.addEventListener === 'undefined' - ) { - this.debugLog( - 'No event mechanism available, running in non-web environment', - ) - return { - addEventListener: () => {}, - removeEventListener: () => {}, - dispatchEvent: () => false, - } - } - - this.debugLog('Using new EventTarget as fallback') - return eventTarget - } - - getPluginId() { - return this.#pluginId - } - - private emitEventToBus(event: TanStackDevtoolsEvent) { - this.debugLog('Emitting event to client bus', event) - this.#eventTarget().dispatchEvent( - new CustomEvent('tanstack-dispatch-event', { detail: event }), - ) - } - - emit< - TSuffix extends Extract< - keyof TEventMap, - `${TPluginId & string}:${string}` - > extends `${TPluginId & string}:${infer S}` - ? S - : never, - >( - eventSuffix: TSuffix, - payload: TEventMap[`${TPluginId & string}:${TSuffix}`], - ) { - // wait to connect to the bus - if (!this.#connected) { - this.debugLog('Bus not available, will be pushed as soon as connected') - return this.#queuedEvents.push({ - type: `${this.#pluginId}:${eventSuffix}`, - payload, - pluginId: this.#pluginId, - }) - } - // emit right now - return this.emitEventToBus({ - type: `${this.#pluginId}:${eventSuffix}`, - payload, - pluginId: this.#pluginId, - }) - } - - on< - TSuffix extends Extract< - keyof TEventMap, - `${TPluginId & string}:${string}` - > extends `${TPluginId & string}:${infer S}` - ? S - : never, - >( - eventSuffix: TSuffix, - cb: ( - event: TanStackDevtoolsEvent< - `${TPluginId & string}:${TSuffix}`, - TEventMap[`${TPluginId & string}:${TSuffix}`] - >, - ) => void, - ) { - const eventName = `${this.#pluginId}:${eventSuffix}` as const - const handler = (e: Event) => { - this.debugLog('Received event from bus', (e as CustomEvent).detail) - cb((e as CustomEvent).detail) - } - this.#eventTarget().addEventListener(eventName, handler) - this.debugLog('Registered event to bus', eventName) - return () => { - this.#eventTarget().removeEventListener(eventName, handler) - } - } - - onAll(cb: (event: TanStackDevtoolsEvent) => void) { - const handler = (e: Event) => { - const event = (e as CustomEvent).detail - - cb(event) - } - this.#eventTarget().addEventListener('tanstack-devtools-global', handler) - return () => - this.#eventTarget().removeEventListener( - 'tanstack-devtools-global', - handler, - ) - } - onAllPluginEvents(cb: (event: AllDevtoolsEvents) => void) { - const handler = (e: Event) => { - const event = (e as CustomEvent).detail - if (this.#pluginId && event.pluginId !== this.#pluginId) { - return - } - cb(event) - } - this.#eventTarget().addEventListener('tanstack-devtools-global', handler) - return () => - this.#eventTarget().removeEventListener( - 'tanstack-devtools-global', - handler, - ) - } -} diff --git a/packages/devtools-utils/src/react/index.ts b/packages/devtools-utils/src/react/index.ts new file mode 100644 index 00000000..5347230f --- /dev/null +++ b/packages/devtools-utils/src/react/index.ts @@ -0,0 +1 @@ +export * from './panel' \ No newline at end of file diff --git a/packages/devtools-utils/src/react/panel.test.tsx b/packages/devtools-utils/src/react/panel.test.tsx new file mode 100644 index 00000000..01c9831f --- /dev/null +++ b/packages/devtools-utils/src/react/panel.test.tsx @@ -0,0 +1,17 @@ +import { describe, expect, it } from "vitest"; +import { createReactPanel } from "./panel"; + + +describe("createReactPanel", () => { + it("should create a React component when provided with a package name", () => { + const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react'); + expect(ReactDevtoolsPanel).toBeDefined(); + expect(NoOpReactDevtoolsPanel).toBeDefined(); + }); + + it("should create a React component when provided with a different import name", () => { + const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react', 'default'); + expect(ReactDevtoolsPanel).toBeDefined(); + expect(NoOpReactDevtoolsPanel).toBeDefined(); + }); +}); \ No newline at end of file diff --git a/packages/devtools-utils/src/react/panel.tsx b/packages/devtools-utils/src/react/panel.tsx new file mode 100644 index 00000000..789d46ff --- /dev/null +++ b/packages/devtools-utils/src/react/panel.tsx @@ -0,0 +1,54 @@ +import React, { useEffect, useRef } from 'react' + +export interface DevtoolsPanelProps { + theme?: 'light' | 'dark' +} + +/** + * Creates a React 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-react' + * @param importName The name of the export to be imported from the devtools package (e.g., 'default' or 'DevtoolsCore') + * @returns A React component that mounts the devtools + * @example + * ```tsx + * // if the export is default + * const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react') + * ``` + * + * @example + * ```tsx + * // if the export is named differently + * const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react', 'DevtoolsCore') + * ``` + */ +export function createReactPanel void + unmount: () => void +}>(devtoolsPackageName: string, importName = 'default') { + function Panel(props: TComponentProps) { + const devToolRef = useRef(null) + const devtools = useRef(null) + useEffect(() => { + if (devtools.current) return + + import(/* @vite-ignore */devtoolsPackageName).then(({ [importName]: Core }) => { + devtools.current = new Core() + + if (devToolRef.current && devtools.current) { + devtools.current.mount(devToolRef.current, props?.theme ?? 'dark') + } + }) + + return () => { + devtools.current?.unmount() + } + }, [props?.theme]) + + return
+ } + + function NoOpPanel() { + return null; + } + return [Panel, NoOpPanel] as const; +} diff --git a/packages/devtools-utils/src/solid/class.test.tsx b/packages/devtools-utils/src/solid/class.test.tsx new file mode 100644 index 00000000..2b112708 --- /dev/null +++ b/packages/devtools-utils/src/solid/class.test.tsx @@ -0,0 +1,96 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { constructCoreClass } from "./class"; + +const lazyImportMock = vi.fn((fn) => fn()); +const renderMock = vi.fn(); +const portalMock = vi.fn((props: any) =>
{props.children}
); + +vi.mock("solid-js", async () => { + const actual = await vi.importActual("solid-js"); + return { + ...actual, + lazy: lazyImportMock, + } +}); + +vi.mock("solid-js/web", async () => { + const actual = await vi.importActual("solid-js/web"); + return { + ...actual, + render: renderMock, + Portal: portalMock, + }; +}); + + +describe("constructCoreClass", () => { + beforeEach(() => { + vi.clearAllMocks(); + }) + it("should export DevtoolsCore and NoOpDevtoolsCore classes and make no calls to Solid.js primitives", () => { + const [DevtoolsCore, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); + expect(DevtoolsCore).toBeDefined(); + expect(NoOpDevtoolsCore).toBeDefined(); + expect(lazyImportMock).not.toHaveBeenCalled(); + }); + + it("DevtoolsCore should call solid primitives when mount is called", async () => { + const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); + const instance = new DevtoolsCore(); + await instance.mount(document.createElement("div"), "dark") + expect(renderMock).toHaveBeenCalled(); + }); + + it("DevtoolsCore should throw if mount is called twice without unmounting", async () => { + const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); + const instance = new DevtoolsCore(); + await instance.mount(document.createElement("div"), "dark") + await expect(instance.mount(document.createElement("div"), "dark")).rejects.toThrow("Devtools is already mounted"); + }); + + it("DevtoolsCore should throw if unmount is called before mount", () => { + const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); + const instance = new DevtoolsCore(); + expect(() => instance.unmount()).toThrow("Devtools is not mounted"); + }); + + it("DevtoolsCore should allow mount after unmount", async () => { + const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); + const instance = new DevtoolsCore(); + await instance.mount(document.createElement("div"), "dark") + instance.unmount(); + await expect(instance.mount(document.createElement("div"), "dark")).resolves.not.toThrow(); + }); + + + + it("NoOpDevtoolsCore should not call any solid primitives when mount is called", async () => { + const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); + const noOpInstance = new NoOpDevtoolsCore(); + await noOpInstance.mount(document.createElement("div"), "dark") + + expect(lazyImportMock).not.toHaveBeenCalled(); + expect(renderMock).not.toHaveBeenCalled(); + expect(portalMock).not.toHaveBeenCalled(); + }); + + it("NoOpDevtoolsCore should not throw if mount is called multiple times", async () => { + const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); + const noOpInstance = new NoOpDevtoolsCore(); + await noOpInstance.mount(document.createElement("div"), "dark") + await expect(noOpInstance.mount(document.createElement("div"), "dark")).resolves.not.toThrow(); + }); + + it("NoOpDevtoolsCore should not throw if unmount is called before mount", () => { + const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); + const noOpInstance = new NoOpDevtoolsCore(); + expect(() => noOpInstance.unmount()).not.toThrow(); + }); + + it("NoOpDevtoolsCore should not throw if unmount is called after mount", async () => { + const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); + const noOpInstance = new NoOpDevtoolsCore(); + await noOpInstance.mount(document.createElement("div"), "dark") + expect(() => noOpInstance.unmount()).not.toThrow(); + }); +}); \ No newline at end of file diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx new file mode 100644 index 00000000..563b8681 --- /dev/null +++ b/packages/devtools-utils/src/solid/class.tsx @@ -0,0 +1,69 @@ +/** @jsxImportSource solid-js - we use Solid.js as JSX here */ + +/** + * Constructs the core class for the Devtools. + * This utility is used to construct a lazy loaded Solid component for the Devtools. + * It returns a tuple containing the main DevtoolsCore class and a NoOpDevtoolsCore class. + * The NoOpDevtoolsCore class is a no-op implementation that can be used for production if you want to explicitly exclude + * the Devtools from your application. + * @param importPath The path to the Solid component to be lazily imported + * @returns Tuple containing the DevtoolsCore class and a NoOpDevtoolsCore class + */ +export function constructCoreClass(importPath: string) { + class DevtoolsCore { + #isMounted = false + #dispose?: () => void + #Component: any + #ThemeProvider: any + + constructor() { + } + + async mount(el: T, theme: 'light' | 'dark') { + const { lazy } = await import('solid-js') + const { render, Portal } = await import('solid-js/web') + if (this.#isMounted) { + throw new Error('Devtools is already mounted') + } + const mountTo = el + const dispose = render(() => { + this.#Component = lazy(() => import(/** @vite-ignore */importPath)) + const Devtools = this.#Component + this.#ThemeProvider = lazy(() => + import('@tanstack/devtools-ui').then((mod) => ({ + default: mod.ThemeContextProvider, + })), + ) + const ThemeProvider = this.#ThemeProvider + + return ( + +
+ + + +
+
+ ) + }, mountTo) + this.#isMounted = true + this.#dispose = dispose + } + + unmount() { + if (!this.#isMounted) { + throw new Error('Devtools is not mounted') + } + this.#dispose?.() + this.#isMounted = false + } + } + class NoOpDevtoolsCore extends DevtoolsCore { + constructor() { + super() + } + async mount(_el: T, _theme: 'light' | 'dark') { } + unmount() { } + } + return [DevtoolsCore, NoOpDevtoolsCore] as const +} \ No newline at end of file diff --git a/packages/devtools-utils/src/solid/index.ts b/packages/devtools-utils/src/solid/index.ts new file mode 100644 index 00000000..c508a0ac --- /dev/null +++ b/packages/devtools-utils/src/solid/index.ts @@ -0,0 +1,2 @@ +export * from "./class" +export * from './panel' \ No newline at end of file diff --git a/packages/devtools-utils/src/solid/panel.tsx b/packages/devtools-utils/src/solid/panel.tsx new file mode 100644 index 00000000..9d9599e2 --- /dev/null +++ b/packages/devtools-utils/src/solid/panel.tsx @@ -0,0 +1,33 @@ +/** @jsxImportSource solid-js - we use Solid.js as JSX here */ + +import { onCleanup, onMount } from 'solid-js' + +export interface DevtoolsPanelProps { + theme?: 'light' | 'dark' +} + +export function createSolidPanel(importPath: string, importName = "default") { + function Panel(props: TComponentProps) { + let devToolRef: HTMLDivElement | undefined + + onMount(async () => { + const devtools = await import(/* @vite-ignore */importPath).then(mod => new mod[importName]()) + if (devToolRef) { + devtools.mount(devToolRef, props?.theme ?? 'dark') + + onCleanup(() => { + devtools.unmount() + }) + } + }) + + return
+ } + + function NoOpPanel() { + return null; + } + + return [Panel, NoOpPanel] as const; +} + diff --git a/packages/devtools-utils/tests/index.test.ts b/packages/devtools-utils/tests/index.test.ts deleted file mode 100644 index 048ffa4a..00000000 --- a/packages/devtools-utils/tests/index.test.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { ClientEventBus } from '@tanstack/devtools-event-bus/client' -import { EventClient } from '../src' - -// start the client bus for testing -const bus = new ClientEventBus() -bus.start() -// client bus uses window to dispatch events -const clientBusEmitTarget = window -describe('EventClient', () => { - describe('debug config', () => { - it('should emit logs when debug set to true and have the correct plugin name', () => { - const consoleSpy = vi.spyOn(console, 'log') - new EventClient({ - debug: true, - pluginId: 'test', - }) - expect(consoleSpy).toHaveBeenCalledWith( - '🌴 [tanstack-devtools:test-plugin]', - ' Initializing event subscription for plugin', - 'test', - ) - }) - - it("shouldn't emit logs when debug set to false", () => { - const consoleSpy = vi.spyOn(console, 'log') - new EventClient({ - debug: false, - pluginId: 'test', - }) - expect(consoleSpy).not.toHaveBeenCalled() - }) - }) - - describe('getGlobalTarget', () => { - it('if the global target is set it should re-use it for emitting/listening/removing of events', () => { - const target = new EventTarget() - const handleSuccessConnection = vi.fn() - target.addEventListener('tanstack-connect', () => { - target.dispatchEvent(new CustomEvent('tanstack-connect-success')) - }) - globalThis.__TANSTACK_EVENT_TARGET__ = null - - vi.spyOn( - globalThis, - '__TANSTACK_EVENT_TARGET__', - 'get', - ).mockImplementation(() => { - return target - }) - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') - const targetListenSpy = vi.spyOn(target, 'addEventListener') - const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) - cleanup() - client.emit('test:event', { foo: 'bar' }) - expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) - expect(targetListenSpy).toHaveBeenCalledWith( - expect.any(String), - expect.any(Function), - ) - expect(targetRemoveSpy).toHaveBeenCalledWith( - expect.any(String), - expect.any(Function), - ) - vi.resetAllMocks() - target.removeEventListener('tanstack-connect', handleSuccessConnection) - }) - it('should use the window object if the globalTarget is not set for emitting/listening/removing of events', () => { - const target = window - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') - const targetListenSpy = vi.spyOn(target, 'addEventListener') - const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) - cleanup() - client.emit('test:event', { foo: 'bar' }) - expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) - expect(targetListenSpy).toHaveBeenCalledWith( - expect.any(String), - expect.any(Function), - ) - expect(targetRemoveSpy).toHaveBeenCalledWith( - expect.any(String), - expect.any(Function), - ) - }) - }) - - describe('on', () => { - it('should register an event with the pluginId (event => test:event)', () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - - const eventBusSpy = vi.spyOn(clientBusEmitTarget, 'addEventListener') - client.on('event', () => {}) - expect(eventBusSpy).toHaveBeenCalledWith( - 'test:event', - expect.any(Function), - ) - }) - it('should register an event listener for the specified event and get events when they are emitted', () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - const cleanup = client.on('event', eventHandler) - client.emit('event', { foo: 'bar' }) - expect(eventHandler).toHaveBeenCalledWith({ - type: 'test:event', - payload: { foo: 'bar' }, - pluginId: 'test', - }) - cleanup() - }) - - it("shouldn't get an event if unregistered before it comes", () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - const cleanup = client.on('event', eventHandler) - cleanup() - client.emit('event', { foo: 'bar' }) - expect(eventHandler).not.toHaveBeenCalled() - }) - - it("shouldn't get an event if it's not registered to it", () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - client.on('event', eventHandler) - client.emit('other_event', { foo: 'bar' }) - expect(eventHandler).not.toHaveBeenCalled() - }) - }) - - describe('emit', () => { - it('should emit an event with the correct type and payload to the event bus', () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - client.on('event', eventHandler) - client.emit('event', { foo: 'bar' }) - expect(eventHandler).toHaveBeenCalledWith({ - type: 'test:event', - payload: { foo: 'bar' }, - pluginId: 'test', - }) - }) - }) - - describe('onAll', () => { - it('should listen to all events even if they do not come from the registered client', () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - client.onAll(eventHandler) - client.emit('event', { foo: 'bar' }) - expect(eventHandler).toHaveBeenCalledWith({ - type: 'test:event', - payload: { foo: 'bar' }, - pluginId: 'test', - }) - clientBusEmitTarget.dispatchEvent( - new CustomEvent('tanstack-dispatch-event', { - detail: { - type: 'other-plugin', - payload: { foo: 'bar' }, - }, - }), - ) - - expect(eventHandler).lastCalledWith({ - type: 'other-plugin', - payload: { foo: 'bar' }, - }) - }) - }) - - describe('queued events', () => { - it('emits queued events when connected to the event bus', async () => { - bus.stop() - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - client.on('event', eventHandler) - client.emit('event', { foo: 'bar' }) - - bus.start() - // wait to connect to the bus - await new Promise((resolve) => setTimeout(resolve, 500)) - expect(eventHandler).toHaveBeenCalledWith({ - type: 'test:event', - payload: { foo: 'bar' }, - pluginId: 'test', - }) - }) - }) - describe('onAllPluginEvents', () => { - it('should listen to all events that come from the plugin', () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - client.onAllPluginEvents(eventHandler) - client.emit('event', { foo: 'bar' }) - client.emit('event2', { foo: 'bar' }) - expect(eventHandler).nthCalledWith(1, { - type: 'test:event', - payload: { foo: 'bar' }, - pluginId: 'test', - }) - expect(eventHandler).nthCalledWith(2, { - type: 'test:event2', - payload: { foo: 'bar' }, - pluginId: 'test', - }) - }) - - it('should ignore events that do not come from the plugin', () => { - const client = new EventClient({ - debug: false, - pluginId: 'test', - }) - const eventHandler = vi.fn() - client.onAllPluginEvents(eventHandler) - clientBusEmitTarget.dispatchEvent( - new CustomEvent('tanstack-dispatch-event', { - detail: { - type: 'other-plugin', - payload: { foo: 'bar' }, - }, - }), - ) - expect(eventHandler).not.toHaveBeenCalled() - }) - }) -}) diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json index 1838eb23..811aef46 100644 --- a/packages/devtools-utils/tsconfig.json +++ b/packages/devtools-utils/tsconfig.json @@ -1,4 +1,7 @@ { + "compilerOptions": { + "jsx": "preserve" + }, "extends": "../../tsconfig.json", "include": [ "src", diff --git a/packages/devtools-utils/vite.config.ts b/packages/devtools-utils/vite.config.ts index 79dae7cd..8ed5bc64 100644 --- a/packages/devtools-utils/vite.config.ts +++ b/packages/devtools-utils/vite.config.ts @@ -17,7 +17,8 @@ const config = defineConfig({ export default mergeConfig( config, tanstackViteConfig({ - entry: ['./src/index.ts'], + entry: ['./src/react/index.ts', "./src/solid/index.ts"], srcDir: './src', + cjs: false }), ) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c20a4df4..cc5820f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -468,6 +468,8 @@ importers: specifier: ^4.2.4 version: 4.2.4 + examples/react/start/generated/prisma: {} + examples/react/time-travel: dependencies: '@tanstack/devtools-event-client': @@ -610,7 +612,20 @@ importers: specifier: ^2.11.8 version: 2.11.8(@testing-library/jest-dom@6.8.0)(solid-js@1.9.9)(vite@7.1.7(@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)) - packages/devtools-utils: {} + packages/devtools-utils: + dependencies: + '@tanstack/devtools-ui': + specifier: workspace:^ + version: link:../devtools-ui + '@types/react': + specifier: '>=17.0.0' + version: 19.1.13 + react: + specifier: '>=17.0.0' + version: 19.1.1 + solid-js: + specifier: '>=1.9.7' + version: 1.9.9 packages/devtools-vite: dependencies: From 3a5c518ad3fb2cd81a6a3411df570146343a370b Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 09:23:27 +0000 Subject: [PATCH 04/21] ci: apply automated fixes --- knip.json | 20 +-- packages/devtools-utils/package.json | 2 +- packages/devtools-utils/src/react/index.ts | 2 +- .../devtools-utils/src/react/panel.test.tsx | 34 ++-- packages/devtools-utils/src/react/panel.tsx | 29 ++-- .../devtools-utils/src/solid/class.test.tsx | 159 ++++++++++-------- packages/devtools-utils/src/solid/class.tsx | 13 +- packages/devtools-utils/src/solid/index.ts | 4 +- packages/devtools-utils/src/solid/panel.tsx | 13 +- packages/devtools-utils/vite.config.ts | 4 +- 10 files changed, 147 insertions(+), 133 deletions(-) diff --git a/knip.json b/knip.json index a280a8aa..89e0afd4 100644 --- a/knip.json +++ b/knip.json @@ -1,23 +1,13 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "ignoreDependencies": [ - "@faker-js/faker" - ], - "ignoreWorkspaces": [ - "examples/**" - ], + "ignoreDependencies": ["@faker-js/faker"], + "ignoreWorkspaces": ["examples/**"], "workspaces": { "packages/devtools-utils": { - "ignoreDependencies": [ - "react", - "solid-js", - "@types/react" - ] + "ignoreDependencies": ["react", "solid-js", "@types/react"] }, "packages/solid-devtools": { - "ignore": [ - "**/core.tsx" - ] + "ignore": ["**/core.tsx"] } } -} \ No newline at end of file +} diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index c2f282bd..d22f40b2 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -70,4 +70,4 @@ "test:build": "publint --strict", "build": "vite build" } -} \ No newline at end of file +} diff --git a/packages/devtools-utils/src/react/index.ts b/packages/devtools-utils/src/react/index.ts index 5347230f..aa0566fa 100644 --- a/packages/devtools-utils/src/react/index.ts +++ b/packages/devtools-utils/src/react/index.ts @@ -1 +1 @@ -export * from './panel' \ No newline at end of file +export * from './panel' diff --git a/packages/devtools-utils/src/react/panel.test.tsx b/packages/devtools-utils/src/react/panel.test.tsx index 01c9831f..d240b0e0 100644 --- a/packages/devtools-utils/src/react/panel.test.tsx +++ b/packages/devtools-utils/src/react/panel.test.tsx @@ -1,17 +1,21 @@ -import { describe, expect, it } from "vitest"; -import { createReactPanel } from "./panel"; +import { describe, expect, it } from 'vitest' +import { createReactPanel } from './panel' +describe('createReactPanel', () => { + it('should create a React component when provided with a package name', () => { + const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel( + '@tanstack/devtools-react', + ) + expect(ReactDevtoolsPanel).toBeDefined() + expect(NoOpReactDevtoolsPanel).toBeDefined() + }) -describe("createReactPanel", () => { - it("should create a React component when provided with a package name", () => { - const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react'); - expect(ReactDevtoolsPanel).toBeDefined(); - expect(NoOpReactDevtoolsPanel).toBeDefined(); - }); - - it("should create a React component when provided with a different import name", () => { - const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react', 'default'); - expect(ReactDevtoolsPanel).toBeDefined(); - expect(NoOpReactDevtoolsPanel).toBeDefined(); - }); -}); \ No newline at end of file + it('should create a React component when provided with a different import name', () => { + const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel( + '@tanstack/devtools-react', + 'default', + ) + expect(ReactDevtoolsPanel).toBeDefined() + expect(NoOpReactDevtoolsPanel).toBeDefined() + }) +}) diff --git a/packages/devtools-utils/src/react/panel.tsx b/packages/devtools-utils/src/react/panel.tsx index 789d46ff..09299ab9 100644 --- a/packages/devtools-utils/src/react/panel.tsx +++ b/packages/devtools-utils/src/react/panel.tsx @@ -21,23 +21,28 @@ export interface DevtoolsPanelProps { * const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel('@tanstack/devtools-react', 'DevtoolsCore') * ``` */ -export function createReactPanel void - unmount: () => void -}>(devtoolsPackageName: string, importName = 'default') { +export function createReactPanel< + TComponentProps extends DevtoolsPanelProps | undefined, + TCoreDevtoolsClass extends { + mount: (el: HTMLElement, theme: 'light' | 'dark') => void + unmount: () => void + }, +>(devtoolsPackageName: string, importName = 'default') { function Panel(props: TComponentProps) { const devToolRef = useRef(null) const devtools = useRef(null) useEffect(() => { if (devtools.current) return - import(/* @vite-ignore */devtoolsPackageName).then(({ [importName]: Core }) => { - devtools.current = new Core() + import(/* @vite-ignore */ devtoolsPackageName).then( + ({ [importName]: Core }) => { + devtools.current = new Core() - if (devToolRef.current && devtools.current) { - devtools.current.mount(devToolRef.current, props?.theme ?? 'dark') - } - }) + if (devToolRef.current && devtools.current) { + devtools.current.mount(devToolRef.current, props?.theme ?? 'dark') + } + }, + ) return () => { devtools.current?.unmount() @@ -48,7 +53,7 @@ export function createReactPanel fn()); -const renderMock = vi.fn(); -const portalMock = vi.fn((props: any) =>
{props.children}
); +const lazyImportMock = vi.fn((fn) => fn()) +const renderMock = vi.fn() +const portalMock = vi.fn((props: any) =>
{props.children}
) -vi.mock("solid-js", async () => { - const actual = await vi.importActual("solid-js"); +vi.mock('solid-js', async () => { + const actual = await vi.importActual('solid-js') return { ...actual, lazy: lazyImportMock, } -}); +}) -vi.mock("solid-js/web", async () => { - const actual = await vi.importActual("solid-js/web"); +vi.mock('solid-js/web', async () => { + const actual = await vi.importActual('solid-js/web') return { ...actual, render: renderMock, Portal: portalMock, - }; -}); - + } +}) -describe("constructCoreClass", () => { +describe('constructCoreClass', () => { beforeEach(() => { - vi.clearAllMocks(); + vi.clearAllMocks() + }) + it('should export DevtoolsCore and NoOpDevtoolsCore classes and make no calls to Solid.js primitives', () => { + const [DevtoolsCore, NoOpDevtoolsCore] = constructCoreClass( + '@test/devtools-mock-package', + ) + expect(DevtoolsCore).toBeDefined() + expect(NoOpDevtoolsCore).toBeDefined() + expect(lazyImportMock).not.toHaveBeenCalled() }) - it("should export DevtoolsCore and NoOpDevtoolsCore classes and make no calls to Solid.js primitives", () => { - const [DevtoolsCore, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); - expect(DevtoolsCore).toBeDefined(); - expect(NoOpDevtoolsCore).toBeDefined(); - expect(lazyImportMock).not.toHaveBeenCalled(); - }); - - it("DevtoolsCore should call solid primitives when mount is called", async () => { - const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); - const instance = new DevtoolsCore(); - await instance.mount(document.createElement("div"), "dark") - expect(renderMock).toHaveBeenCalled(); - }); - - it("DevtoolsCore should throw if mount is called twice without unmounting", async () => { - const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); - const instance = new DevtoolsCore(); - await instance.mount(document.createElement("div"), "dark") - await expect(instance.mount(document.createElement("div"), "dark")).rejects.toThrow("Devtools is already mounted"); - }); - it("DevtoolsCore should throw if unmount is called before mount", () => { - const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); - const instance = new DevtoolsCore(); - expect(() => instance.unmount()).toThrow("Devtools is not mounted"); - }); + it('DevtoolsCore should call solid primitives when mount is called', async () => { + const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const instance = new DevtoolsCore() + await instance.mount(document.createElement('div'), 'dark') + expect(renderMock).toHaveBeenCalled() + }) - it("DevtoolsCore should allow mount after unmount", async () => { - const [DevtoolsCore, _] = constructCoreClass("@test/devtools-mock-package"); - const instance = new DevtoolsCore(); - await instance.mount(document.createElement("div"), "dark") - instance.unmount(); - await expect(instance.mount(document.createElement("div"), "dark")).resolves.not.toThrow(); - }); + it('DevtoolsCore should throw if mount is called twice without unmounting', async () => { + const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const instance = new DevtoolsCore() + await instance.mount(document.createElement('div'), 'dark') + await expect( + instance.mount(document.createElement('div'), 'dark'), + ).rejects.toThrow('Devtools is already mounted') + }) + it('DevtoolsCore should throw if unmount is called before mount', () => { + const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const instance = new DevtoolsCore() + expect(() => instance.unmount()).toThrow('Devtools is not mounted') + }) + it('DevtoolsCore should allow mount after unmount', async () => { + const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const instance = new DevtoolsCore() + await instance.mount(document.createElement('div'), 'dark') + instance.unmount() + await expect( + instance.mount(document.createElement('div'), 'dark'), + ).resolves.not.toThrow() + }) - it("NoOpDevtoolsCore should not call any solid primitives when mount is called", async () => { - const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); - const noOpInstance = new NoOpDevtoolsCore(); - await noOpInstance.mount(document.createElement("div"), "dark") + it('NoOpDevtoolsCore should not call any solid primitives when mount is called', async () => { + const [_, NoOpDevtoolsCore] = constructCoreClass( + '@test/devtools-mock-package', + ) + const noOpInstance = new NoOpDevtoolsCore() + await noOpInstance.mount(document.createElement('div'), 'dark') - expect(lazyImportMock).not.toHaveBeenCalled(); - expect(renderMock).not.toHaveBeenCalled(); - expect(portalMock).not.toHaveBeenCalled(); - }); + expect(lazyImportMock).not.toHaveBeenCalled() + expect(renderMock).not.toHaveBeenCalled() + expect(portalMock).not.toHaveBeenCalled() + }) - it("NoOpDevtoolsCore should not throw if mount is called multiple times", async () => { - const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); - const noOpInstance = new NoOpDevtoolsCore(); - await noOpInstance.mount(document.createElement("div"), "dark") - await expect(noOpInstance.mount(document.createElement("div"), "dark")).resolves.not.toThrow(); - }); + it('NoOpDevtoolsCore should not throw if mount is called multiple times', async () => { + const [_, NoOpDevtoolsCore] = constructCoreClass( + '@test/devtools-mock-package', + ) + const noOpInstance = new NoOpDevtoolsCore() + await noOpInstance.mount(document.createElement('div'), 'dark') + await expect( + noOpInstance.mount(document.createElement('div'), 'dark'), + ).resolves.not.toThrow() + }) - it("NoOpDevtoolsCore should not throw if unmount is called before mount", () => { - const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); - const noOpInstance = new NoOpDevtoolsCore(); - expect(() => noOpInstance.unmount()).not.toThrow(); - }); + it('NoOpDevtoolsCore should not throw if unmount is called before mount', () => { + const [_, NoOpDevtoolsCore] = constructCoreClass( + '@test/devtools-mock-package', + ) + const noOpInstance = new NoOpDevtoolsCore() + expect(() => noOpInstance.unmount()).not.toThrow() + }) - it("NoOpDevtoolsCore should not throw if unmount is called after mount", async () => { - const [_, NoOpDevtoolsCore] = constructCoreClass("@test/devtools-mock-package"); - const noOpInstance = new NoOpDevtoolsCore(); - await noOpInstance.mount(document.createElement("div"), "dark") - expect(() => noOpInstance.unmount()).not.toThrow(); - }); -}); \ No newline at end of file + it('NoOpDevtoolsCore should not throw if unmount is called after mount', async () => { + const [_, NoOpDevtoolsCore] = constructCoreClass( + '@test/devtools-mock-package', + ) + const noOpInstance = new NoOpDevtoolsCore() + await noOpInstance.mount(document.createElement('div'), 'dark') + expect(() => noOpInstance.unmount()).not.toThrow() + }) +}) diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx index 563b8681..86c84fb4 100644 --- a/packages/devtools-utils/src/solid/class.tsx +++ b/packages/devtools-utils/src/solid/class.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource solid-js - we use Solid.js as JSX here */ /** - * Constructs the core class for the Devtools. + * Constructs the core class for the Devtools. * This utility is used to construct a lazy loaded Solid component for the Devtools. * It returns a tuple containing the main DevtoolsCore class and a NoOpDevtoolsCore class. * The NoOpDevtoolsCore class is a no-op implementation that can be used for production if you want to explicitly exclude @@ -16,8 +16,7 @@ export function constructCoreClass(importPath: string) { #Component: any #ThemeProvider: any - constructor() { - } + constructor() {} async mount(el: T, theme: 'light' | 'dark') { const { lazy } = await import('solid-js') @@ -27,7 +26,7 @@ export function constructCoreClass(importPath: string) { } const mountTo = el const dispose = render(() => { - this.#Component = lazy(() => import(/** @vite-ignore */importPath)) + this.#Component = lazy(() => import(/** @vite-ignore */ importPath)) const Devtools = this.#Component this.#ThemeProvider = lazy(() => import('@tanstack/devtools-ui').then((mod) => ({ @@ -62,8 +61,8 @@ export function constructCoreClass(importPath: string) { constructor() { super() } - async mount(_el: T, _theme: 'light' | 'dark') { } - unmount() { } + async mount(_el: T, _theme: 'light' | 'dark') {} + unmount() {} } return [DevtoolsCore, NoOpDevtoolsCore] as const -} \ No newline at end of file +} diff --git a/packages/devtools-utils/src/solid/index.ts b/packages/devtools-utils/src/solid/index.ts index c508a0ac..f30a8c58 100644 --- a/packages/devtools-utils/src/solid/index.ts +++ b/packages/devtools-utils/src/solid/index.ts @@ -1,2 +1,2 @@ -export * from "./class" -export * from './panel' \ No newline at end of file +export * from './class' +export * from './panel' diff --git a/packages/devtools-utils/src/solid/panel.tsx b/packages/devtools-utils/src/solid/panel.tsx index 9d9599e2..1a8f261c 100644 --- a/packages/devtools-utils/src/solid/panel.tsx +++ b/packages/devtools-utils/src/solid/panel.tsx @@ -6,12 +6,16 @@ export interface DevtoolsPanelProps { theme?: 'light' | 'dark' } -export function createSolidPanel(importPath: string, importName = "default") { +export function createSolidPanel< + TComponentProps extends DevtoolsPanelProps | undefined, +>(importPath: string, importName = 'default') { function Panel(props: TComponentProps) { let devToolRef: HTMLDivElement | undefined onMount(async () => { - const devtools = await import(/* @vite-ignore */importPath).then(mod => new mod[importName]()) + const devtools = await import(/* @vite-ignore */ importPath).then( + (mod) => new mod[importName](), + ) if (devToolRef) { devtools.mount(devToolRef, props?.theme ?? 'dark') @@ -25,9 +29,8 @@ export function createSolidPanel Date: Tue, 23 Sep 2025 11:32:05 +0200 Subject: [PATCH 05/21] add plugin utility --- packages/devtools-utils/src/react/index.ts | 1 + packages/devtools-utils/src/react/plugin.tsx | 18 ++++++++++++++++ packages/devtools-utils/src/solid/index.ts | 1 + packages/devtools-utils/src/solid/plugin.tsx | 22 ++++++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 packages/devtools-utils/src/react/plugin.tsx create mode 100644 packages/devtools-utils/src/solid/plugin.tsx diff --git a/packages/devtools-utils/src/react/index.ts b/packages/devtools-utils/src/react/index.ts index aa0566fa..91f64a70 100644 --- a/packages/devtools-utils/src/react/index.ts +++ b/packages/devtools-utils/src/react/index.ts @@ -1 +1,2 @@ export * from './panel' +export * from './plugin' \ No newline at end of file diff --git a/packages/devtools-utils/src/react/plugin.tsx b/packages/devtools-utils/src/react/plugin.tsx new file mode 100644 index 00000000..99670cfd --- /dev/null +++ b/packages/devtools-utils/src/react/plugin.tsx @@ -0,0 +1,18 @@ +import type { JSX } from "react" +import type { DevtoolsPanelProps } from "./panel" + +export function createReactPlugin(name: string, Component: (props: DevtoolsPanelProps) => JSX.Element) { + function Plugin() { + return { + name: name, + render: (_el: HTMLElement, theme: 'light' | 'dark') => , + } + } + function NoOpPlugin() { + return { + name: name, + render: (_el: HTMLElement, _theme: 'light' | 'dark') => null, + } + } + return [Plugin, NoOpPlugin] as const +} \ No newline at end of file diff --git a/packages/devtools-utils/src/solid/index.ts b/packages/devtools-utils/src/solid/index.ts index f30a8c58..6c2ecbf7 100644 --- a/packages/devtools-utils/src/solid/index.ts +++ b/packages/devtools-utils/src/solid/index.ts @@ -1,2 +1,3 @@ export * from './class' export * from './panel' +export * from "./plugin" \ No newline at end of file diff --git a/packages/devtools-utils/src/solid/plugin.tsx b/packages/devtools-utils/src/solid/plugin.tsx new file mode 100644 index 00000000..b00ea9ae --- /dev/null +++ b/packages/devtools-utils/src/solid/plugin.tsx @@ -0,0 +1,22 @@ +/** @jsxImportSource solid-js - we use Solid.js as JSX here */ + +import type { JSX } from "solid-js"; +import type { DevtoolsPanelProps } from "./panel"; + +export function createSolidPlugin(name: string, Component: (props: DevtoolsPanelProps) => JSX.Element) { + function Plugin() { + return { + name: name, + render: (_el: HTMLElement, theme: 'light' | 'dark') => { + return ; + }, + } + } + function NoOpPlugin() { + return { + name: name, + render: (_el: HTMLElement, _theme: 'light' | 'dark') => null, + } + } + return [Plugin, NoOpPlugin] as const +} \ No newline at end of file From 18fc9a9c627f47db916cb0351b00b0e786d6d46b Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 09:33:11 +0000 Subject: [PATCH 06/21] ci: apply automated fixes --- packages/devtools-utils/src/react/index.ts | 2 +- packages/devtools-utils/src/react/plugin.tsx | 15 ++++++++++----- packages/devtools-utils/src/solid/index.ts | 2 +- packages/devtools-utils/src/solid/plugin.tsx | 13 ++++++++----- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/devtools-utils/src/react/index.ts b/packages/devtools-utils/src/react/index.ts index 91f64a70..37bc5ffd 100644 --- a/packages/devtools-utils/src/react/index.ts +++ b/packages/devtools-utils/src/react/index.ts @@ -1,2 +1,2 @@ export * from './panel' -export * from './plugin' \ No newline at end of file +export * from './plugin' diff --git a/packages/devtools-utils/src/react/plugin.tsx b/packages/devtools-utils/src/react/plugin.tsx index 99670cfd..f4743f00 100644 --- a/packages/devtools-utils/src/react/plugin.tsx +++ b/packages/devtools-utils/src/react/plugin.tsx @@ -1,11 +1,16 @@ -import type { JSX } from "react" -import type { DevtoolsPanelProps } from "./panel" +import type { JSX } from 'react' +import type { DevtoolsPanelProps } from './panel' -export function createReactPlugin(name: string, Component: (props: DevtoolsPanelProps) => JSX.Element) { +export function createReactPlugin( + name: string, + Component: (props: DevtoolsPanelProps) => JSX.Element, +) { function Plugin() { return { name: name, - render: (_el: HTMLElement, theme: 'light' | 'dark') => , + render: (_el: HTMLElement, theme: 'light' | 'dark') => ( + + ), } } function NoOpPlugin() { @@ -15,4 +20,4 @@ export function createReactPlugin(name: string, Component: (props: DevtoolsPanel } } return [Plugin, NoOpPlugin] as const -} \ No newline at end of file +} diff --git a/packages/devtools-utils/src/solid/index.ts b/packages/devtools-utils/src/solid/index.ts index 6c2ecbf7..ca6ccadc 100644 --- a/packages/devtools-utils/src/solid/index.ts +++ b/packages/devtools-utils/src/solid/index.ts @@ -1,3 +1,3 @@ export * from './class' export * from './panel' -export * from "./plugin" \ No newline at end of file +export * from './plugin' diff --git a/packages/devtools-utils/src/solid/plugin.tsx b/packages/devtools-utils/src/solid/plugin.tsx index b00ea9ae..f09ffc1a 100644 --- a/packages/devtools-utils/src/solid/plugin.tsx +++ b/packages/devtools-utils/src/solid/plugin.tsx @@ -1,14 +1,17 @@ /** @jsxImportSource solid-js - we use Solid.js as JSX here */ -import type { JSX } from "solid-js"; -import type { DevtoolsPanelProps } from "./panel"; +import type { JSX } from 'solid-js' +import type { DevtoolsPanelProps } from './panel' -export function createSolidPlugin(name: string, Component: (props: DevtoolsPanelProps) => JSX.Element) { +export function createSolidPlugin( + name: string, + Component: (props: DevtoolsPanelProps) => JSX.Element, +) { function Plugin() { return { name: name, render: (_el: HTMLElement, theme: 'light' | 'dark') => { - return ; + return }, } } @@ -19,4 +22,4 @@ export function createSolidPlugin(name: string, Component: (props: DevtoolsPanel } } return [Plugin, NoOpPlugin] as const -} \ No newline at end of file +} From 7506426d77ce54899188ab4ec7f6edf1c66ac7c8 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 11:42:58 +0200 Subject: [PATCH 07/21] fix type incosistencies --- packages/devtools-utils/src/react/panel.tsx | 4 ++-- packages/devtools-utils/src/react/plugin.tsx | 2 +- packages/devtools-utils/src/solid/panel.tsx | 4 ++-- packages/devtools-utils/src/solid/plugin.tsx | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/devtools-utils/src/react/panel.tsx b/packages/devtools-utils/src/react/panel.tsx index 09299ab9..d62ac297 100644 --- a/packages/devtools-utils/src/react/panel.tsx +++ b/packages/devtools-utils/src/react/panel.tsx @@ -52,8 +52,8 @@ export function createReactPanel< return
} - function NoOpPanel() { - return null + function NoOpPanel(_props: TComponentProps) { + return <> } return [Panel, NoOpPanel] as const } diff --git a/packages/devtools-utils/src/react/plugin.tsx b/packages/devtools-utils/src/react/plugin.tsx index f4743f00..d911bdce 100644 --- a/packages/devtools-utils/src/react/plugin.tsx +++ b/packages/devtools-utils/src/react/plugin.tsx @@ -16,7 +16,7 @@ export function createReactPlugin( function NoOpPlugin() { return { name: name, - render: (_el: HTMLElement, _theme: 'light' | 'dark') => null, + render: (_el: HTMLElement, _theme: 'light' | 'dark') => <>, } } return [Plugin, NoOpPlugin] as const diff --git a/packages/devtools-utils/src/solid/panel.tsx b/packages/devtools-utils/src/solid/panel.tsx index 1a8f261c..d65a7dc4 100644 --- a/packages/devtools-utils/src/solid/panel.tsx +++ b/packages/devtools-utils/src/solid/panel.tsx @@ -28,8 +28,8 @@ export function createSolidPanel< return
} - function NoOpPanel() { - return null + function NoOpPanel(_props: TComponentProps) { + return <> } return [Panel, NoOpPanel] as const diff --git a/packages/devtools-utils/src/solid/plugin.tsx b/packages/devtools-utils/src/solid/plugin.tsx index f09ffc1a..967dfac4 100644 --- a/packages/devtools-utils/src/solid/plugin.tsx +++ b/packages/devtools-utils/src/solid/plugin.tsx @@ -18,7 +18,7 @@ export function createSolidPlugin( function NoOpPlugin() { return { name: name, - render: (_el: HTMLElement, _theme: 'light' | 'dark') => null, + render: (_el: HTMLElement, _theme: 'light' | 'dark') => <>, } } return [Plugin, NoOpPlugin] as const From 25d19810bda20880019ca949edadcf3a0a487e88 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 12:29:44 +0200 Subject: [PATCH 08/21] fix bundling isues --- knip.json | 28 ++++++++++++++++---- packages/devtools-utils/package.json | 16 +++++------ packages/devtools-utils/src/react/panel.tsx | 2 +- packages/devtools-utils/tsconfig.json | 6 +++-- packages/devtools-utils/tsconfig.solid.json | 14 ++++++++++ packages/devtools-utils/vite.config.solid.ts | 26 ++++++++++++++++++ packages/devtools-utils/vite.config.ts | 8 +++--- pnpm-lock.yaml | 4 +-- 8 files changed, 83 insertions(+), 21 deletions(-) create mode 100644 packages/devtools-utils/tsconfig.solid.json create mode 100644 packages/devtools-utils/vite.config.solid.ts diff --git a/knip.json b/knip.json index 89e0afd4..94280f49 100644 --- a/knip.json +++ b/knip.json @@ -1,13 +1,31 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "ignoreDependencies": ["@faker-js/faker"], - "ignoreWorkspaces": ["examples/**"], + "ignoreDependencies": [ + "@faker-js/faker" + ], + "ignoreWorkspaces": [ + "examples/**" + ], "workspaces": { "packages/devtools-utils": { - "ignoreDependencies": ["react", "solid-js", "@types/react"] + "ignoreDependencies": [ + "react", + "solid-js", + "@types/react" + ], + "entry": [ + "**/vite.config.solid.ts", + "**/src/solid/**" + ], + "project": [ + "**/vite.config.solid.ts", + "**/src/solid/**" + ] }, "packages/solid-devtools": { - "ignore": ["**/core.tsx"] + "ignore": [ + "**/core.tsx" + ] } } -} +} \ No newline at end of file diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index d22f40b2..8b7aaabc 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -21,14 +21,14 @@ "exports": { "./react": { "import": { - "types": "./dist/esm/react/index.d.ts", - "default": "./dist/esm/react/index.js" + "types": "./dist/react/esm/index.d.ts", + "default": "./dist/react/esm/index.js" } }, "./solid": { "import": { - "types": "./dist/esm/solid/index.d.ts", - "default": "./dist/esm/solid/index.js" + "types": "./dist/solid/esm/index.d.ts", + "default": "./dist/solid/esm/index.js" } }, "./package.json": "./package.json" @@ -41,8 +41,8 @@ "@tanstack/devtools-ui": "workspace:^" }, "peerDependencies": { - "@types/react": ">=17.0.0", - "react": ">=17.0.0", + "@types/react": ">=19.0.0", + "react": ">=19.0.0", "solid-js": ">=1.9.7" }, "peerDependenciesMeta": { @@ -68,6 +68,6 @@ "test:lib:dev": "pnpm test:lib --watch", "test:types": "tsc", "test:build": "publint --strict", - "build": "vite build" + "build": "vite build && vite build --config ./vite.config.solid.ts" } -} +} \ No newline at end of file diff --git a/packages/devtools-utils/src/react/panel.tsx b/packages/devtools-utils/src/react/panel.tsx index d62ac297..0e9aad7d 100644 --- a/packages/devtools-utils/src/react/panel.tsx +++ b/packages/devtools-utils/src/react/panel.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react' +import { useEffect, useRef } from 'react' export interface DevtoolsPanelProps { theme?: 'light' | 'dark' diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json index 811aef46..5c481de0 100644 --- a/packages/devtools-utils/tsconfig.json +++ b/packages/devtools-utils/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { - "jsx": "preserve" + "jsx": "react-jsx", + "jsxImportSource": "react" }, "extends": "../../tsconfig.json", "include": [ @@ -8,6 +9,7 @@ "eslint.config.js", "vite.config.ts", "tests", - "src/server" + "src/server", + "vite.config.solid.ts" ] } diff --git a/packages/devtools-utils/tsconfig.solid.json b/packages/devtools-utils/tsconfig.solid.json new file mode 100644 index 00000000..2e3760c2 --- /dev/null +++ b/packages/devtools-utils/tsconfig.solid.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "jsx": "preserve", + "jsxImportSource": "solid-js", + }, + "extends": "../../tsconfig.json", + "include": [ + "src", + "eslint.config.js", + "vite.config.ts", + "tests", + "src/server" + ] +} diff --git a/packages/devtools-utils/vite.config.solid.ts b/packages/devtools-utils/vite.config.solid.ts new file mode 100644 index 00000000..2b420ec4 --- /dev/null +++ b/packages/devtools-utils/vite.config.solid.ts @@ -0,0 +1,26 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { tanstackViteConfig } from '@tanstack/config/vite' +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/solid/index.ts',], + srcDir: './src/solid', + tsconfigPath: './tsconfig.json', + outDir: './dist/solid', + cjs: false + }), +) diff --git a/packages/devtools-utils/vite.config.ts b/packages/devtools-utils/vite.config.ts index 9668abfc..58d3908a 100644 --- a/packages/devtools-utils/vite.config.ts +++ b/packages/devtools-utils/vite.config.ts @@ -17,8 +17,10 @@ const config = defineConfig({ export default mergeConfig( config, tanstackViteConfig({ - entry: ['./src/react/index.ts', './src/solid/index.ts'], - srcDir: './src', - cjs: false, + entry: ['./src/react/index.ts',], + srcDir: './src/react', + tsconfigPath: './tsconfig.json', + outDir: './dist/react', + cjs: false }), ) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc5820f4..066ba300 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -618,10 +618,10 @@ importers: specifier: workspace:^ version: link:../devtools-ui '@types/react': - specifier: '>=17.0.0' + specifier: '>=19.0.0' version: 19.1.13 react: - specifier: '>=17.0.0' + specifier: '>=19.0.0' version: 19.1.1 solid-js: specifier: '>=1.9.7' From b1e8209100525bb60b50a1024505aa6881dacd63 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 10:30:29 +0000 Subject: [PATCH 09/21] ci: apply automated fixes --- knip.json | 30 +++++--------------- packages/devtools-utils/package.json | 2 +- packages/devtools-utils/tsconfig.solid.json | 2 +- packages/devtools-utils/vite.config.solid.ts | 4 +-- packages/devtools-utils/vite.config.ts | 4 +-- 5 files changed, 13 insertions(+), 29 deletions(-) diff --git a/knip.json b/knip.json index 94280f49..e8af5e1d 100644 --- a/knip.json +++ b/knip.json @@ -1,31 +1,15 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "ignoreDependencies": [ - "@faker-js/faker" - ], - "ignoreWorkspaces": [ - "examples/**" - ], + "ignoreDependencies": ["@faker-js/faker"], + "ignoreWorkspaces": ["examples/**"], "workspaces": { "packages/devtools-utils": { - "ignoreDependencies": [ - "react", - "solid-js", - "@types/react" - ], - "entry": [ - "**/vite.config.solid.ts", - "**/src/solid/**" - ], - "project": [ - "**/vite.config.solid.ts", - "**/src/solid/**" - ] + "ignoreDependencies": ["react", "solid-js", "@types/react"], + "entry": ["**/vite.config.solid.ts", "**/src/solid/**"], + "project": ["**/vite.config.solid.ts", "**/src/solid/**"] }, "packages/solid-devtools": { - "ignore": [ - "**/core.tsx" - ] + "ignore": ["**/core.tsx"] } } -} \ No newline at end of file +} diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index 8b7aaabc..7fa34c14 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -70,4 +70,4 @@ "test:build": "publint --strict", "build": "vite build && vite build --config ./vite.config.solid.ts" } -} \ No newline at end of file +} diff --git a/packages/devtools-utils/tsconfig.solid.json b/packages/devtools-utils/tsconfig.solid.json index 2e3760c2..42d45c36 100644 --- a/packages/devtools-utils/tsconfig.solid.json +++ b/packages/devtools-utils/tsconfig.solid.json @@ -1,7 +1,7 @@ { "compilerOptions": { "jsx": "preserve", - "jsxImportSource": "solid-js", + "jsxImportSource": "solid-js" }, "extends": "../../tsconfig.json", "include": [ diff --git a/packages/devtools-utils/vite.config.solid.ts b/packages/devtools-utils/vite.config.solid.ts index 2b420ec4..1ce83aca 100644 --- a/packages/devtools-utils/vite.config.solid.ts +++ b/packages/devtools-utils/vite.config.solid.ts @@ -17,10 +17,10 @@ const config = defineConfig({ export default mergeConfig( config, tanstackViteConfig({ - entry: ['./src/solid/index.ts',], + entry: ['./src/solid/index.ts'], srcDir: './src/solid', tsconfigPath: './tsconfig.json', outDir: './dist/solid', - cjs: false + cjs: false, }), ) diff --git a/packages/devtools-utils/vite.config.ts b/packages/devtools-utils/vite.config.ts index 58d3908a..d1a0347f 100644 --- a/packages/devtools-utils/vite.config.ts +++ b/packages/devtools-utils/vite.config.ts @@ -17,10 +17,10 @@ const config = defineConfig({ export default mergeConfig( config, tanstackViteConfig({ - entry: ['./src/react/index.ts',], + entry: ['./src/react/index.ts'], srcDir: './src/react', tsconfigPath: './tsconfig.json', outDir: './dist/react', - cjs: false + cjs: false, }), ) From 1a61e777b6e8e90a48ed030a5062cec972b5e3d4 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 13:49:29 +0200 Subject: [PATCH 10/21] test --- packages/devtools-utils/package.json | 7 +++++-- packages/devtools-utils/tsconfig.json | 6 ++++-- packages/devtools-utils/tsconfig.solid.json | 10 ++++------ packages/devtools-utils/vite.config.solid.ts | 12 ++++++++++-- packages/devtools-utils/vite.config.ts | 1 - pnpm-lock.yaml | 4 ++++ 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index 7fa34c14..ad322060 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -68,6 +68,9 @@ "test:lib:dev": "pnpm test:lib --watch", "test:types": "tsc", "test:build": "publint --strict", - "build": "vite build && vite build --config ./vite.config.solid.ts" + "build": "vite build && vite build --config ./vite.config.solid.ts " + }, + "devDependencies": { + "vite-plugin-solid": "^2.11.8" } -} +} \ No newline at end of file diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json index 5c481de0..42862a61 100644 --- a/packages/devtools-utils/tsconfig.json +++ b/packages/devtools-utils/tsconfig.json @@ -4,12 +4,14 @@ "jsxImportSource": "react" }, "extends": "../../tsconfig.json", + "exclude": [ + "src/solid/**" + ], "include": [ - "src", + "src/react", "eslint.config.js", "vite.config.ts", "tests", - "src/server", "vite.config.solid.ts" ] } diff --git a/packages/devtools-utils/tsconfig.solid.json b/packages/devtools-utils/tsconfig.solid.json index 42d45c36..69a0c671 100644 --- a/packages/devtools-utils/tsconfig.solid.json +++ b/packages/devtools-utils/tsconfig.solid.json @@ -1,14 +1,12 @@ { - "compilerOptions": { + "compilerOptions": { "jsx": "preserve", "jsxImportSource": "solid-js" + }, "extends": "../../tsconfig.json", "include": [ - "src", - "eslint.config.js", - "vite.config.ts", - "tests", - "src/server" + "src/solid/**" ] } + \ No newline at end of file diff --git a/packages/devtools-utils/vite.config.solid.ts b/packages/devtools-utils/vite.config.solid.ts index 1ce83aca..af8981af 100644 --- a/packages/devtools-utils/vite.config.solid.ts +++ b/packages/devtools-utils/vite.config.solid.ts @@ -1,9 +1,14 @@ import { defineConfig, mergeConfig } from 'vitest/config' import { tanstackViteConfig } from '@tanstack/config/vite' +import solid from "vite-plugin-solid" import packageJson from './package.json' +import tsconfig from './tsconfig.solid.json' + const config = defineConfig({ - plugins: [], + plugins: [solid({ + ssr: true + })], test: { name: packageJson.name, dir: './', @@ -12,6 +17,9 @@ const config = defineConfig({ setupFiles: ['./tests/test-setup.ts'], globals: true, }, + esbuild: { + tsconfigRaw: JSON.stringify(tsconfig) + } }) export default mergeConfig( @@ -19,7 +27,7 @@ export default mergeConfig( tanstackViteConfig({ entry: ['./src/solid/index.ts'], srcDir: './src/solid', - tsconfigPath: './tsconfig.json', + tsconfigPath: './tsconfig.solid.json', outDir: './dist/solid', cjs: false, }), diff --git a/packages/devtools-utils/vite.config.ts b/packages/devtools-utils/vite.config.ts index d1a0347f..8a74dc1e 100644 --- a/packages/devtools-utils/vite.config.ts +++ b/packages/devtools-utils/vite.config.ts @@ -19,7 +19,6 @@ export default mergeConfig( tanstackViteConfig({ entry: ['./src/react/index.ts'], srcDir: './src/react', - tsconfigPath: './tsconfig.json', outDir: './dist/react', cjs: false, }), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 066ba300..3d05b6c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -626,6 +626,10 @@ importers: solid-js: specifier: '>=1.9.7' version: 1.9.9 + devDependencies: + vite-plugin-solid: + specifier: ^2.11.8 + version: 2.11.8(@testing-library/jest-dom@6.8.0)(solid-js@1.9.9)(vite@7.1.7(@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)) packages/devtools-vite: dependencies: From 2c25bb44d3ef6d4d6797e92519bf003f72ed2cd6 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:50:24 +0000 Subject: [PATCH 11/21] ci: apply automated fixes --- packages/devtools-utils/package.json | 2 +- packages/devtools-utils/tsconfig.json | 4 +--- packages/devtools-utils/tsconfig.solid.json | 8 ++------ packages/devtools-utils/vite.config.solid.ts | 15 ++++++++------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index ad322060..518f3e29 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -73,4 +73,4 @@ "devDependencies": { "vite-plugin-solid": "^2.11.8" } -} \ No newline at end of file +} diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json index 42862a61..ebf55666 100644 --- a/packages/devtools-utils/tsconfig.json +++ b/packages/devtools-utils/tsconfig.json @@ -4,9 +4,7 @@ "jsxImportSource": "react" }, "extends": "../../tsconfig.json", - "exclude": [ - "src/solid/**" - ], + "exclude": ["src/solid/**"], "include": [ "src/react", "eslint.config.js", diff --git a/packages/devtools-utils/tsconfig.solid.json b/packages/devtools-utils/tsconfig.solid.json index 69a0c671..dd4f3ba9 100644 --- a/packages/devtools-utils/tsconfig.solid.json +++ b/packages/devtools-utils/tsconfig.solid.json @@ -1,12 +1,8 @@ { - "compilerOptions": { + "compilerOptions": { "jsx": "preserve", "jsxImportSource": "solid-js" - }, "extends": "../../tsconfig.json", - "include": [ - "src/solid/**" - ] + "include": ["src/solid/**"] } - \ No newline at end of file diff --git a/packages/devtools-utils/vite.config.solid.ts b/packages/devtools-utils/vite.config.solid.ts index af8981af..b19485cd 100644 --- a/packages/devtools-utils/vite.config.solid.ts +++ b/packages/devtools-utils/vite.config.solid.ts @@ -1,14 +1,15 @@ import { defineConfig, mergeConfig } from 'vitest/config' import { tanstackViteConfig } from '@tanstack/config/vite' -import solid from "vite-plugin-solid" +import solid from 'vite-plugin-solid' import packageJson from './package.json' import tsconfig from './tsconfig.solid.json' - const config = defineConfig({ - plugins: [solid({ - ssr: true - })], + plugins: [ + solid({ + ssr: true, + }), + ], test: { name: packageJson.name, dir: './', @@ -18,8 +19,8 @@ const config = defineConfig({ globals: true, }, esbuild: { - tsconfigRaw: JSON.stringify(tsconfig) - } + tsconfigRaw: JSON.stringify(tsconfig), + }, }) export default mergeConfig( From 0c619c81f922a81d6cacf6e0e37be0d44a204df6 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 14:01:38 +0200 Subject: [PATCH 12/21] test --- .../devtools-utils/src/react/panel.test.tsx | 21 ------------------- packages/devtools-utils/src/react/panel.tsx | 17 +++++++-------- packages/devtools-utils/src/solid/class.tsx | 8 ++++--- packages/devtools-utils/src/solid/panel.tsx | 10 ++++----- 4 files changed, 18 insertions(+), 38 deletions(-) delete mode 100644 packages/devtools-utils/src/react/panel.test.tsx diff --git a/packages/devtools-utils/src/react/panel.test.tsx b/packages/devtools-utils/src/react/panel.test.tsx deleted file mode 100644 index d240b0e0..00000000 --- a/packages/devtools-utils/src/react/panel.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { createReactPanel } from './panel' - -describe('createReactPanel', () => { - it('should create a React component when provided with a package name', () => { - const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel( - '@tanstack/devtools-react', - ) - expect(ReactDevtoolsPanel).toBeDefined() - expect(NoOpReactDevtoolsPanel).toBeDefined() - }) - - it('should create a React component when provided with a different import name', () => { - const [ReactDevtoolsPanel, NoOpReactDevtoolsPanel] = createReactPanel( - '@tanstack/devtools-react', - 'default', - ) - expect(ReactDevtoolsPanel).toBeDefined() - expect(NoOpReactDevtoolsPanel).toBeDefined() - }) -}) diff --git a/packages/devtools-utils/src/react/panel.tsx b/packages/devtools-utils/src/react/panel.tsx index 0e9aad7d..beece43f 100644 --- a/packages/devtools-utils/src/react/panel.tsx +++ b/packages/devtools-utils/src/react/panel.tsx @@ -27,22 +27,21 @@ export function createReactPanel< mount: (el: HTMLElement, theme: 'light' | 'dark') => void unmount: () => void }, ->(devtoolsPackageName: string, importName = 'default') { +>( + CoreClass: new () => TCoreDevtoolsClass +) { function Panel(props: TComponentProps) { const devToolRef = useRef(null) const devtools = useRef(null) useEffect(() => { if (devtools.current) return - import(/* @vite-ignore */ devtoolsPackageName).then( - ({ [importName]: Core }) => { - devtools.current = new Core() + devtools.current = new CoreClass() + + if (devToolRef.current) { + devtools.current.mount(devToolRef.current, props?.theme ?? 'dark') + } - if (devToolRef.current && devtools.current) { - devtools.current.mount(devToolRef.current, props?.theme ?? 'dark') - } - }, - ) return () => { devtools.current?.unmount() diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx index 86c84fb4..4db33e45 100644 --- a/packages/devtools-utils/src/solid/class.tsx +++ b/packages/devtools-utils/src/solid/class.tsx @@ -16,7 +16,7 @@ export function constructCoreClass(importPath: string) { #Component: any #ThemeProvider: any - constructor() {} + constructor() { } async mount(el: T, theme: 'light' | 'dark') { const { lazy } = await import('solid-js') @@ -61,8 +61,10 @@ export function constructCoreClass(importPath: string) { constructor() { super() } - async mount(_el: T, _theme: 'light' | 'dark') {} - unmount() {} + async mount(_el: T, _theme: 'light' | 'dark') { } + unmount() { } } return [DevtoolsCore, NoOpDevtoolsCore] as const } + +export type ClassType = ReturnType[0] \ No newline at end of file diff --git a/packages/devtools-utils/src/solid/panel.tsx b/packages/devtools-utils/src/solid/panel.tsx index d65a7dc4..89cdf20a 100644 --- a/packages/devtools-utils/src/solid/panel.tsx +++ b/packages/devtools-utils/src/solid/panel.tsx @@ -1,6 +1,7 @@ /** @jsxImportSource solid-js - we use Solid.js as JSX here */ import { onCleanup, onMount } from 'solid-js' +import type { ClassType } from './class' export interface DevtoolsPanelProps { theme?: 'light' | 'dark' @@ -8,14 +9,13 @@ export interface DevtoolsPanelProps { export function createSolidPanel< TComponentProps extends DevtoolsPanelProps | undefined, ->(importPath: string, importName = 'default') { +>(CoreClass: ClassType) { function Panel(props: TComponentProps) { let devToolRef: HTMLDivElement | undefined - onMount(async () => { - const devtools = await import(/* @vite-ignore */ importPath).then( - (mod) => new mod[importName](), - ) + onMount(() => { + const devtools = new CoreClass() + if (devToolRef) { devtools.mount(devToolRef, props?.theme ?? 'dark') From b4c8327abe3eea64ce53573da3875898ac05467e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:02:40 +0000 Subject: [PATCH 13/21] ci: apply automated fixes --- packages/devtools-utils/src/react/panel.tsx | 5 +---- packages/devtools-utils/src/solid/class.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/devtools-utils/src/react/panel.tsx b/packages/devtools-utils/src/react/panel.tsx index beece43f..c2353564 100644 --- a/packages/devtools-utils/src/react/panel.tsx +++ b/packages/devtools-utils/src/react/panel.tsx @@ -27,9 +27,7 @@ export function createReactPanel< mount: (el: HTMLElement, theme: 'light' | 'dark') => void unmount: () => void }, ->( - CoreClass: new () => TCoreDevtoolsClass -) { +>(CoreClass: new () => TCoreDevtoolsClass) { function Panel(props: TComponentProps) { const devToolRef = useRef(null) const devtools = useRef(null) @@ -42,7 +40,6 @@ export function createReactPanel< devtools.current.mount(devToolRef.current, props?.theme ?? 'dark') } - return () => { devtools.current?.unmount() } diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx index 4db33e45..527e62c9 100644 --- a/packages/devtools-utils/src/solid/class.tsx +++ b/packages/devtools-utils/src/solid/class.tsx @@ -16,7 +16,7 @@ export function constructCoreClass(importPath: string) { #Component: any #ThemeProvider: any - constructor() { } + constructor() {} async mount(el: T, theme: 'light' | 'dark') { const { lazy } = await import('solid-js') @@ -61,10 +61,10 @@ export function constructCoreClass(importPath: string) { constructor() { super() } - async mount(_el: T, _theme: 'light' | 'dark') { } - unmount() { } + async mount(_el: T, _theme: 'light' | 'dark') {} + unmount() {} } return [DevtoolsCore, NoOpDevtoolsCore] as const } -export type ClassType = ReturnType[0] \ No newline at end of file +export type ClassType = ReturnType[0] From 5110b20c8740ab165cf2bca82914d8486f0866e2 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 14:12:57 +0200 Subject: [PATCH 14/21] test --- .../devtools-utils/src/solid/class.test.tsx | 19 ++++++++++--------- packages/devtools-utils/src/solid/class.tsx | 13 ++++++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/devtools-utils/src/solid/class.test.tsx b/packages/devtools-utils/src/solid/class.test.tsx index 34a62126..5d862509 100644 --- a/packages/devtools-utils/src/solid/class.test.tsx +++ b/packages/devtools-utils/src/solid/class.test.tsx @@ -1,3 +1,4 @@ +/** @jsxImportSource solid-js - we use Solid.js as JSX here */ import { beforeEach, describe, expect, it, vi } from 'vitest' import { constructCoreClass } from './class' @@ -28,7 +29,7 @@ describe('constructCoreClass', () => { }) it('should export DevtoolsCore and NoOpDevtoolsCore classes and make no calls to Solid.js primitives', () => { const [DevtoolsCore, NoOpDevtoolsCore] = constructCoreClass( - '@test/devtools-mock-package', + () =>
Test Component
, ) expect(DevtoolsCore).toBeDefined() expect(NoOpDevtoolsCore).toBeDefined() @@ -36,14 +37,14 @@ describe('constructCoreClass', () => { }) it('DevtoolsCore should call solid primitives when mount is called', async () => { - const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) const instance = new DevtoolsCore() await instance.mount(document.createElement('div'), 'dark') expect(renderMock).toHaveBeenCalled() }) it('DevtoolsCore should throw if mount is called twice without unmounting', async () => { - const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) const instance = new DevtoolsCore() await instance.mount(document.createElement('div'), 'dark') await expect( @@ -52,13 +53,13 @@ describe('constructCoreClass', () => { }) it('DevtoolsCore should throw if unmount is called before mount', () => { - const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) const instance = new DevtoolsCore() expect(() => instance.unmount()).toThrow('Devtools is not mounted') }) it('DevtoolsCore should allow mount after unmount', async () => { - const [DevtoolsCore, _] = constructCoreClass('@test/devtools-mock-package') + const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) const instance = new DevtoolsCore() await instance.mount(document.createElement('div'), 'dark') instance.unmount() @@ -69,7 +70,7 @@ describe('constructCoreClass', () => { it('NoOpDevtoolsCore should not call any solid primitives when mount is called', async () => { const [_, NoOpDevtoolsCore] = constructCoreClass( - '@test/devtools-mock-package', + () =>
Test Component
, ) const noOpInstance = new NoOpDevtoolsCore() await noOpInstance.mount(document.createElement('div'), 'dark') @@ -81,7 +82,7 @@ describe('constructCoreClass', () => { it('NoOpDevtoolsCore should not throw if mount is called multiple times', async () => { const [_, NoOpDevtoolsCore] = constructCoreClass( - '@test/devtools-mock-package', + () =>
Test Component
, ) const noOpInstance = new NoOpDevtoolsCore() await noOpInstance.mount(document.createElement('div'), 'dark') @@ -92,7 +93,7 @@ describe('constructCoreClass', () => { it('NoOpDevtoolsCore should not throw if unmount is called before mount', () => { const [_, NoOpDevtoolsCore] = constructCoreClass( - '@test/devtools-mock-package', + () =>
Test Component
, ) const noOpInstance = new NoOpDevtoolsCore() expect(() => noOpInstance.unmount()).not.toThrow() @@ -100,7 +101,7 @@ describe('constructCoreClass', () => { it('NoOpDevtoolsCore should not throw if unmount is called after mount', async () => { const [_, NoOpDevtoolsCore] = constructCoreClass( - '@test/devtools-mock-package', + () =>
Test Component
, ) const noOpInstance = new NoOpDevtoolsCore() await noOpInstance.mount(document.createElement('div'), 'dark') diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx index 527e62c9..174c2f88 100644 --- a/packages/devtools-utils/src/solid/class.tsx +++ b/packages/devtools-utils/src/solid/class.tsx @@ -1,5 +1,7 @@ /** @jsxImportSource solid-js - we use Solid.js as JSX here */ +import type { JSX, } from 'solid-js' + /** * Constructs the core class for the Devtools. * This utility is used to construct a lazy loaded Solid component for the Devtools. @@ -9,14 +11,14 @@ * @param importPath The path to the Solid component to be lazily imported * @returns Tuple containing the DevtoolsCore class and a NoOpDevtoolsCore class */ -export function constructCoreClass(importPath: string) { +export function constructCoreClass(Component: () => JSX.Element) { class DevtoolsCore { #isMounted = false #dispose?: () => void #Component: any #ThemeProvider: any - constructor() {} + constructor() { } async mount(el: T, theme: 'light' | 'dark') { const { lazy } = await import('solid-js') @@ -26,7 +28,8 @@ export function constructCoreClass(importPath: string) { } const mountTo = el const dispose = render(() => { - this.#Component = lazy(() => import(/** @vite-ignore */ importPath)) + // eslint-disable-next-line @typescript-eslint/require-await + this.#Component = lazy(async () => ({ default: Component })) const Devtools = this.#Component this.#ThemeProvider = lazy(() => import('@tanstack/devtools-ui').then((mod) => ({ @@ -61,8 +64,8 @@ export function constructCoreClass(importPath: string) { constructor() { super() } - async mount(_el: T, _theme: 'light' | 'dark') {} - unmount() {} + async mount(_el: T, _theme: 'light' | 'dark') { } + unmount() { } } return [DevtoolsCore, NoOpDevtoolsCore] as const } From b757d96325b703f33cc84ebfa2250f3137d01df1 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:14:05 +0000 Subject: [PATCH 15/21] ci: apply automated fixes --- .../devtools-utils/src/solid/class.test.tsx | 46 +++++++++++-------- packages/devtools-utils/src/solid/class.tsx | 8 ++-- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/packages/devtools-utils/src/solid/class.test.tsx b/packages/devtools-utils/src/solid/class.test.tsx index 5d862509..80ce341a 100644 --- a/packages/devtools-utils/src/solid/class.test.tsx +++ b/packages/devtools-utils/src/solid/class.test.tsx @@ -28,23 +28,27 @@ describe('constructCoreClass', () => { vi.clearAllMocks() }) it('should export DevtoolsCore and NoOpDevtoolsCore classes and make no calls to Solid.js primitives', () => { - const [DevtoolsCore, NoOpDevtoolsCore] = constructCoreClass( - () =>
Test Component
, - ) + const [DevtoolsCore, NoOpDevtoolsCore] = constructCoreClass(() => ( +
Test Component
+ )) expect(DevtoolsCore).toBeDefined() expect(NoOpDevtoolsCore).toBeDefined() expect(lazyImportMock).not.toHaveBeenCalled() }) it('DevtoolsCore should call solid primitives when mount is called', async () => { - const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) + const [DevtoolsCore, _] = constructCoreClass(() => ( +
Test Component
+ )) const instance = new DevtoolsCore() await instance.mount(document.createElement('div'), 'dark') expect(renderMock).toHaveBeenCalled() }) it('DevtoolsCore should throw if mount is called twice without unmounting', async () => { - const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) + const [DevtoolsCore, _] = constructCoreClass(() => ( +
Test Component
+ )) const instance = new DevtoolsCore() await instance.mount(document.createElement('div'), 'dark') await expect( @@ -53,13 +57,17 @@ describe('constructCoreClass', () => { }) it('DevtoolsCore should throw if unmount is called before mount', () => { - const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) + const [DevtoolsCore, _] = constructCoreClass(() => ( +
Test Component
+ )) const instance = new DevtoolsCore() expect(() => instance.unmount()).toThrow('Devtools is not mounted') }) it('DevtoolsCore should allow mount after unmount', async () => { - const [DevtoolsCore, _] = constructCoreClass(() =>
Test Component
) + const [DevtoolsCore, _] = constructCoreClass(() => ( +
Test Component
+ )) const instance = new DevtoolsCore() await instance.mount(document.createElement('div'), 'dark') instance.unmount() @@ -69,9 +77,9 @@ describe('constructCoreClass', () => { }) it('NoOpDevtoolsCore should not call any solid primitives when mount is called', async () => { - const [_, NoOpDevtoolsCore] = constructCoreClass( - () =>
Test Component
, - ) + const [_, NoOpDevtoolsCore] = constructCoreClass(() => ( +
Test Component
+ )) const noOpInstance = new NoOpDevtoolsCore() await noOpInstance.mount(document.createElement('div'), 'dark') @@ -81,9 +89,9 @@ describe('constructCoreClass', () => { }) it('NoOpDevtoolsCore should not throw if mount is called multiple times', async () => { - const [_, NoOpDevtoolsCore] = constructCoreClass( - () =>
Test Component
, - ) + const [_, NoOpDevtoolsCore] = constructCoreClass(() => ( +
Test Component
+ )) const noOpInstance = new NoOpDevtoolsCore() await noOpInstance.mount(document.createElement('div'), 'dark') await expect( @@ -92,17 +100,17 @@ describe('constructCoreClass', () => { }) it('NoOpDevtoolsCore should not throw if unmount is called before mount', () => { - const [_, NoOpDevtoolsCore] = constructCoreClass( - () =>
Test Component
, - ) + const [_, NoOpDevtoolsCore] = constructCoreClass(() => ( +
Test Component
+ )) const noOpInstance = new NoOpDevtoolsCore() expect(() => noOpInstance.unmount()).not.toThrow() }) it('NoOpDevtoolsCore should not throw if unmount is called after mount', async () => { - const [_, NoOpDevtoolsCore] = constructCoreClass( - () =>
Test Component
, - ) + const [_, NoOpDevtoolsCore] = constructCoreClass(() => ( +
Test Component
+ )) const noOpInstance = new NoOpDevtoolsCore() await noOpInstance.mount(document.createElement('div'), 'dark') expect(() => noOpInstance.unmount()).not.toThrow() diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx index 174c2f88..aba16de9 100644 --- a/packages/devtools-utils/src/solid/class.tsx +++ b/packages/devtools-utils/src/solid/class.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource solid-js - we use Solid.js as JSX here */ -import type { JSX, } from 'solid-js' +import type { JSX } from 'solid-js' /** * Constructs the core class for the Devtools. @@ -18,7 +18,7 @@ export function constructCoreClass(Component: () => JSX.Element) { #Component: any #ThemeProvider: any - constructor() { } + constructor() {} async mount(el: T, theme: 'light' | 'dark') { const { lazy } = await import('solid-js') @@ -64,8 +64,8 @@ export function constructCoreClass(Component: () => JSX.Element) { constructor() { super() } - async mount(_el: T, _theme: 'light' | 'dark') { } - unmount() { } + async mount(_el: T, _theme: 'light' | 'dark') {} + unmount() {} } return [DevtoolsCore, NoOpDevtoolsCore] as const } From 16c273bbdb3f470a06fcb6ecc27a2cb3e6fe88a2 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 14:27:30 +0200 Subject: [PATCH 16/21] test --- packages/devtools-utils/package.json | 11 ++++++----- packages/devtools-utils/src/solid/class.tsx | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index 518f3e29..6183a2f2 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -37,15 +37,16 @@ "engines": { "node": ">=18" }, - "dependencies": { - "@tanstack/devtools-ui": "workspace:^" - }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", - "solid-js": ">=1.9.7" + "solid-js": ">=1.9.7", + "@tanstack/devtools-ui": "workspace:^" }, "peerDependenciesMeta": { + "@tanstack/devtools-ui": { + "optional": true + }, "react": { "optional": true }, @@ -73,4 +74,4 @@ "devDependencies": { "vite-plugin-solid": "^2.11.8" } -} +} \ No newline at end of file diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx index aba16de9..5a860db0 100644 --- a/packages/devtools-utils/src/solid/class.tsx +++ b/packages/devtools-utils/src/solid/class.tsx @@ -18,7 +18,7 @@ export function constructCoreClass(Component: () => JSX.Element) { #Component: any #ThemeProvider: any - constructor() {} + constructor() { } async mount(el: T, theme: 'light' | 'dark') { const { lazy } = await import('solid-js') @@ -30,12 +30,13 @@ export function constructCoreClass(Component: () => JSX.Element) { const dispose = render(() => { // eslint-disable-next-line @typescript-eslint/require-await this.#Component = lazy(async () => ({ default: Component })) - const Devtools = this.#Component + this.#ThemeProvider = lazy(() => import('@tanstack/devtools-ui').then((mod) => ({ default: mod.ThemeContextProvider, })), ) + const Devtools = this.#Component const ThemeProvider = this.#ThemeProvider return ( @@ -64,8 +65,8 @@ export function constructCoreClass(Component: () => JSX.Element) { constructor() { super() } - async mount(_el: T, _theme: 'light' | 'dark') {} - unmount() {} + async mount(_el: T, _theme: 'light' | 'dark') { } + unmount() { } } return [DevtoolsCore, NoOpDevtoolsCore] as const } From 51054e4cf9db61b148b10a84edc9add33336105e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:28:28 +0000 Subject: [PATCH 17/21] ci: apply automated fixes --- packages/devtools-utils/package.json | 2 +- packages/devtools-utils/src/solid/class.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index 6183a2f2..1590c06a 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -74,4 +74,4 @@ "devDependencies": { "vite-plugin-solid": "^2.11.8" } -} \ No newline at end of file +} diff --git a/packages/devtools-utils/src/solid/class.tsx b/packages/devtools-utils/src/solid/class.tsx index 5a860db0..bda0ac56 100644 --- a/packages/devtools-utils/src/solid/class.tsx +++ b/packages/devtools-utils/src/solid/class.tsx @@ -18,7 +18,7 @@ export function constructCoreClass(Component: () => JSX.Element) { #Component: any #ThemeProvider: any - constructor() { } + constructor() {} async mount(el: T, theme: 'light' | 'dark') { const { lazy } = await import('solid-js') @@ -65,8 +65,8 @@ export function constructCoreClass(Component: () => JSX.Element) { constructor() { super() } - async mount(_el: T, _theme: 'light' | 'dark') { } - unmount() { } + async mount(_el: T, _theme: 'light' | 'dark') {} + unmount() {} } return [DevtoolsCore, NoOpDevtoolsCore] as const } From eecf87d17635a95402759f9c2d5560b3299bd7f5 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 14:34:52 +0200 Subject: [PATCH 18/21] revert --- packages/devtools-utils/package.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index 1590c06a..ad322060 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -37,16 +37,15 @@ "engines": { "node": ">=18" }, + "dependencies": { + "@tanstack/devtools-ui": "workspace:^" + }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", - "solid-js": ">=1.9.7", - "@tanstack/devtools-ui": "workspace:^" + "solid-js": ">=1.9.7" }, "peerDependenciesMeta": { - "@tanstack/devtools-ui": { - "optional": true - }, "react": { "optional": true }, @@ -74,4 +73,4 @@ "devDependencies": { "vite-plugin-solid": "^2.11.8" } -} +} \ No newline at end of file From b0417ca9784477c8b38761a0dc5dd5ed5fa89fc4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:35:47 +0000 Subject: [PATCH 19/21] ci: apply automated fixes --- packages/devtools-utils/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devtools-utils/package.json b/packages/devtools-utils/package.json index ad322060..518f3e29 100644 --- a/packages/devtools-utils/package.json +++ b/packages/devtools-utils/package.json @@ -73,4 +73,4 @@ "devDependencies": { "vite-plugin-solid": "^2.11.8" } -} \ No newline at end of file +} From 9c63c9d6d222c569ea99ef77d710163d08bea5a1 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 23 Sep 2025 14:47:26 +0200 Subject: [PATCH 20/21] fix tests --- packages/devtools-utils/src/index.ts | 0 packages/devtools-utils/tsconfig.json | 3 +-- packages/event-bus-client/src/plugin.ts | 2 +- packages/event-bus/src/server/server.ts | 6 +++--- 4 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 packages/devtools-utils/src/index.ts diff --git a/packages/devtools-utils/src/index.ts b/packages/devtools-utils/src/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/devtools-utils/tsconfig.json b/packages/devtools-utils/tsconfig.json index ebf55666..179e0a51 100644 --- a/packages/devtools-utils/tsconfig.json +++ b/packages/devtools-utils/tsconfig.json @@ -4,9 +4,8 @@ "jsxImportSource": "react" }, "extends": "../../tsconfig.json", - "exclude": ["src/solid/**"], "include": [ - "src/react", + "src", "eslint.config.js", "vite.config.ts", "tests", diff --git a/packages/event-bus-client/src/plugin.ts b/packages/event-bus-client/src/plugin.ts index ff155900..9c0366ce 100644 --- a/packages/event-bus-client/src/plugin.ts +++ b/packages/event-bus-client/src/plugin.ts @@ -4,7 +4,7 @@ interface TanStackDevtoolsEvent { pluginId?: string // Optional pluginId to filter events by plugin } declare global { - // eslint-disable-next-line no-var + var __TANSTACK_EVENT_TARGET__: EventTarget | null } diff --git a/packages/event-bus/src/server/server.ts b/packages/event-bus/src/server/server.ts index 1cd35878..25ed77fd 100644 --- a/packages/event-bus/src/server/server.ts +++ b/packages/event-bus/src/server/server.ts @@ -12,11 +12,11 @@ export interface TanStackDevtoolsEvent< } // Used so no new server starts up when HMR happens declare global { - // eslint-disable-next-line no-var + var __TANSTACK_DEVTOOLS_SERVER__: http.Server | null - // eslint-disable-next-line no-var + var __TANSTACK_DEVTOOLS_WSS_SERVER__: WebSocketServer | null - // eslint-disable-next-line no-var + var __TANSTACK_EVENT_TARGET__: EventTarget | null } From b766ae735b2a876e7c452a59abb35a8ed210c8cd Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:48:34 +0000 Subject: [PATCH 21/21] ci: apply automated fixes --- packages/event-bus-client/src/plugin.ts | 1 - packages/event-bus/src/server/server.ts | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/event-bus-client/src/plugin.ts b/packages/event-bus-client/src/plugin.ts index 9c0366ce..22e59fd3 100644 --- a/packages/event-bus-client/src/plugin.ts +++ b/packages/event-bus-client/src/plugin.ts @@ -4,7 +4,6 @@ interface TanStackDevtoolsEvent { pluginId?: string // Optional pluginId to filter events by plugin } declare global { - var __TANSTACK_EVENT_TARGET__: EventTarget | null } diff --git a/packages/event-bus/src/server/server.ts b/packages/event-bus/src/server/server.ts index 25ed77fd..21afa8d3 100644 --- a/packages/event-bus/src/server/server.ts +++ b/packages/event-bus/src/server/server.ts @@ -12,11 +12,10 @@ export interface TanStackDevtoolsEvent< } // Used so no new server starts up when HMR happens declare global { - var __TANSTACK_DEVTOOLS_SERVER__: http.Server | null - + var __TANSTACK_DEVTOOLS_WSS_SERVER__: WebSocketServer | null - + var __TANSTACK_EVENT_TARGET__: EventTarget | null }