Skip to content

Commit b832b06

Browse files
authored
feat(solid-query-devtools): Add Devtools (#6130)
* feat(solid-query-devtools): Add Devtools Adds the devtools for @tanstack/solid-query package
1 parent d97e246 commit b832b06

File tree

11 files changed

+349
-3
lines changed

11 files changed

+349
-3
lines changed

docs/solid/devtools.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
id: devtools
3+
title: Devtools
4+
---
5+
6+
Wave your hands in the air and shout hooray because Solid Query comes with dedicated devtools! 🥳
7+
8+
When you begin your Solid Query journey, you'll want these devtools by your side. They help visualize all of the inner workings of Solid Query and will likely save you hours of debugging if you find yourself in a pinch!
9+
10+
> Also note that you can use these devtools to observe queries, but **not mutations** (yet).
11+
12+
## Install and Import the Devtools
13+
14+
The devtools are a separate package that you need to install:
15+
16+
```bash
17+
$ npm i @tanstack/solid-query-devtools@rc
18+
# or
19+
$ pnpm add @tanstack/solid-query-devtools@rc
20+
# or
21+
$ yarn add @tanstack/solid-query-devtools@rc
22+
```
23+
24+
You can import the devtools like this:
25+
26+
```tsx
27+
import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
28+
```
29+
30+
By default, Solid Query Devtools are only included in bundles when `process.env.NODE_ENV === 'development'`, so you don't need to worry about excluding them during a production build.
31+
32+
## Floating Mode
33+
34+
Floating Mode will mount the devtools as a fixed, floating element in your app and provide a toggle in the corner of the screen to show and hide the devtools. This toggle state will be stored and remembered in localStorage across reloads.
35+
36+
Place the following code as high in your Solid app as you can. The closer it is to the root of the page, the better it will work!
37+
38+
```tsx
39+
import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
40+
41+
function App() {
42+
return (
43+
<QueryClientProvider client={queryClient}>
44+
{/* The rest of your application */}
45+
<SolidQueryDevtools initialIsOpen={false} />
46+
</QueryClientProvider>
47+
)
48+
}
49+
```
50+
51+
### Options
52+
53+
- `initialIsOpen: Boolean`
54+
- Set this `true` if you want the dev tools to default to being open
55+
- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"`
56+
- Defaults to `bottom-left`
57+
- The position of the Solid Query logo to open and close the devtools panel
58+
- `position?: "top" | "bottom" | "left" | "right"`
59+
- Defaults to `bottom`
60+
- The position of the Solid Query devtools panel
61+
- `client?: QueryClient`,
62+
- Use this to use a custom QueryClient. Otherwise, the one from the nearest context will be used.
63+
- `errorTypes?: { name: string; initializer: (query: Query) => TError}`
64+
- Use this to predefine some errors that can be triggered on your queries. Initializer will be called (with the specific query) when that error is toggled on from the UI. It must return an Error.

packages/query-devtools/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
"browser": {},
1818
"exports": {
1919
"solid": {
20-
"development": "./build/dev.jsx",
21-
"import": "./build/index.jsx"
20+
"development": "./build/index.js",
21+
"import": "./build/index.js"
2222
},
2323
"development": {
2424
"import": {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @ts-check
2+
3+
/** @type {import('eslint').Linter.Config} */
4+
const config = {
5+
parserOptions: {
6+
tsconfigRootDir: __dirname,
7+
project: './tsconfig.json',
8+
},
9+
}
10+
11+
module.exports = config
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"name": "@tanstack/solid-query-devtools",
3+
"version": "5.0.0-rc.8",
4+
"description": "Developer tools to interact with and visualize the TanStack/solid-query Query cache",
5+
"author": "tannerlinsley",
6+
"license": "MIT",
7+
"repository": "tanstack/query",
8+
"homepage": "https://tanstack.com/query",
9+
"funding": {
10+
"type": "github",
11+
"url": "https://github.com/sponsors/tannerlinsley"
12+
},
13+
"type": "module",
14+
"main": "./build/index.cjs",
15+
"module": "./build/index.js",
16+
"types": "./build/index.d.ts",
17+
"browser": {},
18+
"exports": {
19+
"solid": {
20+
"development": "./build/dev.jsx",
21+
"import": "./build/index.jsx"
22+
},
23+
"development": {
24+
"import": {
25+
"types": "./build/index.d.ts",
26+
"default": "./build/dev.js"
27+
},
28+
"require": "./build/dev.cjs"
29+
},
30+
"import": {
31+
"types": "./build/index.d.ts",
32+
"default": "./build/index.js"
33+
},
34+
"require": "./build/index.cjs"
35+
},
36+
"scripts": {
37+
"clean": "rimraf ./build && rimraf ./coverage",
38+
"test:eslint": "eslint --ext .ts,.tsx ./src",
39+
"test:types": "tsc",
40+
"test:build": "publint --strict",
41+
"build": "tsup",
42+
"build:dev": "tsup --watch"
43+
},
44+
"files": [
45+
"build",
46+
"src"
47+
],
48+
"dependencies": {
49+
"@tanstack/query-devtools": "workspace:*"
50+
},
51+
"peerDependencies": {
52+
"solid-js": "^1.7.8",
53+
"@tanstack/solid-query": "workspace:^"
54+
},
55+
"devDependencies": {
56+
"solid-js": "^1.7.8",
57+
"@tanstack/solid-query": "workspace:^",
58+
"tsup-preset-solid": "^2.0.1",
59+
"vite-plugin-solid": "^2.7.0"
60+
}
61+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import {
2+
createEffect,
3+
createMemo,
4+
createSignal,
5+
onCleanup,
6+
onMount,
7+
sharedConfig,
8+
splitProps,
9+
untrack,
10+
} from 'solid-js'
11+
import { onlineManager, useQueryClient } from '@tanstack/solid-query'
12+
import { isServer } from 'solid-js/web'
13+
import { TanstackQueryDevtools } from '@tanstack/query-devtools'
14+
import type {
15+
DevToolsErrorType,
16+
DevtoolsButtonPosition,
17+
DevtoolsPosition,
18+
} from '@tanstack/query-devtools'
19+
import type { QueryClient } from '@tanstack/solid-query'
20+
import type { Component, ComponentProps, JSX } from 'solid-js'
21+
22+
export interface DevtoolsOptions {
23+
/**
24+
* Set this true if you want the dev tools to default to being open
25+
*/
26+
initialIsOpen?: boolean
27+
/**
28+
* The position of the React Query logo to open and close the devtools panel.
29+
* 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
30+
* Defaults to 'bottom-left'.
31+
*/
32+
buttonPosition?: DevtoolsButtonPosition
33+
/**
34+
* The position of the React Query devtools panel.
35+
* 'top' | 'bottom' | 'left' | 'right'
36+
* Defaults to 'bottom'.
37+
*/
38+
position?: DevtoolsPosition
39+
/**
40+
* Custom instance of QueryClient
41+
*/
42+
client?: QueryClient
43+
/**
44+
* Use this so you can define custom errors that can be shown in the devtools.
45+
*/
46+
errorTypes?: Array<DevToolsErrorType>
47+
}
48+
49+
export default function SolidQueryDevtools(props: DevtoolsOptions) {
50+
const queryClient = useQueryClient()
51+
const client = createMemo(() => props.client || queryClient)
52+
let ref!: HTMLDivElement
53+
const devtools = new TanstackQueryDevtools({
54+
client: client(),
55+
queryFlavor: 'Solid Query',
56+
version: '5',
57+
onlineManager,
58+
buttonPosition: props.buttonPosition,
59+
position: props.position,
60+
initialIsOpen: props.initialIsOpen,
61+
errorTypes: props.errorTypes,
62+
})
63+
64+
createEffect(() => {
65+
devtools.setClient(client())
66+
})
67+
68+
createEffect(() => {
69+
const buttonPos = props.buttonPosition
70+
if (buttonPos) {
71+
devtools.setButtonPosition(buttonPos)
72+
}
73+
})
74+
75+
createEffect(() => {
76+
const pos = props.position
77+
if (pos) {
78+
devtools.setPosition(pos)
79+
}
80+
})
81+
82+
createEffect(() => {
83+
devtools.setInitialIsOpen(props.initialIsOpen || false)
84+
})
85+
86+
createEffect(() => {
87+
devtools.setErrorTypes(props.errorTypes || [])
88+
})
89+
90+
onMount(() => {
91+
devtools.mount(ref)
92+
onCleanup(() => devtools.unmount())
93+
})
94+
95+
return <div class="tsqd-parent-container" ref={ref}></div>
96+
}
97+
98+
/*
99+
This function has been taken from solid-start's codebase
100+
This allows the devtools to be loaded only on the client and bypasses any server side rendering
101+
https://github.com/solidjs/solid-start/blob/2967fc2db3f0df826f061020231dbdafdfa0746b/packages/start/islands/clientOnly.tsx
102+
*/
103+
export function clientOnly<T extends Component<any>>(
104+
fn: () => Promise<{
105+
default: T
106+
}>,
107+
) {
108+
if (isServer)
109+
return (props: ComponentProps<T> & { fallback?: JSX.Element }) =>
110+
props.fallback
111+
112+
const [comp, setComp] = createSignal<T>()
113+
fn().then((m) => setComp(() => m.default))
114+
return (props: ComponentProps<T>) => {
115+
let Comp: T | undefined
116+
let m: boolean
117+
const [, rest] = splitProps(props, ['fallback'])
118+
if ((Comp = comp()) && !sharedConfig.context) return Comp(rest)
119+
const [mounted, setMounted] = createSignal(!sharedConfig.context)
120+
onMount(() => setMounted(true))
121+
return createMemo(
122+
() => (
123+
(Comp = comp()),
124+
(m = mounted()),
125+
untrack(() => (Comp && m ? Comp(rest) : props.fallback))
126+
),
127+
)
128+
}
129+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { isDev } from 'solid-js/web'
2+
import { clientOnly } from './devtools'
3+
import type SolidQueryDevtoolsComp from './devtools'
4+
5+
export const SolidQueryDevtools: typeof SolidQueryDevtoolsComp = isDev
6+
? clientOnly(() => import('./devtools'))
7+
: function () {
8+
return null
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"jsx": "preserve",
5+
"jsxImportSource": "solid-js",
6+
"types": ["vitest/globals"]
7+
},
8+
"include": ["src/**/*.ts", "src/**/*.tsx", ".eslintrc.cjs", "tsup.config.js"]
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @ts-check
2+
3+
import { defineConfig } from 'tsup'
4+
import { generateTsupOptions, parsePresetOptions } from 'tsup-preset-solid'
5+
6+
const preset_options = {
7+
entries: {
8+
entry: 'src/index.tsx',
9+
dev_entry: true,
10+
},
11+
cjs: true,
12+
drop_console: true,
13+
}
14+
15+
export default defineConfig(() => {
16+
const parsed_data = parsePresetOptions(preset_options)
17+
const tsup_options = generateTsupOptions(parsed_data)
18+
19+
tsup_options.forEach((tsup_option) => {
20+
tsup_option.outDir = 'build'
21+
})
22+
23+
return tsup_options
24+
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import solid from 'vite-plugin-solid'
2+
import { defineConfig } from 'vitest/config'
3+
4+
export default defineConfig({
5+
test: {
6+
name: 'solid-query-devtools',
7+
dir: './src',
8+
watch: false,
9+
setupFiles: [],
10+
environment: 'jsdom',
11+
globals: true,
12+
coverage: { provider: 'istanbul' },
13+
},
14+
plugins: [solid()],
15+
})

pnpm-lock.yaml

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)