From fe5be7bdeae75f3546aa22b5f346022335f4ae9e Mon Sep 17 00:00:00 2001 From: "Alexis H. Munsayac" Date: Mon, 19 Oct 2026 11:20:35 +0800 Subject: [PATCH] feat(start): add start.clientSetup to run client code before hydrate - The generated client entry imports the configured module ahead of the app, so it runs before hydrate() in SSR mode and before render() in client mode. - A top-level await in the module delays hydrate()/render() until it settles. - It is a static import, so the app stays in the entry chunk and the server still preloads its chunks and links its CSS. - The module joins the dep-scan entries. - An authored entry-client together with the option is a config error. - The README start options list now also names `instrument`. Closes #419. Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/start-client-setup.md | 7 + README.md | 36 ++- examples/start-ssr/src/ClientSetupApp.tsx | 16 ++ examples/start-ssr/src/client-setup.ts | 32 +++ examples/start-ssr/test/run.mjs | 277 +++++++++++++++++++++- examples/start-ssr/vite.config.ts | 9 + src/ssr/index.ts | 59 ++++- 7 files changed, 433 insertions(+), 3 deletions(-) create mode 100644 .changeset/start-client-setup.md create mode 100644 examples/start-ssr/src/ClientSetupApp.tsx create mode 100644 examples/start-ssr/src/client-setup.ts diff --git a/.changeset/start-client-setup.md b/.changeset/start-client-setup.md new file mode 100644 index 0000000..3e203da --- /dev/null +++ b/.changeset/start-client-setup.md @@ -0,0 +1,7 @@ +--- +'@solidjs/vite-plugin': patch +--- + +New `start.clientSetup` option: a client module the generated client entry runs before it hydrates or mounts the app. Use it to install a server-function transport wrapper, a `vite:preloadError` listener, or an error reporter before the first render, without writing your own `entry-client` / `entry-server` pair. The module may use top-level `await`, and `hydrate()` / `render()` waits for it. Combining it with an authored `entry-client` is a config error. + +The start options list in the README now also names `instrument`. diff --git a/README.md b/README.md index 74508fc..c3fdfea 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,8 @@ same server functions. The object form carries the options (`start: true` is pure sugar for `start: {}` — both mean the identical start mode with defaults, and `false`/absent means off): `app`, `document`, `entryServer`, `entryClient`, -`middleware`, `setup`, `renderMode`, `env`, `devtools`, `errorBoundary`, +`middleware`, `setup`, `instrument`, `clientSetup`, `renderMode`, `env`, +`devtools`, `errorBoundary`, `css`, `external`, `node`, all documented below. Install `@solidjs/start-devtools` as a development dependency to add the @@ -556,6 +557,39 @@ instrument.mjs` dance. The module may be async and needs no exports; the server build must keep code splitting on (the default), since inlining dynamic imports would hoist the handler graph back above the instrument. +**`clientSetup`** points at a client module that the generated client entry +runs before it hydrates (SSR mode) or mounts (client mode) the app. Use it +for browser setup that must be in place before the first render: + +- a server-function transport wrapper or request-header hook, +- a `vite:preloadError` listener, +- an error reporter's `init()`. + +```ts +// vite.config.ts +solid({ start: { clientSetup: './src/client-setup.ts' }, ssr: true }); + +// src/client-setup.ts +window.addEventListener('vite:preloadError', () => window.location.reload()); +``` + +How it runs: + +- The generated entry imports the module for its side effects, ahead of the + app. A synchronous module finishes before the app's modules evaluate. +- The module may use top-level `await`. `hydrate()` / `render()` waits for + it to settle. The app's modules may evaluate while it waits, but nothing + renders. +- Because it can wait, it can read a global that a classic `