Skip to content

Commit 024ea7d

Browse files
fix(devtools): prevent solid-js from leaking into server bundles (#367)
* fix(devtools): prevent solid-js from leaking into server bundles Move all solid-js runtime imports (render, Portal, lazy, ClientEventBus) from core into a new mount-impl.tsx file loaded via dynamic import(). This ensures solid-js code is never in the static import graph and only loads as a separate chunk when mount() is called on the client. * feat(devtools-utils): add Solid.js support and enhance routing configurations * ci: apply automated fixes * fix(devtools-utils): resolve self-referencing import for clean CI builds Add ambient module declaration (barrel-types.d.ts) so TypeScript can resolve @tanstack/devtools-utils/solid on clean checkouts without dist/. * ci: apply automated fixes * fix(devtools-utils): fix lint errors in solid class files * fix(devtools-utils): use variable import path to prevent esbuild SSR pre-bundling esbuild follows dynamic imports with string literals during Vite's dependency optimization, pulling solid-js into the SSR bundle. Using a variable path prevents static analysis from following the import. * fix(devtools-utils): add workerd export condition to ./solid for Cloudflare SSR The ./solid export was missing a workerd condition, causing Vite's SSR dep optimizer (esbuild) to resolve to the client entry instead of server.js when running under Cloudflare workerd. The client entry's compiled Solid JSX imports use/setStyleProperty from solid-js/web which don't exist in the server entry, crashing the dev server. Also reverts the variable import workaround in class.ts back to a literal import, since the workerd condition now properly routes SSR resolution to the server entry. * Refactor code structure for improved readability and maintainability * fix(devtools-utils): use relative dynamic import for class-mount-impl Replace the cross-package bare specifier import('@tanstack/devtools-utils/solid') with a relative import('./class-mount-impl'). Bare specifiers in dynamic imports inside pre-bundled dependencies aren't resolved by bundlers like esbuild/webpack, causing "Failed to resolve module specifier" errors in the browser. The relative import works in all bundlers (Vite, Next.js, etc.) because the class-mount-impl.js file is co-located in the same dist directory, built together via a single vite config with vite-plugin-solid for JSX compilation. * ci: apply automated fixes * chore: fix issues * chore: changeset * fix: lock * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent f6fc5d1 commit 024ea7d

File tree

18 files changed

+282
-190
lines changed

18 files changed

+282
-190
lines changed

.changeset/strong-buttons-wear.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@tanstack/devtools-utils': patch
3+
'@tanstack/devtools': patch
4+
---
5+
6+
Fix issues with bundling solid

examples/react/bundling-repro/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
"@tanstack/react-router-devtools": "^1.132.0",
3131
"@tanstack/react-router-ssr-query": "^1.131.7",
3232
"@tanstack/react-start": "^1.132.0",
33-
"@tanstack/react-store": "^0.8.0",
33+
"@tanstack/react-store": "^0.9.0",
3434
"@tanstack/router-plugin": "^1.132.0",
35-
"@tanstack/store": "^0.8.0",
35+
"@tanstack/store": "^0.9.0",
3636
"class-variance-authority": "^0.7.1",
3737
"clsx": "^2.1.1",
3838
"highlight.js": "^11.11.1",

examples/react/bundling-repro/src/components/demo-AIAssistant.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ function Messages({ messages }: { messages: ChatMessages }) {
7979
}
8080

8181
export default function AIAssistant() {
82-
const isOpen = useStore(showAIAssistant)
82+
const isOpen = useStore(showAIAssistant, (state) => state)
8383
const { messages, sendMessage } = useGuitarRecommendationChat()
8484
const [input, setInput] = useState('')
8585

examples/react/bundling-repro/src/feat/demo-store-devtools.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EventClient } from '@tanstack/devtools-event-client'
22
import { useState, useEffect } from 'react'
33

4-
import { store, fullName } from './demo-store'
4+
import { store } from './demo-store'
55

66
type EventMap = {
77
'store-devtools:state': {
@@ -25,15 +25,15 @@ store.subscribe(() => {
2525
sdec.emit('state', {
2626
firstName: store.state.firstName,
2727
lastName: store.state.lastName,
28-
fullName: fullName.state,
28+
fullName: `${store.state.firstName} ${store.state.lastName}`,
2929
})
3030
})
3131

3232
function DevtoolPanel() {
3333
const [state, setState] = useState<EventMap['store-devtools:state']>(() => ({
3434
firstName: store.state.firstName,
3535
lastName: store.state.lastName,
36-
fullName: fullName.state,
36+
fullName: `${store.state.firstName} ${store.state.lastName}`,
3737
}))
3838

3939
useEffect(() => {
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
import { Derived, Store } from '@tanstack/store'
1+
import { Store } from '@tanstack/store'
22

33
export const store = new Store({
44
firstName: 'Jane',
55
lastName: 'Smith',
66
})
7-
8-
export const fullName = new Derived({
9-
fn: () => `${store.state.firstName} ${store.state.lastName}`,
10-
deps: [store],
11-
})
12-
13-
fullName.mount()

examples/react/bundling-repro/src/routeTree.gen.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export interface FileRoutesByFullPath {
149149
'/demo/guitars/$guitarId': typeof DemoGuitarsGuitarIdRoute
150150
'/demo/start/api-request': typeof DemoStartApiRequestRoute
151151
'/demo/start/server-funcs': typeof DemoStartServerFuncsRoute
152-
'/demo/guitars': typeof DemoGuitarsIndexRoute
152+
'/demo/guitars/': typeof DemoGuitarsIndexRoute
153153
'/demo/api/ai/chat': typeof DemoApiAiChatRoute
154154
'/demo/api/ai/image': typeof DemoApiAiImageRoute
155155
'/demo/api/ai/structured': typeof DemoApiAiStructuredRoute
@@ -158,7 +158,7 @@ export interface FileRoutesByFullPath {
158158
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute
159159
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute
160160
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute
161-
'/demo/start/ssr': typeof DemoStartSsrIndexRoute
161+
'/demo/start/ssr/': typeof DemoStartSsrIndexRoute
162162
}
163163
export interface FileRoutesByTo {
164164
'/': typeof IndexRoute
@@ -221,7 +221,7 @@ export interface FileRouteTypes {
221221
| '/demo/guitars/$guitarId'
222222
| '/demo/start/api-request'
223223
| '/demo/start/server-funcs'
224-
| '/demo/guitars'
224+
| '/demo/guitars/'
225225
| '/demo/api/ai/chat'
226226
| '/demo/api/ai/image'
227227
| '/demo/api/ai/structured'
@@ -230,7 +230,7 @@ export interface FileRouteTypes {
230230
| '/demo/start/ssr/data-only'
231231
| '/demo/start/ssr/full-ssr'
232232
| '/demo/start/ssr/spa-mode'
233-
| '/demo/start/ssr'
233+
| '/demo/start/ssr/'
234234
fileRoutesByTo: FileRoutesByTo
235235
to:
236236
| '/'
@@ -350,7 +350,7 @@ declare module '@tanstack/react-router' {
350350
'/demo/guitars/': {
351351
id: '/demo/guitars/'
352352
path: '/demo/guitars'
353-
fullPath: '/demo/guitars'
353+
fullPath: '/demo/guitars/'
354354
preLoaderRoute: typeof DemoGuitarsIndexRouteImport
355355
parentRoute: typeof rootRouteImport
356356
}
@@ -392,7 +392,7 @@ declare module '@tanstack/react-router' {
392392
'/demo/start/ssr/': {
393393
id: '/demo/start/ssr/'
394394
path: '/demo/start/ssr'
395-
fullPath: '/demo/start/ssr'
395+
fullPath: '/demo/start/ssr/'
396396
preLoaderRoute: typeof DemoStartSsrIndexRouteImport
397397
parentRoute: typeof rootRouteImport
398398
}

examples/react/bundling-repro/src/routes/demo/store.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createFileRoute } from '@tanstack/react-router'
22
import { useStore } from '@tanstack/react-store'
33

4-
import { fullName, store } from '@/feat/demo-store'
4+
import { store } from '@/feat/demo-store'
55

66
export const Route = createFileRoute('/demo/store')({
77
component: DemoStore,
@@ -36,10 +36,11 @@ function LastName() {
3636
}
3737

3838
function FullName() {
39-
const fName = useStore(fullName)
39+
const firstName = useStore(store, (state) => state.firstName)
40+
const lastName = useStore(store, (state) => state.lastName)
4041
return (
4142
<div className="bg-white/10 rounded-lg px-4 py-2 outline-none ">
42-
{fName}
43+
{firstName} {lastName}
4344
</div>
4445
)
4546
}

examples/react/bundling-repro/vite.config.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ const config = defineConfig({
2424
tailwindcss(),
2525
tanstackStart(),
2626
viteReact({
27-
babel: {
28-
plugins: ['babel-plugin-react-compiler'],
29-
},
27+
babel: {},
3028
}),
3129
],
3230
})

examples/react/start/src/routeTree.gen.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export interface FileRoutesByFullPath {
6767
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute
6868
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute
6969
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute
70-
'/demo/start/ssr': typeof DemoStartSsrIndexRoute
70+
'/demo/start/ssr/': typeof DemoStartSsrIndexRoute
7171
}
7272
export interface FileRoutesByTo {
7373
'/': typeof IndexRoute
@@ -100,7 +100,7 @@ export interface FileRouteTypes {
100100
| '/demo/start/ssr/data-only'
101101
| '/demo/start/ssr/full-ssr'
102102
| '/demo/start/ssr/spa-mode'
103-
| '/demo/start/ssr'
103+
| '/demo/start/ssr/'
104104
fileRoutesByTo: FileRoutesByTo
105105
to:
106106
| '/'
@@ -167,7 +167,7 @@ declare module '@tanstack/react-router' {
167167
'/demo/start/ssr/': {
168168
id: '/demo/start/ssr/'
169169
path: '/demo/start/ssr'
170-
fullPath: '/demo/start/ssr'
170+
fullPath: '/demo/start/ssr/'
171171
preLoaderRoute: typeof DemoStartSsrIndexRouteImport
172172
parentRoute: typeof rootRouteImport
173173
}

packages/devtools-utils/package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
}
3333
},
3434
"./solid": {
35+
"workerd": {
36+
"types": "./dist/solid/esm/index.d.ts",
37+
"import": "./dist/solid/esm/server.js"
38+
},
3539
"browser": {
3640
"development": {
3741
"types": "./dist/solid/esm/index.d.ts",
@@ -47,6 +51,12 @@
4751
"types": "./dist/solid/esm/index.d.ts",
4852
"import": "./dist/solid/esm/index.js"
4953
},
54+
"./solid/class": {
55+
"import": {
56+
"types": "./dist/solid-class/esm/class.d.ts",
57+
"default": "./dist/solid-class/esm/class.js"
58+
}
59+
},
5060
"./vue": {
5161
"import": {
5262
"types": "./dist/vue/esm/index.d.ts",
@@ -98,7 +108,7 @@
98108
"test:lib:dev": "pnpm test:lib --watch",
99109
"test:types": "tsc",
100110
"test:build": "publint --strict",
101-
"build": "vite build && vite build --config vite.config.preact.ts && vite build --config vite.config.vue.ts && tsup "
111+
"build": "vite build && vite build --config vite.config.preact.ts && vite build --config vite.config.vue.ts && vite build --config vite.config.solid-class.ts && tsup"
102112
},
103113
"devDependencies": {
104114
"tsup": "^8.5.0",

0 commit comments

Comments
 (0)