@solidjs/vite-plugin 3.0.0-next.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,711 @@
1
+ <p>
2
+ <img width="100%" src="https://raw.githubusercontent.com/solidjs/solid-vite-plugin/master/banner.png" alt="Solid Vite Plugin">
3
+ </p>
4
+
5
+ # ⚡ @solidjs/vite-plugin
6
+
7
+ > **Renamed from `vite-plugin-solid`.** This package was previously published as
8
+ > [`vite-plugin-solid`](https://www.npmjs.com/package/vite-plugin-solid). To migrate,
9
+ > swap the dependency and the import — `npm install -D @solidjs/vite-plugin` and
10
+ > `import solid from '@solidjs/vite-plugin'` — nothing else changes.
11
+
12
+ A simple integration to run [solid-js](https://github.com/solidjs/solid) with [vite](https://github.com/vitejs/vite)
13
+
14
+ <img alt="HMR gif demonstrationdemodemodemo" src=".github/hmr.gif">
15
+
16
+ # Got a question? / Need help?
17
+
18
+ Join [solid discord](https://discord.com/invite/solidjs) and check the [troubleshooting section](#troubleshooting) to see if your question hasn't been already answered.
19
+
20
+ ## Features
21
+
22
+ - HMR with no configuration needed
23
+ - Drop-in installation as a vite plugin
24
+ - Minimal bundle size
25
+ - Support typescript (`.tsx`) out of the box
26
+ - Support code splitting out of the box
27
+
28
+ ## Requirements
29
+
30
+ This module is 100% ESM compatible and requires NodeJS `14.18.0` or later.
31
+
32
+ You can check your current version of NodeJS by typing `node -v` in your terminal. If your version is below that one version I'd encourage you to either do an update globally or use a NodeJS version management tool such as [Volta](https://volta.sh/) or [nvm](https://github.com/nvm-sh/nvm).
33
+
34
+ Supported Vite versions: **Vite 6, 7 and 8**. Support for Vite 3–5 was
35
+ dropped; if you are on an older Vite, stay on an earlier release of this
36
+ plugin (2.x) or upgrade Vite.
37
+
38
+ ## Quickstart
39
+
40
+ You can use the [vite-template-solid](https://github.com/solidjs/templates) starter templates similar to CRA:
41
+
42
+ ```bash
43
+ $ npx degit solidjs/templates/js my-solid-project
44
+ $ cd my-solid-project
45
+ $ npm install # or pnpm install or yarn install
46
+ $ npm run start # starts dev-server with hot-module-reloading
47
+ $ npm run build # builds to /dist
48
+ ```
49
+
50
+ ## Installation
51
+
52
+ Install `vite`, `@solidjs/vite-plugin` as dev dependencies.
53
+
54
+ Install `solid-js` as dependency.
55
+
56
+ You have to install those so that you are in control to which solid version is used to compile your code.
57
+
58
+ ```bash
59
+ # with npm
60
+ $ npm install -D vite @solidjs/vite-plugin
61
+ $ npm install solid-js
62
+
63
+ # with pnpm
64
+ $ pnpm add -D vite @solidjs/vite-plugin
65
+ $ pnpm add solid-js
66
+
67
+ # with yarn
68
+ $ yarn add -D vite @solidjs/vite-plugin
69
+ $ yarn add solid-js
70
+ ```
71
+
72
+ Add it as plugin to `vite.config.js`
73
+
74
+ ```js
75
+ // vite.config.ts
76
+ import { defineConfig } from 'vite';
77
+ import solidPlugin from '@solidjs/vite-plugin';
78
+
79
+ export default defineConfig({
80
+ plugins: [solidPlugin()],
81
+ });
82
+ ```
83
+
84
+ ## Run
85
+
86
+ Just use regular `vite` or `vite build` commands
87
+
88
+ ```json
89
+ {
90
+ "scripts": {
91
+ "dev": "vite",
92
+ "build": "vite build"
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## API
98
+
99
+ ### options
100
+
101
+ - Type: Object
102
+ - Default: {}
103
+
104
+ #### options.include
105
+
106
+ - Type: (string | RegExp)[] | string | RegExp | null
107
+ - Default: undefined
108
+
109
+ A [picomatch](https://github.com/micromatch/picomatch) pattern, or array of patterns, which specifies the files the plugin should operate on.
110
+
111
+ #### options.exclude
112
+
113
+ - Type: (string | RegExp)[] | string | RegExp | null
114
+ - Default: undefined
115
+
116
+ A [picomatch](https://github.com/micromatch/picomatch) pattern, or array of patterns, which specifies the files to be ignored by the plugin.
117
+
118
+ #### options.dev
119
+
120
+ - Type: Boolean
121
+ - Default: true
122
+
123
+ This will inject `solid-js/dev` in place of `solid-js` in dev mode. Has no effect in prod.
124
+ If set to false, it won't inject it in dev.
125
+ This is useful for extra logs and debug.
126
+
127
+ #### options.hot
128
+
129
+ - Type: Boolean
130
+ - Default: true
131
+
132
+ This will inject HMR runtime in dev mode. Has no effect in prod.
133
+ If set to false, it won't inject the runtime in dev.
134
+
135
+ #### options.ssr
136
+
137
+ - Type: Boolean
138
+ - Default: false
139
+
140
+ Whether the app is server-rendered — one meaning everywhere.
141
+
142
+ Without [`start`](#optionsstart), `ssr: true` enables the SSR transforms
143
+ (hydratable client code, SSR server code); you provide the entries and the
144
+ server yourself, as before. With `start`, the boolean selects the start
145
+ mode: `ssr: true` is SSR start mode, `ssr: false`/omitted is client start
146
+ mode — see below.
147
+
148
+ Objects are no longer accepted (config-time error): the start-mode options
149
+ that used to live on `ssr: { ... }` moved to `start: { ... }`, with
150
+ `ssr: true` set alongside.
151
+
152
+ #### options.start
153
+
154
+ - Type: Boolean | Object
155
+ - Default: undefined
156
+
157
+ **Start is now a mode of the plugin**: the serving layer that
158
+ replaces SolidStart. The plugin owns entries, dev serving, and the build —
159
+ no entry files, no `index.html`, no dev server script. `start: true` is the
160
+ zero-config spelling; add `ssr: true` for streaming SSR:
161
+
162
+ ```ts
163
+ // vite.config.ts
164
+ import { defineConfig } from 'vite';
165
+ import solidPlugin from '@solidjs/vite-plugin';
166
+
167
+ export default defineConfig({
168
+ plugins: [solidPlugin({ start: true, ssr: true })],
169
+ });
170
+ ```
171
+
172
+ One set of conventions serves both rendering modes, and the
173
+ [`ssr`](#optionsssr) boolean picks between them: `ssr: true` streams
174
+ server-side rendering with zero wiring; without it the same app is
175
+ client-rendered onto a prerendered static shell. Flipping a project between
176
+ SPA and SSR is toggling that one boolean — same `App`, same `Document`,
177
+ same server functions.
178
+
179
+ The object form carries the options (`start: true` is pure sugar for
180
+ `start: {}` — both mean the identical start mode with defaults, and
181
+ `false`/absent means off): `app`, `document`, `entryServer`, `entryClient`,
182
+ `middleware`, `env`, `external`, all documented below.
183
+
184
+ ```tsx
185
+ // src/App.tsx — the entire app: a plain content component
186
+ export default function App() {
187
+ return <h1>Hello SSR</h1>;
188
+ }
189
+ ```
190
+
191
+ With `ssr: true` — **SSR start mode**:
192
+
193
+ - **Dev**: `vite` just works — a middleware on the dev server streams the
194
+ rendered app for HTML-accepting GET requests through the SSR environment,
195
+ injecting the Vite client (HMR, error overlay) and the dev style patch
196
+ into `<head>`. SSR errors render Vite's error page with the overlay.
197
+ - **Build**: a plain `vite build` produces both bundles via the
198
+ environments/builder API — client assets (+ manifest) to `dist/client` and
199
+ the server bundle to `dist/server/server.js`. (`vite build --app`, or the
200
+ classic `vite build` + `vite build --ssr` two-step, work too.)
201
+ - **Build ordering**: server builds read the client manifest, so with `ssr`
202
+ enabled the plugin also orders builder-mode (environments API) app builds
203
+ client-first via a `buildApp` hook (Vite 7.1+). That covers composed
204
+ setups whose own orchestrator builds server environments before the
205
+ client — e.g. @cloudflare/vite-plugin — with no hand-written ordering
206
+ plugin; setups without another orchestrator keep Vite's stock
207
+ build-everything behavior, just client-first.
208
+ - **Prod**: the server bundle's entry is `virtual:solid-ssr-handler`.
209
+ Its named `handleRequest(request)` export maps a web-standard `Request`
210
+ to a streamed `Response`; its default `{ fetch(request) }` export provides
211
+ the same handler in the Fetchable shape used by Workers, Nitro, Netlify
212
+ Functions, Bun, and `deno serve`:
213
+
214
+ ```js
215
+ import app, { handleRequest } from './dist/server/server.js';
216
+ // serve dist/client statically, everything else:
217
+ const response = await handleRequest(request);
218
+ const sameResponse = await app.fetch(request);
219
+ ```
220
+
221
+ The Fetchable wrapper deliberately accepts only the request. Hosts may pass
222
+ environment or execution-context arguments after it; those are not the
223
+ Solid options accepted by `handleRequest`'s second parameter.
224
+
225
+ - **Preview**: `vite build && vite preview` runs the production artifact
226
+ with no server file — Vite's preview statics serve `dist/client`, and
227
+ everything else (pages, the server-function endpoint, middleware)
228
+ dispatches through the built handler.
229
+
230
+ Each request is scoped with `provideRequestEvent`, so `getRequestEvent()`
231
+ works during the render; hashed client assets (entry script, CSS) are
232
+ resolved through the build manifest and injected into `<head>`.
233
+
234
+ Every dispatch runs under a stub-backed request event
235
+ (`createRequestEvent` from `@solidjs/web`), and page responses go through
236
+ the runtime's response-head lifecycle (`createSSRResponse`):
237
+ `httpStatus()` / `httpHeader()` writes made during the render land on the
238
+ wire at shell flush, a `Location` header set before the flush becomes a
239
+ real 3xx redirect, and one set after it (streamed responses) falls back to
240
+ a `<script>window.location=...</script>` tail.
241
+
242
+ **`middleware`** points at a server-only module default-exporting one
243
+ fetch-style middleware — `(request, next) => Response | Promise<Response>`
244
+ — or an array of them, composed in order:
245
+
246
+ ```ts
247
+ // vite.config.ts
248
+ solid({ start: { middleware: './src/middleware.ts' }, ssr: true });
249
+
250
+ // src/middleware.ts
251
+ import { getRequestEvent } from '@solidjs/web';
252
+
253
+ export default async function auth(request: Request, next) {
254
+ getRequestEvent().locals.user = await userFromCookie(request);
255
+ try {
256
+ const response = await next();
257
+ response.headers.set('server-timing', 'app'); // pre-wire window
258
+ return response;
259
+ } catch (error) {
260
+ return new Response('oops', { status: 500 });
261
+ }
262
+ }
263
+ ```
264
+
265
+ The chain fronts every request the plugin dispatches — page SSR and the
266
+ server-function endpoint, dev, production, and preview alike — and runs
267
+ inside the request-event scope, so `getRequestEvent()` works exactly as in
268
+ application code (the endpoint shares the chain's event, so `locals`
269
+ decoration is visible to server functions too). Nothing reaches the wire
270
+ until the outermost middleware returns: headers stay mutable after
271
+ `next()` even for streamed responses.
272
+
273
+ **`setup`** points at a server-only module default-exporting a per-request
274
+ app-setup hook: `(event, App) => Component | void | Promise<Component |
275
+ void>`. The generated server entry awaits it after the middleware chain has
276
+ dispatched to the page render and immediately before `renderToStream` — the
277
+ seam for routers that must prepare an app instance per request before SSR
278
+ can begin (create a router bound to the request, `await router.load()`,
279
+ then render):
280
+
281
+ ```ts
282
+ // vite.config.ts
283
+ solid({ start: { setup: './src/setup.tsx' }, ssr: true });
284
+
285
+ // src/setup.tsx
286
+ import type { Component } from 'solid-js';
287
+ import type { RequestEvent } from '@solidjs/web';
288
+
289
+ export default async function setup(event: RequestEvent, App: Component) {
290
+ const router = createRouter({ url: event.request.url });
291
+ await router.load(); // async work completes before the shell streams
292
+ return () => <App router={router} />; // rendered in the app's place
293
+ }
294
+ ```
295
+
296
+ `event` is the shared request event — the same one the middleware chain
297
+ decorated, so `locals` are visible — and the hook runs inside the request
298
+ scope (`getRequestEvent()` answers in anything it calls). Return a
299
+ component and the generated entry renders it inside the Document where
300
+ `<App />` would have been; return nothing and `<App />` renders unchanged,
301
+ so a pure side-effect setup (seeding a per-request cache) needs no return.
302
+ Zero-config apps are untouched: without the option the generated entry is
303
+ byte-identical to before.
304
+
305
+ Two boundaries to know: the hook is a page-render seam — the middleware
306
+ chain and the server-function endpoint run without it — and it only exists
307
+ in generated entries (an authored `entry-server` owns `render()` already;
308
+ configuring both is an error). And as with any server-side tree shaping,
309
+ whatever the hook renders must be matched client-side for hydration —
310
+ routers that own both sides (their client entry re-creates the router and
311
+ hydrates the same tree) fit naturally.
312
+
313
+ **`env`** — first-party typed environment variables. A schema file at the
314
+ project root — `env.ts` (or `env.js`), probed automatically; point
315
+ elsewhere with `start: { env: './path' }`, disable with `env: false` —
316
+ default-exports `server` and `client` maps of
317
+ [Standard Schema](https://standardschema.dev) validators (zod, valibot,
318
+ arktype — even mixed per key; nothing is imported from the plugin):
319
+
320
+ ```ts
321
+ // env.ts
322
+ import { z } from 'zod';
323
+
324
+ export default {
325
+ server: {
326
+ DATABASE_URL: z.url(),
327
+ SESSION_SECRET: z.string().min(32),
328
+ },
329
+ client: {
330
+ VITE_APP_NAME: z.string().min(1),
331
+ },
332
+ };
333
+ ```
334
+
335
+ The validated values come back through two fully typed virtual modules:
336
+
337
+ ```ts
338
+ // server-only modules (middleware, "use server" modules, the server entry)
339
+ import { env } from 'virtual:env/server'; // every var
340
+
341
+ // anywhere
342
+ import { env } from 'virtual:env/client'; // the VITE_-prefixed client vars
343
+ ```
344
+
345
+ - **Validation is node-only and layered.** The plugin loads the `.env*`
346
+ files through Vite's `loadEnv` (with `process.env` winning, so CI
347
+ secrets take precedence), folds them into `process.env` itself — no
348
+ `loadEnv` one-liner in vite.config, and server code reading
349
+ `process.env` directly sees the file-loaded vars too — and validates
350
+ before anything builds. In dev every failure renders the error overlay
351
+ with the per-key report, and `.env*`/schema edits revalidate live. In a
352
+ build, `client` failures fail the build (those values are baked);
353
+ `server` failures only warn — a build machine may legitimately not have
354
+ the production secrets — and boot validation enforces them.
355
+ - **Client values are baked, server values are runtime.** That's what the
356
+ public `VITE_` prefix means: `virtual:env/client` is the validated
357
+ output serialized as plain JSON (defaults applied, coercions done) with
358
+ zero schema-library bytes. `virtual:env/server` is not baked — it reads
359
+ `process.env` when the server boots and validates through your own
360
+ schema (imported into the server bundle, where shipping the validator
361
+ is fine). Platform-injected vars that don't exist at build time work,
362
+ secrets rotate without a rebuild, and no secret value exists in any
363
+ dist artifact; an invalid server environment fails boot with the same
364
+ per-key report.
365
+ - **Leaks are errors.** Importing `virtual:env/server` from a client
366
+ module graph is a hard error naming the importer (the app root and
367
+ everything it imports hydrate — they are client code; keep server env
368
+ in middleware, `"use server"` modules, or an authored server entry).
369
+ Client keys must carry the public prefix (`VITE_`, or your `envPrefix`)
370
+ — enforced at config time. And a client-build scan fails the build when
371
+ a server var's literal value shows up quoted in a client chunk.
372
+ - **Types are generated by inference.** A `solid-env.d.ts` is written next
373
+ to the schema file (keep both inside your tsconfig `include`): it
374
+ derives each var's type from your own schema through the Standard
375
+ Schema output type, so `env.VITE_APP_NAME` is whatever your validator
376
+ outputs — with any compliant library and no per-library plumbing.
377
+
378
+ Env works identically in both `start` modes (a client-mode static build
379
+ carries only the client vars); it is a start-mode feature, so without `start`
380
+ there is no env layer. See `examples/start-env` for the full story,
381
+ including the failure modes.
382
+
383
+ Design credit: the shape of this feature — the schema-file convention,
384
+ the `virtual:env/*` module names (kept identical on purpose), baked
385
+ client values, the leak scan — follows
386
+ [@vite-env/core](https://github.com/pyyupsk/vite-env) (MIT), the
387
+ design-correct prior art, reimplemented on this plugin's machinery with
388
+ Standard Schema as the only contract (and runtime-read server values).
389
+
390
+ **Entry resolution** (all paths relative to the Vite root):
391
+
392
+ 1. Explicit `start.entryServer` / `start.entryClient` options.
393
+ 2. Conventional files: `src/entry-server.{tsx,jsx,ts,js,mjs}` and
394
+ `src/entry-client.{tsx,jsx,ts,js,mjs}`. Entry files come in pairs —
395
+ providing only one is an error. The server entry must export
396
+ `render(request?, context?)` returning a `renderToStream` result, an HTML
397
+ string, or a `Response`; `context.clientEntry` carries the resolved
398
+ client entry URL, and in production any literal
399
+ `"/src/entry-client.tsx"` reference in the rendered HTML is rewritten to
400
+ the hashed asset (the classic harness convention keeps working).
401
+ 3. Generated entries (the zero-config path): when no entry files exist, both
402
+ are generated from a root component — `start.app`, defaulting to
403
+ `src/App.{tsx,jsx,ts,js}` (or lowercase `src/app.*`) — wrapped in a
404
+ document shell: `start.document`, defaulting to `src/Document.{tsx,jsx}`,
405
+ else a built-in minimal shell. A custom document receives the app as
406
+ `props.children` and must render the full `<html>` document including
407
+ `<HydrationScript />`; the client entry script is injected into `<head>`
408
+ automatically.
409
+
410
+ With [`serverFunctions`](#optionsserverfunctions) also enabled the two
411
+ compose: `handleRequest` serves the endpoint on every surface (in dev the
412
+ server-function middleware pre-loads the referenced module, then dispatches
413
+ through the same handler), so one middleware chain and one request event
414
+ front pages and server functions identically.
415
+
416
+ The normal `ssr` environment exposes the default Fetchable handler as its
417
+ `index` service entry in development and production. Provider Vite plugins
418
+ can adopt that environment directly: they supply its runtime and build
419
+ orchestration while Solid continues to supply the application entry,
420
+ manifest, middleware, and server-function dispatch. When a provider replaces
421
+ the development environment with a non-runnable one, Solid detects that
422
+ ownership and stands its HTTP middlewares down automatically.
423
+
424
+ Two explicit switches remain for custom host setups:
425
+
426
+ 1. **`start.external: true`** — hands the whole server side to a host that
427
+ does not adopt Solid's normal `ssr` environment. Solid skips its
428
+ server-build wiring and stands its development middlewares down, while
429
+ continuing to provide the generated entries, client manifest, and
430
+ `virtual:solid-ssr-handler`. This is mainly for differently named or
431
+ independently configured environments.
432
+ 2. **[`serverFunctions.devMiddleware: false`](#optionsserverfunctions)** —
433
+ the narrow, endpoint-only switch: keeps start mode's server build and SSR
434
+ serving, hands only server-function dispatch in dev to the host. For
435
+ setups without `start`, or when only the endpoint should move.
436
+
437
+ Without `ssr: true` — **client mode** (experimental), the same conventions
438
+ with client-only rendering:
439
+
440
+ ```js
441
+ export default defineConfig({
442
+ plugins: [solidPlugin({ start: true })],
443
+ });
444
+ ```
445
+
446
+ - **Dev**: every HTML-accepting GET streams the rendered document shell —
447
+ without the app, which never renders on the server — with the entry
448
+ graph's CSS inlined; deep links get the same shell (history-fallback
449
+ semantics). The generated client entry `render()`s (not hydrates) the app
450
+ into `document.body`.
451
+ - **Build**: `vite build` emits a purely static `dist/client` — the shell is
452
+ prerendered once through the built handler into `dist/client/index.html`,
453
+ with the hashed entry script and the entry graph's CSS links — deployable
454
+ to any static host. No server bundle remains unless `serverFunctions` is
455
+ enabled, in which case `dist/server` is kept and its `handleRequest`
456
+ serves the endpoint (pages stay static).
457
+ - **Transforms**: client code compiles exactly like a plain SPA today
458
+ (`generate: 'dom'`, non-hydratable); only the document shell goes through
459
+ the SSR transforms.
460
+ - **`vite preview`** serves the static build with history fallback (and
461
+ dispatches the server-function endpoint through the kept handler).
462
+ - Server-only options are inert here rather than errors, so a config
463
+ survives the flip untouched: `start.entryServer` (and conventional
464
+ `src/entry-server.*` files) are ignored — the shell render is always
465
+ generated — and so is `start.external`. An authored `src/entry-client.*`
466
+ stands alone and owns the mount.
467
+
468
+ The point is the migration story: an app born with `start: true` moves to
469
+ server rendering by setting `ssr: true` — same `App`, same `Document`,
470
+ same routes, same server functions; the plugin swaps render for hydrate,
471
+ turns the hydratable transforms on, and ships the server bundle. (A
472
+ `Document` authored for SSR carries `<HydrationScript />`; in client mode
473
+ the plugin strips its script from the served shell — nothing hydrates, so
474
+ a shared `Document` costs nothing — and the built-in shell omits it.)
475
+
476
+ Start-mode serving is opt-in via `start`, so bare `ssr: true` setups keep the
477
+ transform-only behavior. See `examples/turnkey` for a complete SSR app
478
+ (including a one-file production server and server functions),
479
+ `examples/start-client` for client mode (whose test flips the same app
480
+ between the modes), and `examples/ssr` for the manual `ssr: true` wiring.
481
+
482
+ #### options.serverFunctions
483
+
484
+ - Type: Boolean | Object
485
+ - Default: undefined
486
+
487
+ Enables `"use server"` server function compilation (experimental). Pass
488
+ `true` for the defaults (runtime from `@solidjs/web/server-functions`,
489
+ endpoint `/_server`) or an options object (`runtime`, `endpoint`, `filter`,
490
+ `directive`, `manifest`, `devMiddleware`, `configure`) to customize.
491
+
492
+ The setup is zero-config: in dev a middleware on the Vite server handles the
493
+ endpoint end to end — no server-function code needed in your server entry.
494
+ For production SSR builds, either use SSR start mode ([`start`](#optionsstart)
495
+ with `ssr: true`, whose handler serves the endpoint automatically) or
496
+ import `virtual:solid-server-function-handler` in your server entry and
497
+ mount its `handleServerFunctionRequest(request)` export on the endpoint.
498
+
499
+ **`devMiddleware: false`** hands endpoint dispatch in dev to a host instead
500
+ of the plugin's middleware. The middleware executes functions in Vite's
501
+ node-side SSR environment; when another plugin's server environment should
502
+ run them — e.g. @cloudflare/vite-plugin, so functions see workerd bindings
503
+ (`env`/`ctx`) in dev exactly like production — turn it off and let the host
504
+ dispatch: it loads `virtual:solid-server-function-handler` through its own
505
+ environment and calls `handleServerFunctionRequest(request)`, the same
506
+ contract as production. Compilation and the virtual modules keep working;
507
+ endpoint requests simply fall through to the host. Since the middleware's
508
+ on-demand module loading is off too, a host owning dev dispatch should
509
+ side-effect import `virtual:solid-server-function-manifest` in its server
510
+ entry so functions referenced only by client code still register. (When a
511
+ provider owns the `ssr` environment outright — it isn't runnable — the
512
+ middleware already stands down automatically; see the `external` option
513
+ under [`start`](#optionsstart) for the whole-server switch and how the
514
+ three options relate.)
515
+
516
+ **`configure: './src/server-config.ts'`** pins a server-only module (path
517
+ resolved against the Vite root) into the handler graph: the generated
518
+ `virtual:solid-server-function-handler` module side-effect imports it before
519
+ dispatching anything. It's the guaranteed pre-dispatch home for server-side
520
+ runtime registration — e.g. a router's single-flight collector:
521
+
522
+ ```ts
523
+ // src/server-config.ts
524
+ import { configureServerFunctionsServer } from '@solidjs/web/server-functions/server';
525
+ configureServerFunctionsServer({ collectFlightData: createFlightDataCollector(router) });
526
+ ```
527
+
528
+ Registration living in the app graph only loads with the first page render,
529
+ so after a dev-server restart the first mutation can race it; the handler
530
+ graph loads before the first dispatch on every surface (dev middleware and
531
+ production handler alike), and edits to the module hot-invalidate the
532
+ handler in dev. Config calls merge per key, so it composes with the
533
+ plugin's own runtime configuration.
534
+
535
+ Meta-frameworks that need to control plugin ordering and dispatch requests
536
+ through their own server should use the standalone `serverFunctions()`
537
+ export instead, which never installs the dev middleware. See
538
+ `examples/turnkey` for a complete app.
539
+
540
+ **Server components (experimental):** `serverFunctions: { components: true }`
541
+ lets a `"use server"` function return a component. Server components ride
542
+ server functions — same endpoint, same compilation — with zero extra plugin
543
+ config: responses for component-returning functions are served as streamed
544
+ HTML that the client applies in place (client state and DOM identity inside
545
+ survive updates), and the plugin's dev middleware and production handler
546
+ handle that automatically. Combined with SSR start mode
547
+ ([`start`](#optionsstart) with `ssr: true`) and generated entries, the
548
+ document wiring is emitted too: server components render inline in the
549
+ SSR'd document and are adopted
550
+ at boot with zero endpoint requests. With authored entries, the app-side
551
+ pieces (the render plugin, the bootstrap script, and the client's
552
+ `installServerComponents()` call, all from `@solidjs/web/frames`) live in
553
+ your entry files instead. See `examples/turnkey` for a complete page.
554
+
555
+ #### options.compiler
556
+
557
+ - Type: `"babel" | "native"`
558
+ - Default: `"native"`
559
+
560
+ Choose the JSX compiler backend. The default `"native"` compiles JSX through
561
+ the native compiler from `@dom-expressions/compiler`. `"babel"` runs
562
+ `babel-preset-solid` instead and only switches the JSX transform — every
563
+ other pass (the `lazy()` module-URL transform and the solid-refresh HMR
564
+ transform) is native in both modes.
565
+
566
+ `"babel"` is the escape hatch: if the native output ever differs from what
567
+ you expect, set `compiler: 'babel'` and file an issue — the behavioral diff
568
+ between the two modes is the bug report. Platforms without a prebuilt native
569
+ binary (for example StackBlitz WebContainers) automatically fall back to the
570
+ `@dom-expressions/compiler-wasm32-wasi` build, so no configuration is needed
571
+ there.
572
+
573
+ ```ts
574
+ import { defineConfig } from 'vite';
575
+ import solidPlugin from '@solidjs/vite-plugin';
576
+
577
+ export default defineConfig({
578
+ plugins: [solidPlugin({ compiler: 'babel' })],
579
+ });
580
+ ```
581
+
582
+ #### options.babel
583
+
584
+ - Type: Babel.TransformOptions
585
+ - Default: {}
586
+
587
+ Pass any additional [babel transform options](https://babeljs.io/docs/en/options). Those will be merged with the transformations required by Solid.
588
+
589
+ #### options.solid
590
+
591
+ - Type: [@dom-expressions/compiler](https://github.com/ryansolid/dom-expressions/tree/main/packages/compiler#options) / [@dom-expressions/babel-plugin-jsx](https://github.com/ryansolid/dom-expressions/tree/main/packages/babel-plugin-jsx#plugin-options)
592
+ - Default: {}
593
+
594
+ Pass additional DOM Expressions JSX compiler options. They will be merged with
595
+ Solid's defaults (`moduleName: "@solidjs/web"`, Solid built-ins, custom-element
596
+ context, and conditional wrapping) and applied to whichever compiler backend is
597
+ selected.
598
+
599
+ #### options.typescript
600
+
601
+ - Type: [@babel/preset-typescript](https://babeljs.io/docs/en/babel-preset-typescript)
602
+ - Default: {}
603
+
604
+ Pass any additional [@babel/preset-typescript](https://babeljs.io/docs/en/babel-preset-typescript).
605
+
606
+ #### options.extensions
607
+
608
+ - Type: (string, [string, { typescript: boolean }])[]
609
+ - Default: []
610
+
611
+ An array of custom extension that will be passed through the solid compiler.
612
+ By default, the plugin only transform `jsx` and `tsx` files.
613
+ This is useful if you want to transform `mdx` files for example.
614
+
615
+ ## `server-only` and `client-only` boundary markers
616
+
617
+ The plugin always claims the bare specifiers `server-only` and `client-only`
618
+ as marker modules. Import one to pin a module to an environment:
619
+
620
+ ```ts
621
+ import 'server-only'; // this module must never be bundled for the client
622
+
623
+ export const dbClient = createDbClient(process.env.DATABASE_URL);
624
+ ```
625
+
626
+ Importing `server-only` from a module that ends up in a client bundle fails
627
+ the build with an error naming the importer (and vice versa for
628
+ `client-only`); in the allowed environment the marker resolves to an empty
629
+ module. This turns "server code silently shipped to the browser and crashed
630
+ at runtime" into a build-time error at the exact import edge.
631
+
632
+ For TypeScript, the ambient declarations ship with the plugin — add to an
633
+ `env.d.ts`:
634
+
635
+ ```ts
636
+ /// <reference types="@solidjs/vite-plugin/boundary-modules" />
637
+ ```
638
+
639
+ Note: these markers shadow React's `server-only` / `client-only` npm
640
+ packages if they happen to be installed; the semantics are the same, and the
641
+ plugin's errors are prefixed `[@solidjs/vite-plugin]`.
642
+
643
+ ## Note on HMR
644
+
645
+ Starting from version `1.1.0`, this plugin handles automatic HMR. The refresh
646
+ transform is compiled natively by `@dom-expressions/compiler` and drives the
647
+ dev-only `solid-js/refresh` runtime entry that ships with Solid (the
648
+ standalone [solid-refresh](https://github.com/solidjs/solid-refresh) package
649
+ is no longer used).
650
+
651
+ At this stage it's still early work but provide basic HMR. In order to get the best out of it there are couple of things to keep in mind:
652
+
653
+ - When you modify a file every state below this component will be reset to default state (including the current file). The state in parent component is preserved.
654
+
655
+ - The entrypoint can't benefit from HMR yet and will force a hard reload of the entire app. This is still really fast thanks to browser caching.
656
+
657
+ If at least one of this point is blocking to you, you can revert to the old behavior by [opting out the automatic HMR](#options) and placing the following snippet in your entry point:
658
+
659
+ ```jsx
660
+ const dispose = render(() => <App />, document.body);
661
+
662
+ if (import.meta.hot) {
663
+ import.meta.hot.accept();
664
+ import.meta.hot.dispose(dispose);
665
+ }
666
+ ```
667
+
668
+ # Troubleshooting
669
+
670
+ - It appears that Webstorm generate some weird triggers when saving a file. In order to prevent that you can follow [this thread](https://intellij-support.jetbrains.com/hc/en-us/community/posts/360000154544-I-m-having-a-huge-problem-with-Webstorm-and-react-hot-loader-) and disable the **"Safe Write"** option in **"Settings | Appearance & Behavior | System Settings"**.
671
+
672
+ - If one of your dependency spit out React code instead of Solid that means that they don't expose JSX properly. To get around it, you might want to manually exclude it from the [dependencies optimization](https://vitejs.dev/config/dep-optimization-options.html#optimizedeps-exclude)
673
+
674
+ - If you are trying to make [directives](https://www.solidjs.com/docs/latest/api#use%3A___) work, and they somehow don't try setting the `options.typescript.onlyRemoveTypeImports` option to `true`
675
+
676
+ ## Migration from v1
677
+
678
+ The master branch now target vite 2.
679
+
680
+ The main breaking change from previous version is that the package has been renamed from `@amoutonbrady/vite-plugin-solid` to `vite-plugin-solid` (since renamed again to `@solidjs/vite-plugin` — see the note at the top).
681
+
682
+ For other breaking changes, check [the migration guide of vite](https://vitejs.dev/guide/migration.html).
683
+
684
+ # Testing
685
+
686
+ If you are using [vitest](https://vitest.dev/), this plugin already injects the necessary configuration for you. It even automatically detects if you have `@testing-library/jest-dom` installed in your project and automatically adds it to the `setupFiles`. All you need to add (if you want) is `globals`, `coverage`, and other testing configuration of your choice. If you can live without those, enjoy using vitest without the need to configure it yourself.
687
+
688
+ Tests default to the client posture, regardless of the app's `ssr` flag: DOM codegen, non-hydratable (nothing hydrates in a test), browser export conditions, and a `jsdom` default `test.environment`. A server-rendered app needs no `ssr: mode !== 'test'` workaround — DOM component tests just work.
689
+
690
+ Server-runtime unit tests (server functions, sessions, `renderToString` — anything that needs `isServer` to be `true` and the real server build of the framework) opt out per [vitest project](https://vitest.dev/guide/projects) by setting `test.environment: 'node'` (or `'edge-runtime'`) explicitly. That project gets the server posture end to end: server export conditions, ssr codegen, and the framework inlined so the whole graph — request-event storage included — resolves into one server-build instance. No inline/alias configuration needed. Both postures coexist in one workspace:
691
+
692
+ ```ts
693
+ // vite.config.ts
694
+ test: {
695
+ projects: [
696
+ {
697
+ extends: true,
698
+ test: { name: 'client', environment: 'jsdom', include: ['src/**/*.test.tsx'] },
699
+ },
700
+ {
701
+ extends: true,
702
+ test: { name: 'server', environment: 'node', include: ['src/server/**/*.test.ts'] },
703
+ },
704
+ ],
705
+ },
706
+ ```
707
+
708
+ # Credits
709
+
710
+ - [solid-js](https://github.com/solidjs/solid)
711
+ - [vite](https://github.com/vitejs/vite)