diff --git a/.changeset/calm-cats-build.md b/.changeset/calm-cats-build.md
new file mode 100644
index 0000000..0206d37
--- /dev/null
+++ b/.changeset/calm-cats-build.md
@@ -0,0 +1,9 @@
+---
+'@solidjs/vite-plugin': patch
+---
+
+Rename the plugin-managed application option from `start` to `app` and the
+root component field from `start.app` to `app.root`. Explicit entries now use
+`app.entries.client` and `app.entries.server`, and `errorBoundary` is renamed
+to `productionErrorBoundary`. `StartOptions` is now `AppOptions`. The typed
+`env` option is now top-level and can be used without app mode.
diff --git a/README.md b/README.md
index 4108999..fb28509 100644
--- a/README.md
+++ b/README.md
@@ -139,24 +139,29 @@ If set to false, it won't inject the runtime in dev.
Whether the app is server-rendered — one meaning everywhere.
-Without [`start`](#optionsstart), `ssr: true` enables the SSR transforms
+Without [`app`](#optionsapp), `ssr: true` enables the SSR transforms
(hydratable client code, SSR server code); you provide the entries and the
-server yourself, as before. With `start`, the boolean selects the start
-mode: `ssr: true` is SSR start mode, `ssr: false`/omitted is client start
+server yourself, as before. With `app`, the boolean selects the app
+mode: `ssr: true` is SSR app mode, `ssr: false`/omitted is client app
mode — see below.
-Objects are no longer accepted (config-time error): the start-mode options
-that used to live on `ssr: { ... }` moved to `start: { ... }`, with
+Objects are no longer accepted (config-time error): the app-mode options
+that used to live on `ssr: { ... }` moved to `app: { ... }`, with
`ssr: true` set alongside.
-#### options.start
+#### options.app
- Type: Boolean | Object
- Default: undefined
-**Start is now a mode of the plugin**: the serving layer that
-replaces SolidStart. The plugin owns entries, dev serving, and the build —
-no entry files, no `index.html`, no dev server script. `start: true` is the
+This option was previously named `start`. Use `app` instead, and rename the
+old `start.app` root-component option to `app.root`. Explicit entry overrides
+now use `app.entries.client` and `app.entries.server`; the production boundary
+switch is `app.productionErrorBoundary`.
+
+The `app` option enables the plugin-managed application layer. The plugin
+owns entries, dev serving, and the build, so no entry files, `index.html`,
+or dev server script are required. `app: true` is the
zero-config spelling; add `ssr: true` for streaming SSR:
```ts
@@ -165,7 +170,7 @@ import { defineConfig } from 'vite';
import solidPlugin from '@solidjs/vite-plugin';
export default defineConfig({
- plugins: [solidPlugin({ start: true, ssr: true })],
+ plugins: [solidPlugin({ app: true, ssr: true })],
});
```
@@ -176,10 +181,10 @@ client-rendered onto a prerendered static shell. Flipping a project between
SPA and SSR is toggling that one boolean — same `App`, same `Document`,
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`, `env`, `devtools`, `errorBoundary`, `css`, `external`,
+The object form carries the options (`app: true` is pure sugar for
+`app: {}` — both mean the identical app mode with defaults, and
+`false`/absent means off): `root`, `document`, `entries`, `middleware`,
+`setup`, `devtools`, `productionErrorBoundary`, `css`, and `external`,
all documented below.
Install `@solidjs/start-devtools` as a development dependency to add the
@@ -189,8 +194,8 @@ development toolbar with runtime errors and server function calls:
pnpm add -D @solidjs/start-devtools@next
```
-Start mode detects the package automatically. Set `start: { devtools: true }`
-to require it or `start: { devtools: false }` to disable automatic integration.
+App mode detects the package automatically. Set `app: { devtools: true }`
+to require it or `app: { devtools: false }` to disable automatic integration.
The package is an optional peer and the toolbar is not included in production
builds.
@@ -199,7 +204,7 @@ entries, place the development boundary around the app in the shared document
or root:
```tsx
-import { DevToolbar } from "@solidjs/start-devtools";
+import { DevToolbar } from '@solidjs/start-devtools';
@@ -218,7 +223,7 @@ export default function App() {
}
```
-With `ssr: true` — **SSR start mode**:
+With `ssr: true` — **SSR app mode**:
- **Dev**: `vite` just works — a middleware on the dev server streams the
rendered app for HTML-accepting GET requests through the SSR environment,
@@ -310,7 +315,7 @@ fetch-style middleware — `(request, next) => Response | Promise`
```ts
// vite.config.ts
-solid({ start: { middleware: './src/middleware.ts' }, ssr: true });
+solid({ app: { middleware: './src/middleware.ts' }, ssr: true });
// src/middleware.ts
import { getRequestEvent } from '@solidjs/web';
@@ -345,7 +350,7 @@ then render):
```ts
// vite.config.ts
-solid({ start: { setup: './src/setup.tsx' }, ssr: true });
+solid({ app: { setup: './src/setup.tsx' }, ssr: true });
// src/setup.tsx
import type { Component } from 'solid-js';
@@ -375,98 +380,14 @@ whatever the hook renders must be matched client-side for hydration —
routers that own both sides (their client entry re-creates the router and
hydrates the same tree) fit naturally.
-**`env`** — first-party typed environment variables. A schema file at the
-project root — `env.ts` (or `env.js`), probed automatically; point
-elsewhere with `start: { env: './path' }`, disable with `env: false` —
-default-exports `server` and `client` maps of
-[Standard Schema](https://standardschema.dev) validators (zod, valibot,
-arktype — even mixed per key; nothing is imported from the plugin):
-
-```ts
-// env.ts
-import { z } from 'zod';
-
-export default {
- server: {
- DATABASE_URL: z.url(),
- SESSION_SECRET: z.string().min(32),
- },
- client: {
- VITE_APP_NAME: z.string().min(1),
- },
-};
-```
-
-The validated values come back through two fully typed virtual modules:
-
-```ts
-// server-only modules (middleware, "use server" modules, the server entry)
-import { env } from 'virtual:env/server'; // every var
-
-// anywhere
-import { env } from 'virtual:env/client'; // the VITE_-prefixed client vars
-```
-
-- **Validation is node-only and layered.** The plugin loads the `.env*`
- files through Vite's `loadEnv` (with `process.env` winning, so CI
- secrets take precedence), folds them into `process.env` itself — no
- `loadEnv` one-liner in vite.config, and server code reading
- `process.env` directly sees the file-loaded vars too — and validates
- before anything builds. In dev every failure renders the error overlay
- with the per-key report, and `.env*`/schema edits revalidate live. In a
- build, `client` failures fail the build (those values are baked);
- `server` failures only warn — a build machine may legitimately not have
- the production secrets — and boot validation enforces them.
-- **Client values are baked, server values are runtime.** That's what the
- public `VITE_` prefix means: `virtual:env/client` is the validated
- output serialized as plain JSON (defaults applied, coercions done) with
- zero schema-library bytes. `virtual:env/server` is not baked — it reads
- `process.env` when the server boots and validates through your own
- schema (imported into the server bundle, where shipping the validator
- is fine). Platform-injected vars that don't exist at build time work,
- secrets rotate without a rebuild, and no secret value exists in any
- dist artifact; an invalid server environment fails boot with the same
- per-key report. Boot validation is synchronous: the generated server
- env module contains no top-level await, so the server bundle works
- under any downstream build target (Nitro's node-server preset,
- es2020 — no `esnext` override needed). The flip side: `server`
- validators must be synchronous — an async refinement/transform on a
- server key is rejected at config time with the fix in the message
- (`client` keys may stay async; they are awaited at build time where
- the values are baked).
-- **Leaks are errors.** Importing `virtual:env/server` from a client
- module graph is a hard error naming the importer (the app root and
- everything it imports hydrate — they are client code; keep server env
- in middleware, `"use server"` modules, or an authored server entry).
- Client keys must carry the public prefix (`VITE_`, or your `envPrefix`)
- — enforced at config time. And a client-build scan fails the build when
- a server var's literal value shows up quoted in a client chunk.
-- **Types are generated by inference.** A `solid-env.d.ts` is written next
- to the schema file (keep both inside your tsconfig `include`): it
- derives each var's type from your own schema through the Standard
- Schema output type, so `env.VITE_APP_NAME` is whatever your validator
- outputs — with any compliant library and no per-library plumbing.
-
-Env works identically in both `start` modes (a client-mode static build
-carries only the client vars); it is a start-mode feature, so without `start`
-there is no env layer. See `examples/start-env` for the full story,
-including the failure modes.
-
-Design credit: the shape of this feature — the schema-file convention,
-the `virtual:env/*` module names (kept identical on purpose), baked
-client values, the leak scan — follows
-[@vite-env/core](https://github.com/pyyupsk/vite-env) (MIT), the
-design-correct prior art, reimplemented on this plugin's machinery with
-Standard Schema as the only contract (and runtime-read server values).
-
-**`errorBoundary`** — in a production build, generated entries wrap the app
+**`productionErrorBoundary`** — in a production build, generated entries wrap the app
in a default error boundary (and the document in an outer one): a render
error streams a generic `500 | Internal Server Error` fallback — no stack
or error details reach the HTML; the error itself goes to `console.error`
— and an error caught before the shell flushes commits a real 500 status
through the response-head lifecycle. Development is unaffected (Vite's
error overlay owns dev errors), as are authored entries — the boundary is
-generated-entry codegen. Disable it with `start: { errorBoundary: false }`
+generated-entry codegen. Disable it with `app: { productionErrorBoundary: false }`
when application middleware owns error handling (an error middleware only
sees the throw when no boundary catches it first). Default: `true`.
@@ -482,7 +403,7 @@ server-inlined in dev:
```ts
solid({
- start: {
+ app: {
css: { filter: { include: /node_modules\/some-ui-lib/ } },
},
ssr: true,
@@ -498,7 +419,7 @@ assets.
**Entry resolution** (all paths relative to the Vite root):
-1. Explicit `start.entryServer` / `start.entryClient` options.
+1. Explicit `app.entries.server` / `app.entries.client` options.
2. Conventional files: `src/entry-server.{tsx,jsx,ts,js,mjs}` and
`src/entry-client.{tsx,jsx,ts,js,mjs}`. Entry files come in pairs —
providing only one is an error. The server entry must export
@@ -508,9 +429,9 @@ assets.
`"/src/entry-client.tsx"` reference in the rendered HTML is rewritten to
the hashed asset (the classic harness convention keeps working).
3. Generated entries (the zero-config path): when no entry files exist, both
- are generated from a root component — `start.app`, defaulting to
+ are generated from a root component — `app.root`, defaulting to
`src/App.{tsx,jsx,ts,js}` (or lowercase `src/app.*`) — wrapped in a
- document shell: `start.document`, defaulting to `src/Document.{tsx,jsx}`,
+ document shell: `app.document`, defaulting to `src/Document.{tsx,jsx}`,
else a built-in minimal shell. A custom document receives the app as
`props.children` and must render the full `` document including
` `; the client entry script is injected into ``
@@ -532,16 +453,16 @@ ownership and stands its HTTP middlewares down automatically.
Two explicit switches remain for custom host setups:
-1. **`start.external: true`** — hands the whole server side to a host that
+1. **`app.external: true`** — hands the whole server side to a host that
does not adopt Solid's normal `ssr` environment. Solid skips its
server-build wiring and stands its development middlewares down, while
continuing to provide the generated entries, client manifest, and
`virtual:solid-ssr-handler`. This is mainly for differently named or
independently configured environments.
2. **[`serverFunctions.devMiddleware: false`](#optionsserverfunctions)** —
- the narrow, endpoint-only switch: keeps start mode's server build and SSR
+ the narrow, endpoint-only switch: keeps app mode's server build and SSR
serving, hands only server-function dispatch in dev to the host. For
- setups without `start`, or when only the endpoint should move.
+ setups without `app`, or when only the endpoint should move.
**`virtual:solid-manifest`** exposes the client asset manifest that serving
works from — a server-side module, available in dev and in SSR builds. In
@@ -561,7 +482,7 @@ with client-only rendering:
```js
export default defineConfig({
- plugins: [solidPlugin({ start: true })],
+ plugins: [solidPlugin({ app: true })],
});
```
@@ -582,12 +503,12 @@ export default defineConfig({
- **`vite preview`** serves the static build with history fallback (and
dispatches the server-function endpoint through the kept handler).
- Server-only options are inert here rather than errors, so a config
- survives the flip untouched: `start.entryServer` (and conventional
+ survives the flip untouched: `app.entries.server` (and conventional
`src/entry-server.*` files) are ignored — the shell render is always
- generated — and so is `start.external`. An authored `src/entry-client.*`
+ generated — and so is `app.external`. An authored `src/entry-client.*`
stands alone and owns the mount.
-The point is the migration story: an app born with `start: true` moves to
+The point is the migration story: an app born with `app: true` moves to
server rendering by setting `ssr: true` — same `App`, same `Document`,
same routes, same server functions; the plugin swaps render for hydrate,
turns the hydratable transforms on, and ships the server bundle. (A
@@ -595,12 +516,78 @@ turns the hydratable transforms on, and ships the server bundle. (A
the plugin strips its script from the served shell — nothing hydrates, so
a shared `Document` costs nothing — and the built-in shell omits it.)
-Start-mode serving is opt-in via `start`, so bare `ssr: true` setups keep the
-transform-only behavior. See `examples/start-ssr` for a complete SSR app
+App-mode serving is opt-in via `app`, so bare `ssr: true` setups keep the
+transform-only behavior. See `examples/app-ssr` for a complete SSR app
(including a one-file production server and server functions),
-`examples/start-client` for client mode (whose test flips the same app
+`examples/app-client` for client mode (whose test flips the same app
between the modes), and `examples/ssr` for the manual `ssr: true` wiring.
+#### options.env
+
+- Type: Boolean | String
+- Default: undefined (probe `env.ts` / `env.js`; off when absent)
+
+First-party typed environment variables, independent of app mode. A schema
+file at the project root, `env.ts` or `env.js`, is probed automatically. Point
+elsewhere with `env: './path'`, or disable probing with `env: false`. The file
+default-exports `server` and `client` maps of
+[Standard Schema](https://standardschema.dev) validators (zod, valibot,
+arktype, or a mix of them):
+
+```ts
+// vite.config.ts
+solidPlugin({ env: true });
+
+// env.ts
+import { z } from 'zod';
+
+export default {
+ server: {
+ DATABASE_URL: z.url(),
+ SESSION_SECRET: z.string().min(32),
+ },
+ client: {
+ VITE_APP_NAME: z.string().min(1),
+ },
+};
+```
+
+The validated values come back through two fully typed virtual modules:
+
+```ts
+// server-only modules
+import { env } from 'virtual:env/server'; // every var
+
+// anywhere
+import { env } from 'virtual:env/client'; // the VITE_-prefixed client vars
+```
+
+- **Validation is node-only and layered.** The plugin loads the `.env*`
+ files through Vite's `loadEnv` with `process.env` winning, folds them into
+ `process.env`, and validates before anything builds. In dev, failures render
+ the error overlay and `.env*` or schema edits revalidate live. In a build,
+ client failures fail the build and server failures warn because production
+ secrets may only exist at runtime. Boot validation enforces server values.
+- **Client values are baked, server values are runtime.**
+ `virtual:env/client` contains validated plain JSON with no validator code.
+ `virtual:env/server` reads `process.env` at server boot and validates through
+ the schema in the server bundle. Server validators must be synchronous so
+ generated server modules do not require top-level await. Client validators
+ may be async because they run at build time.
+- **Leaks are errors.** Importing `virtual:env/server` from a client graph is
+ a hard error. Client keys must use Vite's public prefix, and the client build
+ fails if a server value is found in a client chunk.
+- **Types are generated by inference.** A `solid-env.d.ts` is written next to
+ the schema and derives each variable's output type through Standard Schema.
+
+Env works with app mode, manual SSR, router integrations, and client-only
+projects. See `examples/env` for the complete behavior and failure modes.
+
+Design credit: the schema convention, virtual module names, baked client
+values, and leak scan follow
+[@vite-env/core](https://github.com/pyyupsk/vite-env) (MIT), reimplemented
+against this plugin with Standard Schema and runtime-read server values.
+
#### options.serverFunctions
- Type: Boolean | Object
@@ -613,7 +600,7 @@ endpoint `/_server`) or an options object (`runtime`, `endpoint`, `filter`,
The setup is zero-config: in dev a middleware on the Vite server handles the
endpoint end to end — no server-function code needed in your server entry.
-For production SSR builds, either use SSR start mode ([`start`](#optionsstart)
+For production SSR builds, either use SSR app mode ([`app`](#optionsapp)
with `ssr: true`, whose handler serves the endpoint automatically) or
import `virtual:solid-server-function-handler` in your server entry and
mount its `handleServerFunctionRequest(request)` export on the endpoint.
@@ -632,7 +619,7 @@ side-effect import `virtual:solid-server-function-manifest` in its server
entry so functions referenced only by client code still register. (When a
provider owns the `ssr` environment outright — it isn't runnable — the
middleware already stands down automatically; see the `external` option
-under [`start`](#optionsstart) for the whole-server switch and how the
+under [`app`](#optionsapp) for the whole-server switch and how the
three options relate.)
**`configure: './src/server-config.ts'`** pins a server-only module (path
@@ -670,7 +657,7 @@ Set `csrf: false` only when another trusted layer protects the endpoint.
Meta-frameworks that need to control plugin ordering and dispatch requests
through their own server should use the standalone `serverFunctions()`
export instead, which never installs the dev middleware. See
-`examples/start-ssr` for a complete app.
+`examples/app-ssr` for a complete app.
**Server components (experimental):** `serverFunctions: { components: true }`
lets a `"use server"` function return a component. Server components ride
@@ -678,14 +665,14 @@ server functions — same endpoint, same compilation — with zero extra plugin
config: responses for component-returning functions are served as streamed
HTML that the client applies in place (client state and DOM identity inside
survive updates), and the plugin's dev middleware and production handler
-handle that automatically. Combined with SSR start mode
-([`start`](#optionsstart) with `ssr: true`) and generated entries, the
+handle that automatically. Combined with SSR app mode
+([`app`](#optionsapp) with `ssr: true`) and generated entries, the
document wiring is emitted too: server components render inline in the
SSR'd document and are adopted
at boot with zero endpoint requests. With authored entries, the app-side
pieces (the render plugin, the bootstrap script, and the client's
`installServerComponents()` call, all from `@solidjs/web/frames`) live in
-your entry files instead. See `examples/start-ssr` for a complete page.
+your entry files instead. See `examples/app-ssr` for a complete page.
#### options.compiler
diff --git a/examples/start-client/package.json b/examples/app-client/package.json
similarity index 91%
rename from examples/start-client/package.json
rename to examples/app-client/package.json
index a7a96f8..db08c88 100644
--- a/examples/start-client/package.json
+++ b/examples/app-client/package.json
@@ -1,5 +1,5 @@
{
- "name": "example-start-client",
+ "name": "example-app-client",
"private": "true",
"type": "module",
"scripts": {
diff --git a/examples/start-client/src/App.css b/examples/app-client/src/App.css
similarity index 100%
rename from examples/start-client/src/App.css
rename to examples/app-client/src/App.css
diff --git a/examples/start-client/src/App.tsx b/examples/app-client/src/App.tsx
similarity index 93%
rename from examples/start-client/src/App.tsx
rename to examples/app-client/src/App.tsx
index 740d231..d33a480 100644
--- a/examples/start-client/src/App.tsx
+++ b/examples/app-client/src/App.tsx
@@ -10,7 +10,7 @@ export default function App() {
return (
- Client Start Mode
+ Client App Mode
CLIENT-RENDERED-APP
setCount(count() + 1)}>
Increment
diff --git a/examples/start-client/src/Document.tsx b/examples/app-client/src/Document.tsx
similarity index 94%
rename from examples/start-client/src/Document.tsx
rename to examples/app-client/src/Document.tsx
index 8dc2515..f52df90 100644
--- a/examples/start-client/src/Document.tsx
+++ b/examples/app-client/src/Document.tsx
@@ -11,7 +11,7 @@ export default function Document(props) {
- Start Client
+ App Client
{props.children}
diff --git a/examples/start-client/src/LazySection.tsx b/examples/app-client/src/LazySection.tsx
similarity index 100%
rename from examples/start-client/src/LazySection.tsx
rename to examples/app-client/src/LazySection.tsx
diff --git a/examples/start-client/test/run.mjs b/examples/app-client/test/run.mjs
similarity index 94%
rename from examples/start-client/test/run.mjs
rename to examples/app-client/test/run.mjs
index cdda6dc..b55339d 100644
--- a/examples/start-client/test/run.mjs
+++ b/examples/app-client/test/run.mjs
@@ -1,6 +1,6 @@
-// Client start-mode fixture test: proves `solid({ start: true })` (the
-// zero-config sugar for `start: {}`, without `ssr: true`) gives a plain
-// Vite app the start-mode conventions (src/App.tsx, optional src/Document.tsx,
+// Client app-mode fixture test: proves `solid({ app: true })` (the
+// zero-config sugar for `app: {}`, without `ssr: true`) gives a plain
+// Vite app the app-mode conventions (src/App.tsx, optional src/Document.tsx,
// no index.html, no mount file) with client-only rendering:
// - dev: every HTML-accepting GET streams the rendered document shell —
// WITHOUT the app markup (nothing server-renders the app) — carrying the
@@ -91,7 +91,7 @@ async function fetchHtml(url) {
}
// ---------------------------------------------------------------------------
-// CDP driver (compact copy of the start-ssr harness's)
+// CDP driver (compact copy of the app-ssr harness's)
// ---------------------------------------------------------------------------
async function connectChrome() {
let target;
@@ -206,7 +206,7 @@ async function runBrowserChecks(mode, origin) {
const chrome = startProcess(CHROME, [
'--headless=new',
`--remote-debugging-port=${CDP_PORT}`,
- `--user-data-dir=/tmp/start-client-chrome-${mode}`,
+ `--user-data-dir=/tmp/app-client-chrome-${mode}`,
'--no-first-run',
'--disable-extensions',
'about:blank',
@@ -419,7 +419,7 @@ async function flipMode() {
const chrome = startProcess(CHROME, [
'--headless=new',
`--remote-debugging-port=${CDP_PORT}`,
- `--user-data-dir=/tmp/start-client-chrome-flip`,
+ `--user-data-dir=/tmp/app-client-chrome-flip`,
'--no-first-run',
'--disable-extensions',
'about:blank',
@@ -454,19 +454,31 @@ async function flipMode() {
// ---------------------------------------------------------------------------
const requested = process.argv[2];
const modes = requested ? [requested] : ['dev', 'prod', 'flip'];
-// `start: true` is pure sugar for `start: {}` (this suite's vite.config runs
+// `app: true` is pure sugar for `app: {}` (this suite's vite.config runs
// on the boolean form): both spellings must construct the identical plugin
-// set, and `start: false` must mean off exactly like omission.
+// set, and `app: false` must mean off exactly like omission.
{
const { default: solid } = await import('@solidjs/vite-plugin');
const names = (opts) => solid(opts).map((p) => p.name).join(',');
record(
'config',
'sugar',
- 'start: true constructs the same plugins as start: {}',
- names({ start: true }) === names({ start: {} }) &&
- names({ start: true }) !== names({}) &&
- names({ start: false }) === names({}),
+ 'app: true constructs the same plugins as app: {}',
+ names({ app: true }) === names({ app: {} }) &&
+ names({ app: true }) !== names({}) &&
+ names({ app: false }) === names({}),
+ );
+ let migrationError = '';
+ try {
+ names({ start: true });
+ } catch (error) {
+ migrationError = String(error);
+ }
+ record(
+ 'config',
+ 'migration',
+ 'start option reports the app replacement',
+ migrationError.includes('`start` has been renamed to `app`'),
);
}
try {
diff --git a/examples/start-client/tsconfig.json b/examples/app-client/tsconfig.json
similarity index 100%
rename from examples/start-client/tsconfig.json
rename to examples/app-client/tsconfig.json
diff --git a/examples/start-client/vite.config.ts b/examples/app-client/vite.config.ts
similarity index 66%
rename from examples/start-client/vite.config.ts
rename to examples/app-client/vite.config.ts
index 2eaa5cd..7a68bfa 100644
--- a/examples/start-client/vite.config.ts
+++ b/examples/app-client/vite.config.ts
@@ -1,9 +1,9 @@
import { defineConfig } from 'vite';
import solidPlugin from '@solidjs/vite-plugin';
-// Client start mode, zero-config spelling: `start: true` (sugar for
-// `start: {}` — both mean the identical start mode with defaults) opts
-// into the start-mode conventions, and the `ssr` boolean (false/omitted here)
+// Client app mode, zero-config spelling: `app: true` (sugar for
+// `app: {}` — both mean the identical app mode with defaults) opts
+// into the app-mode conventions, and the `ssr` boolean (false/omitted here)
// makes the app client-rendered. No index.html, no mount file, no server
// output: src/App.tsx is the app, src/Document.tsx (optional) is the shell.
// Dev streams the rendered shell for every HTML GET (history-fallback
@@ -14,7 +14,7 @@ import solidPlugin from '@solidjs/vite-plugin';
//
// SOLID_FLIP_SSR=1 flips the one boolean (test/run.mjs's flip mode): the
// identical app SSRs and hydrates with zero source changes. All suite modes
-// run on the boolean `start: true` form, covering the sugar end to end.
+// run on the boolean `app: true` form, covering the sugar end to end.
export default defineConfig({
- plugins: [solidPlugin({ start: true, ssr: !!process.env.SOLID_FLIP_SSR })],
+ plugins: [solidPlugin({ app: true, ssr: !!process.env.SOLID_FLIP_SSR })],
});
diff --git a/examples/start-ssr-external/LazyOutside.tsx b/examples/app-ssr-external/LazyOutside.tsx
similarity index 71%
rename from examples/start-ssr-external/LazyOutside.tsx
rename to examples/app-ssr-external/LazyOutside.tsx
index 807a96d..17c093d 100644
--- a/examples/start-ssr-external/LazyOutside.tsx
+++ b/examples/app-ssr-external/LazyOutside.tsx
@@ -1,5 +1,5 @@
-// Lives OUTSIDE the start-ssr example's Vite root (its asset key is
-// `../start-ssr-external/LazyOutside.tsx`) — the sibling-workspace-package
+// Lives OUTSIDE the app-ssr example's Vite root (its asset key is
+// `../app-ssr-external/LazyOutside.tsx`) — the sibling-workspace-package
// shape. Regression fixture for dev SSR lazy asset URLs (#298): a
// root-external key can't be served as `"/" + key` (`/../…` normalizes
// wrong in the browser); it needs Vite's `/@fs/` URL on the resolved
diff --git a/examples/start-ssr/package.json b/examples/app-ssr/package.json
similarity index 93%
rename from examples/start-ssr/package.json
rename to examples/app-ssr/package.json
index 7cb86b4..408f525 100644
--- a/examples/start-ssr/package.json
+++ b/examples/app-ssr/package.json
@@ -1,5 +1,5 @@
{
- "name": "example-start-ssr",
+ "name": "example-app-ssr",
"private": "true",
"type": "module",
"scripts": {
diff --git a/examples/start-ssr/server.js b/examples/app-ssr/server.js
similarity index 97%
rename from examples/start-ssr/server.js
rename to examples/app-ssr/server.js
index f8c14d2..00639e6 100644
--- a/examples/start-ssr/server.js
+++ b/examples/app-ssr/server.js
@@ -1,4 +1,4 @@
-// The entire production server for an SSR start-mode app: static client assets
+// The entire production server for an SSR app-mode app: static client assets
// plus one import — the built server bundle's `handleRequest`, an
// adapter-agnostic web `Request -> Response` handler that streams the SSR
// render, resolves hashed client assets through the build manifest, and
diff --git a/examples/start-ssr/src/App.css b/examples/app-ssr/src/App.css
similarity index 100%
rename from examples/start-ssr/src/App.css
rename to examples/app-ssr/src/App.css
diff --git a/examples/start-ssr/src/App.tsx b/examples/app-ssr/src/App.tsx
similarity index 97%
rename from examples/start-ssr/src/App.tsx
rename to examples/app-ssr/src/App.tsx
index df2589b..8520259 100644
--- a/examples/start-ssr/src/App.tsx
+++ b/examples/app-ssr/src/App.tsx
@@ -1,4 +1,4 @@
-// The entire app the user writes for SSR start mode: a plain content component
+// The entire app the user writes for SSR app mode: a plain content component
// (no , no HydrationScript, no entries — the plugin's generated
// document shell provides all of that). Exercises, for test/run.mjs:
// - hydration + client interactivity (the counter),
@@ -31,7 +31,7 @@ const OnlyClient = clientOnly(() => import('./ClientOnlyWidget'));
// - a module outside the Vite root — its dev URL must be a base-prefixed
// /@fs/ URL, not "/../…" (#298).
const LazyQuery = lazy(() => import('./QueryLazy.tsx?variant=a'));
-const LazyOutside = lazy(() => import('../../start-ssr-external/LazyOutside'));
+const LazyOutside = lazy(() => import('../../app-ssr-external/LazyOutside'));
function LazyAssetsSection() {
return (
@@ -134,7 +134,7 @@ export default function App() {
return (
- SSR Start Mode
+ SSR App Mode
setCount(count() + 1)}>
count
diff --git a/examples/start-ssr/src/ClientOnlyWidget.tsx b/examples/app-ssr/src/ClientOnlyWidget.tsx
similarity index 100%
rename from examples/start-ssr/src/ClientOnlyWidget.tsx
rename to examples/app-ssr/src/ClientOnlyWidget.tsx
diff --git a/examples/start-ssr/src/CustomDocument.tsx b/examples/app-ssr/src/CustomDocument.tsx
similarity index 100%
rename from examples/start-ssr/src/CustomDocument.tsx
rename to examples/app-ssr/src/CustomDocument.tsx
diff --git a/examples/start-ssr/src/HmrTarget.tsx b/examples/app-ssr/src/HmrTarget.tsx
similarity index 100%
rename from examples/start-ssr/src/HmrTarget.tsx
rename to examples/app-ssr/src/HmrTarget.tsx
diff --git a/examples/start-ssr/src/NestedLazy.tsx b/examples/app-ssr/src/NestedLazy.tsx
similarity index 100%
rename from examples/start-ssr/src/NestedLazy.tsx
rename to examples/app-ssr/src/NestedLazy.tsx
diff --git a/examples/start-ssr/src/NestedLazyLayout.tsx b/examples/app-ssr/src/NestedLazyLayout.tsx
similarity index 100%
rename from examples/start-ssr/src/NestedLazyLayout.tsx
rename to examples/app-ssr/src/NestedLazyLayout.tsx
diff --git a/examples/start-ssr/src/NestedLazyLeaf.tsx b/examples/app-ssr/src/NestedLazyLeaf.tsx
similarity index 100%
rename from examples/start-ssr/src/NestedLazyLeaf.tsx
rename to examples/app-ssr/src/NestedLazyLeaf.tsx
diff --git a/examples/start-ssr/src/QueryLazy.tsx b/examples/app-ssr/src/QueryLazy.tsx
similarity index 100%
rename from examples/start-ssr/src/QueryLazy.tsx
rename to examples/app-ssr/src/QueryLazy.tsx
diff --git a/examples/start-ssr/src/api.ts b/examples/app-ssr/src/api.ts
similarity index 100%
rename from examples/start-ssr/src/api.ts
rename to examples/app-ssr/src/api.ts
diff --git a/examples/start-ssr/src/db.ts b/examples/app-ssr/src/db.ts
similarity index 100%
rename from examples/start-ssr/src/db.ts
rename to examples/app-ssr/src/db.ts
diff --git a/examples/start-ssr/src/frames/FramesApp.tsx b/examples/app-ssr/src/frames/FramesApp.tsx
similarity index 94%
rename from examples/start-ssr/src/frames/FramesApp.tsx
rename to examples/app-ssr/src/frames/FramesApp.tsx
index 2eef82c..a1fadd3 100644
--- a/examples/start-ssr/src/frames/FramesApp.tsx
+++ b/examples/app-ssr/src/frames/FramesApp.tsx
@@ -4,11 +4,11 @@ import { getFreshPanel, getPanel, incrementCounter } from './data';
import Row from './Row';
/**
- * Server-components page: a plain content component like any other start-mode
- * app root. `serverFunctions: { components: true }` + SSR start mode's
+ * Server-components page: a plain content component like any other app-mode
+ * app root. `serverFunctions: { components: true }` + SSR app mode's
* generated entries emit every bit of wiring (the render plugin, the
* bootstrap script, the client-side installServerComponents() call) — this
- * file is only app code. The test's frames mode points `start.app` here.
+ * file is only app code. The test's frames mode points `app.root` here.
*
* The whole client surface for server components is `dynamic` over a server
* function call: every response for a call site resolves to the same stable
diff --git a/examples/start-ssr/src/frames/Row.tsx b/examples/app-ssr/src/frames/Row.tsx
similarity index 100%
rename from examples/start-ssr/src/frames/Row.tsx
rename to examples/app-ssr/src/frames/Row.tsx
diff --git a/examples/start-ssr/src/frames/data.tsx b/examples/app-ssr/src/frames/data.tsx
similarity index 100%
rename from examples/start-ssr/src/frames/data.tsx
rename to examples/app-ssr/src/frames/data.tsx
diff --git a/examples/start-ssr/src/middleware.ts b/examples/app-ssr/src/middleware.ts
similarity index 98%
rename from examples/start-ssr/src/middleware.ts
rename to examples/app-ssr/src/middleware.ts
index 82b33c2..70263a5 100644
--- a/examples/start-ssr/src/middleware.ts
+++ b/examples/app-ssr/src/middleware.ts
@@ -1,5 +1,5 @@
// Fetch-style middleware chain for the middleware/preview e2e modes
-// (SSR_MIDDLEWARE=1 wires it through `start.middleware` in vite.config.ts).
+// (SSR_MIDDLEWARE=1 wires it through `app.middleware` in vite.config.ts).
// Server-only: only the generated handler imports it. Exercises the whole
// contract:
// - runs inside the request-event scope: getRequestEvent() answers, locals
diff --git a/examples/start-ssr/src/posture.test.tsx b/examples/app-ssr/src/posture.test.tsx
similarity index 100%
rename from examples/start-ssr/src/posture.test.tsx
rename to examples/app-ssr/src/posture.test.tsx
diff --git a/examples/start-ssr/src/query-modules.d.ts b/examples/app-ssr/src/query-modules.d.ts
similarity index 100%
rename from examples/start-ssr/src/query-modules.d.ts
rename to examples/app-ssr/src/query-modules.d.ts
diff --git a/examples/start-ssr/src/server-posture.test.tsx b/examples/app-ssr/src/server-posture.test.tsx
similarity index 100%
rename from examples/start-ssr/src/server-posture.test.tsx
rename to examples/app-ssr/src/server-posture.test.tsx
diff --git a/examples/start-ssr/src/serverConfig.ts b/examples/app-ssr/src/serverConfig.ts
similarity index 100%
rename from examples/start-ssr/src/serverConfig.ts
rename to examples/app-ssr/src/serverConfig.ts
diff --git a/examples/start-ssr/src/setup.tsx b/examples/app-ssr/src/setup.tsx
similarity index 93%
rename from examples/start-ssr/src/setup.tsx
rename to examples/app-ssr/src/setup.tsx
index b9447a6..005823a 100644
--- a/examples/start-ssr/src/setup.tsx
+++ b/examples/app-ssr/src/setup.tsx
@@ -1,4 +1,4 @@
-// Per-request app setup (`start.setup`): the seam for routers that must
+// Per-request app setup (`app.setup`): the seam for routers that must
// prepare an app instance before SSR begins — TanStack-style
// `await router.load()` — receiving the shared request event (middleware
// locals included) and returning the component to render in the app's
diff --git a/examples/start-ssr/test/host-dispatch.mjs b/examples/app-ssr/test/host-dispatch.mjs
similarity index 100%
rename from examples/start-ssr/test/host-dispatch.mjs
rename to examples/app-ssr/test/host-dispatch.mjs
diff --git a/examples/start-ssr/test/http-bridge.mjs b/examples/app-ssr/test/http-bridge.mjs
similarity index 97%
rename from examples/start-ssr/test/http-bridge.mjs
rename to examples/app-ssr/test/http-bridge.mjs
index a6ddb4c..f4ca224 100644
--- a/examples/start-ssr/test/http-bridge.mjs
+++ b/examples/app-ssr/test/http-bridge.mjs
@@ -1,4 +1,4 @@
-// Node↔web bridge hardening e2e, run against the real start-mode dev
+// Node↔web bridge hardening e2e, run against the real app-mode dev
// middleware over TLS. Vite's dev server uses
// `http2.createSecureServer({ allowHTTP1: true })` whenever `server.https`
// is set without a proxy, so under https the plugin's middlewares receive
@@ -118,7 +118,7 @@ async function poll(predicate, ms = 3000) {
}
// ---------------------------------------------------------------------------
-// The dev server: root the start-ssr app, TLS on — exactly Vite's
+// The dev server: root the app-ssr app, TLS on — exactly Vite's
// `server.https` shape (http2 secure server with h1 fallback).
// ---------------------------------------------------------------------------
const server = await createServer({
@@ -144,7 +144,7 @@ try {
const page = await withTimeout(h2Request(origin, { path: '/' }), 15000, 'h2 page');
record(
'h2 SSR page renders (pseudo-headers skipped)',
- page.status === 200 && page.body.includes('SSR Start Mode'),
+ page.status === 200 && page.body.includes('SSR App Mode'),
`status ${page.status}: ${page.body.slice(0, 200)}`,
);
diff --git a/examples/start-ssr/test/run.mjs b/examples/app-ssr/test/run.mjs
similarity index 97%
rename from examples/start-ssr/test/run.mjs
rename to examples/app-ssr/test/run.mjs
index 5ae3d7a..ab55491 100644
--- a/examples/start-ssr/test/run.mjs
+++ b/examples/app-ssr/test/run.mjs
@@ -1,4 +1,4 @@
-// Start-mode kitchen-sink fixture test: proves `solid({ start: {}, ssr: true,
+// App-mode kitchen-sink fixture test: proves `solid({ app: {}, ssr: true,
// serverFunctions: true })` gives a plain Vite app working streaming SSR
// *and* "use server"
// server functions with zero wiring — no entry files, no index.html, no dev
@@ -23,13 +23,13 @@
// the respond() envelope — all round-trip from the browser),
// - server-only module code (the secret) never reaches the SSR html, the
// transformed client module, or any client asset,
-// - HMR works through the start-mode dev middleware under the native
+// - HMR works through the app-mode dev middleware under the native
// (Babel-free) pipeline: the solid-js/refresh wrapper is active, an
// on-disk edit hot-applies without a reload, sibling client state
// survives, and a CSS edit hot-applies into a single style element; the
// babel-hmr mode repeats those checks on a dev server forced to
// `compiler: 'babel'`,
-// - the `start.document` escape hatch swaps the document shell and the
+// - the `app.document` escape hatch swaps the document shell and the
// `serverFunctions.endpoint` option threads through middleware and
// runtime configure calls (separate dev servers, no browser),
// - `serverFunctions.configure` pins src/serverConfig.ts into the handler
@@ -78,7 +78,7 @@
// generated entries and client assets carry no reference to the
// server-components runtime when the option is off.
//
-// - `start.css.filter` (css-filter mode, CSS_FILTER= in
+// - `app.css.filter` (css-filter mode, CSS_FILTER= in
// vite.config.ts): the dev CSS crawl's include/exclude semantics against
// a temp node_modules package written by the test — `exclude` prunes an
// app graph (replacing the default node_modules exclusion), `include`
@@ -89,7 +89,7 @@
// path-keyed surfaces): httpStatus(404)/httpHeader reach the wire, a
// pre-flush Location is a real 3xx with no body, a post-flush Location
// emits the script-redirect fallback on the streamed 200,
-// - `start.middleware` (SSR_MIDDLEWARE=1, src/middleware.ts): composition
+// - `app.middleware` (SSR_MIDDLEWARE=1, src/middleware.ts): composition
// order, locals decoration visible to the page and to a server function
// over /_server (one request event fronts both), short-circuiting —
// with the handler edge's commitEventResponse fold carrying an
@@ -333,7 +333,7 @@ const APP_CSS_STYLE_SELECTOR = 'style[data-vite-dev-id$="App.css"]';
async function runSsrChecks(mode, origin) {
const { status, chunks, html } = await fetchStreamed(origin + '/');
record(mode, 'ssr', 'responds 200 to HTML-accepting GET', status === 200);
- record(mode, 'ssr', 'app server-rendered', html.includes('SSR Start Mode'));
+ record(mode, 'ssr', 'app server-rendered', html.includes('SSR App Mode'));
record(
mode,
'ssr',
@@ -492,7 +492,7 @@ async function runLazyAssetChecks(mode, origin, { dev, basePrefix = '' } = {}) {
'root-external module preloaded via base-prefixed /@fs/ URL',
!!externalHref &&
externalHref.startsWith(`${basePrefix}/@fs/`) &&
- externalHref.endsWith('start-ssr-external/LazyOutside.tsx'),
+ externalHref.endsWith('app-ssr-external/LazyOutside.tsx'),
`modulepreloads: ${preloads.join(', ') || '(none)'}`,
);
} else {
@@ -514,7 +514,7 @@ async function runLazyAssetChecks(mode, origin, { dev, basePrefix = '' } = {}) {
!!queryEntry?.file && queryHref === `${basePrefix}/${queryEntry.file}`,
`modulepreloads: ${preloads.join(', ') || '(none)'}`,
);
- const externalEntry = clientManifest['../start-ssr-external/LazyOutside.tsx'];
+ const externalEntry = clientManifest['../app-ssr-external/LazyOutside.tsx'];
record(
mode,
'lazy',
@@ -675,7 +675,7 @@ async function runBrowserChecks(
const chrome = startProcess(CHROME, [
'--headless=new',
`--remote-debugging-port=${CDP_PORT}`,
- `--user-data-dir=/tmp/start-ssr-chrome-${mode}`,
+ `--user-data-dir=/tmp/app-ssr-chrome-${mode}`,
'--no-first-run',
'--disable-extensions',
'about:blank',
@@ -818,7 +818,7 @@ async function runBrowserChecks(
} catch {}
await Promise.race([exited, new Promise((r) => setTimeout(r, 3000))]);
try {
- rmSync(`/tmp/start-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
+ rmSync(`/tmp/app-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
} catch {}
}
}
@@ -827,7 +827,7 @@ async function runCustomEntryDevtoolsChecks(mode, origin) {
const chrome = startProcess(CHROME, [
'--headless=new',
`--remote-debugging-port=${CDP_PORT}`,
- `--user-data-dir=/tmp/start-ssr-chrome-${mode}`,
+ `--user-data-dir=/tmp/app-ssr-chrome-${mode}`,
'--no-first-run',
'--disable-extensions',
'about:blank',
@@ -859,7 +859,7 @@ async function runCustomEntryDevtoolsChecks(mode, origin) {
} catch {}
await Promise.race([exited, new Promise((r) => setTimeout(r, 3000))]);
try {
- rmSync(`/tmp/start-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
+ rmSync(`/tmp/app-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
} catch {}
}
}
@@ -879,7 +879,7 @@ async function runDevMode() {
// requests have let the optimizer finish.
rmSync(path.join(exampleDir, 'node_modules/.vite'), { recursive: true, force: true });
- // The start-mode promise: the dev server is the plain `vite` CLI.
+ // The app-mode promise: the dev server is the plain `vite` CLI.
const server = startProcess('pnpm', ['exec', 'vite', '--port', String(port), '--strictPort'], {
cwd: exampleDir,
env: { ...process.env },
@@ -910,7 +910,7 @@ async function runDevMode() {
// from the query string when no instance header is present.
const cold = functionId
? await fetch(
- `${origin}/_server?id=${encodeURIComponent(functionId)}&args=${encodeURIComponent('["start-ssr"]')}`,
+ `${origin}/_server?id=${encodeURIComponent(functionId)}&args=${encodeURIComponent('["app-ssr"]')}`,
{ method: 'POST' },
)
: null;
@@ -919,7 +919,7 @@ async function runDevMode() {
mode,
'sf',
'cold dispatch before any SSR render (dev middleware)',
- coldText === 'hello start-ssr from the server',
+ coldText === 'hello app-ssr from the server',
functionId ? `got ${JSON.stringify(coldText)}` : 'could not extract function id',
);
const bogus = await fetch(origin + '/_server?id=bogus-0');
@@ -1072,7 +1072,7 @@ async function runProdMode() {
const origin = `http://localhost:${port}`;
console.log(' building…');
- // The start-mode promise: one plain `vite build` produces both bundles.
+ // The app-mode promise: one plain `vite build` produces both bundles.
execSync('pnpm run build', { cwd: exampleDir, stdio: 'pipe' });
record(
mode,
@@ -1111,7 +1111,7 @@ async function runProdMode() {
'build',
'default Fetchable ignores provider arguments',
fetchableResponse.status === 200 &&
- fetchableHtml.includes('SSR Start Mode') &&
+ fetchableHtml.includes('SSR App Mode') &&
!fetchableHtml.includes('provider-argument-must-not-be-forwarded'),
);
const nonceResponse = await builtHandler.handleRequest(new Request(origin + '/'), {
@@ -1157,7 +1157,7 @@ async function runProdMode() {
'no server-components transform in server bundle (option off)',
!serverBundle.includes('@solidjs/web/frames') && !serverBundle.includes('frameTransformResult'),
);
- // Dev-serve-only guarantee for `start.devtools`: production output carries
+ // Dev-serve-only guarantee for `app.devtools`: production output carries
// none of it — client assets checked via the toolbar's minification-proof
// DOM marker and the package name, the (unminified) server bundle via the
// package name and the virtual module id.
@@ -1270,7 +1270,7 @@ async function runProdMode() {
}
}
-// Document escape hatch: a separate dev server with `start.document` pointing
+// Document escape hatch: a separate dev server with `app.document` pointing
// at src/CustomDocument.tsx (via SSR_DOCUMENT in vite.config.ts); the custom
// shell's must show up in the SSR output. No browser needed.
async function runDocumentMode() {
@@ -1296,7 +1296,7 @@ async function runDocumentMode() {
'custom document shell rendered',
html.includes('Custom Document '),
);
- record(mode, 'document', 'app rendered inside custom shell', html.includes('SSR Start Mode'));
+ record(mode, 'document', 'app rendered inside custom shell', html.includes('SSR App Mode'));
} catch (e) {
record(
mode,
@@ -1314,7 +1314,7 @@ async function runDocumentMode() {
// Distinctive rule from the temp test-css-lib package's stylesheet: proves a
// node_modules graph's CSS was opted into the dev SSR crawl by
-// `start.css.filter.include`. Keep in sync with CSS_LIB_FIXTURES below.
+// `app.css.filter.include`. Keep in sync with CSS_LIB_FIXTURES below.
const LIB_CSS_COLOR = 'rgb(7, 140, 210)';
// Temp fixtures for the css-filter mode (written before the sub-runs,
@@ -1358,7 +1358,7 @@ async function runCssFilterMode() {
port: 3172,
checks: (html) => {
record(mode, 'exclude', 'excluded module graph is not crawled for CSS', !html.includes(APP_CSS_COLOR));
- record(mode, 'exclude', 'filter does not prevent app rendering', html.includes('SSR Start Mode'));
+ record(mode, 'exclude', 'filter does not prevent app rendering', html.includes('SSR App Mode'));
},
},
{
@@ -1377,7 +1377,7 @@ async function runCssFilterMode() {
'include-only filter keeps collecting app CSS',
html.includes(APP_CSS_COLOR),
);
- record(mode, 'include', 'filter does not prevent app rendering', html.includes('SSR Start Mode'));
+ record(mode, 'include', 'filter does not prevent app rendering', html.includes('SSR App Mode'));
},
},
{
@@ -1390,7 +1390,7 @@ async function runCssFilterMode() {
'file matching include and exclude stays excluded',
!html.includes(APP_CSS_COLOR),
);
- record(mode, 'conflict', 'filter does not prevent app rendering', html.includes('SSR Start Mode'));
+ record(mode, 'conflict', 'filter does not prevent app rendering', html.includes('SSR App Mode'));
},
},
{
@@ -1955,7 +1955,7 @@ async function runBuilderOrderMode() {
server.stderr.on('data', (d) => (serverLog += d));
await waitForHttp(origin + '/', 30000, { headers: { accept: 'text/html' } });
const { html } = await fetchStreamed(origin + '/');
- record(mode, 'prod', 'app server-rendered', html.includes('SSR Start Mode'));
+ record(mode, 'prod', 'app server-rendered', html.includes('SSR App Mode'));
record(
mode,
'prod',
@@ -2056,7 +2056,7 @@ async function runBuilderPrepareMode() {
server.stderr.on('data', (d) => (serverLog += d));
await waitForHttp(origin + '/', 30000, { headers: { accept: 'text/html' } });
const { html } = await fetchStreamed(origin + '/');
- record(mode, 'prod', 'app server-rendered', html.includes('SSR Start Mode'));
+ record(mode, 'prod', 'app server-rendered', html.includes('SSR App Mode'));
record(
mode,
'prod',
@@ -2087,8 +2087,8 @@ async function runBuilderPrepareMode() {
// Frames: server components (`use server` functions returning a function)
// enabled by the single config line `serverFunctions: { components: true }`
// (via SOLID_SERVER_COMPONENTS in vite.config.ts, which also points
-// `start.app` at the server-components page). Everything else is the stock
-// start-mode surface: generated entries carry the wiring the plugin emits for
+// `app.root` at the server-components page). Everything else is the stock
+// app-mode surface: generated entries carry the wiring the plugin emits for
// the option — the render plugin + direct-call transform in the server
// entry, installServerComponents() in the client entry (the _$SC registry
// self-bootstraps from serialized references; nothing is spliced into
@@ -2148,7 +2148,7 @@ async function runFramesChecks(mode, origin) {
const chrome = startProcess(CHROME, [
'--headless=new',
`--remote-debugging-port=${CDP_PORT}`,
- `--user-data-dir=/tmp/start-ssr-chrome-${mode}`,
+ `--user-data-dir=/tmp/app-ssr-chrome-${mode}`,
'--no-first-run',
'--disable-extensions',
'about:blank',
@@ -2361,7 +2361,7 @@ async function runFramesChecks(mode, origin) {
} catch {}
await Promise.race([exited, new Promise((r) => setTimeout(r, 3000))]);
try {
- rmSync(`/tmp/start-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
+ rmSync(`/tmp/app-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
} catch {}
}
}
@@ -2371,7 +2371,7 @@ async function runFramesMode() {
const devPort = 3166;
const prodPort = 3167;
// The one-line enablement under test: the env flag flips
- // `serverFunctions: { components: true }` + `start.app` in vite.config.ts.
+ // `serverFunctions: { components: true }` + `app.root` in vite.config.ts.
// No entry files — the plugin's generated entries carry all the wiring.
const env = { ...process.env, SOLID_SERVER_COMPONENTS: '1' };
@@ -2473,7 +2473,7 @@ async function runFramesMode() {
}
}
-// `start.middleware`: SSR_MIDDLEWARE=1 wires src/middleware.ts (two fetch-style
+// `app.middleware`: SSR_MIDDLEWARE=1 wires src/middleware.ts (two fetch-style
// functions, composed in order) through the generated handler. Asserted over
// plain HTTP in dev and prod (the same chain fronts both):
// - composition + the post-next() window: the streamed page carries headers
@@ -2634,7 +2634,7 @@ async function runMiddlewareChecksOverHttp(mode, origin, functionId) {
);
}
- // ---- Per-request app setup (start.setup, src/setup.tsx) ----------------
+ // ---- Per-request app setup (app.setup, src/setup.tsx) ----------------
// The hook runs between the middleware chain and renderToStream: its
// marker carries the request pathname, the locals the middleware
// decorated (ordering), and an invocation counter. It must land in the
@@ -2662,7 +2662,7 @@ async function runMiddlewareChecksOverHttp(mode, origin, functionId) {
mode,
'setup',
'app still renders inside the setup-provided root',
- setupFirst.html.includes('SSR Start Mode'),
+ setupFirst.html.includes('SSR App Mode'),
);
const setupSecond = await fetchStreamed(origin + '/');
const second = setupMarker(setupSecond.html);
@@ -2724,7 +2724,7 @@ async function runMiddlewareMode() {
unwind !== -1 && fold !== -1 && fold > unwind,
`runMiddleware @ ${unwind}, fold @ ${fold}`,
);
- // The generated entry-server threads start.setup: awaited with the
+ // The generated entry-server threads app.setup: awaited with the
// request event before renderToStream, its result (or App) rendered.
const entry = await probe.environments.ssr.transformRequest(
'virtual:solid-ssr-entry-server.tsx',
@@ -2738,7 +2738,7 @@ async function runMiddlewareMode() {
record(
'mw-codegen',
'gen',
- 'generated entry threads start.setup and boxes the async stream',
+ 'generated entry threads app.setup and boxes the async stream',
entryCode.includes('const prepared = ') &&
entryCode.includes('__solidSetupStream'),
);
@@ -2896,7 +2896,7 @@ async function runPreviewMode() {
mode,
'ssr',
'preview serves the SSR page through the built handler',
- page.status === 200 && page.html.includes('SSR Start Mode'),
+ page.status === 200 && page.html.includes('SSR App Mode'),
);
record(
mode,
@@ -2959,7 +2959,7 @@ async function runPreviewMode() {
}
// Non-root Vite `base` (SOLID_BASE=/app/): the base must hold end to end on
-// every start-mode surface. Regression coverage for the brenelz base cluster:
+// every app-mode surface. Regression coverage for the brenelz base cluster:
// - #300: `vite preview` strips the base from req.url before the plugin's
// post middleware runs, so the built handler's base-prefixed endpoint
// comparison never matched — /app/_server fell through to page rendering
@@ -2999,7 +2999,7 @@ async function runBaseMode() {
mode,
'dev',
'SSR page served under the base',
- page.status === 200 && page.html.includes('SSR Start Mode'),
+ page.status === 200 && page.html.includes('SSR App Mode'),
`status ${page.status}`,
);
record(
@@ -3087,7 +3087,7 @@ async function runBaseMode() {
mode,
'preview',
'preview serves the SSR page under the base',
- page.status === 200 && page.html.includes('SSR Start Mode'),
+ page.status === 200 && page.html.includes('SSR App Mode'),
`status ${page.status}`,
);
const entryMatch = new RegExp(
@@ -3178,7 +3178,7 @@ async function runBabelHmrMode() {
const chrome = startProcess(CHROME, [
'--headless=new',
`--remote-debugging-port=${CDP_PORT}`,
- `--user-data-dir=/tmp/start-ssr-chrome-${mode}`,
+ `--user-data-dir=/tmp/app-ssr-chrome-${mode}`,
'--no-first-run',
'--disable-extensions',
'about:blank',
@@ -3194,7 +3194,7 @@ async function runBabelHmrMode() {
} catch {}
await Promise.race([exited, new Promise((r) => setTimeout(r, 3000))]);
try {
- rmSync(`/tmp/start-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
+ rmSync(`/tmp/app-ssr-chrome-${mode}`, { recursive: true, force: true, maxRetries: 5 });
} catch {}
}
} catch (e) {
@@ -3229,7 +3229,7 @@ async function runExternalMode() {
mode,
'ssr',
'external handler renders the app',
- response.status === 200 && html.includes('SSR Start Mode'),
+ response.status === 200 && html.includes('SSR App Mode'),
);
record(
mode,
@@ -3334,7 +3334,7 @@ async function runDetectMode() {
mode,
'handler',
'handler self-serves through the provider environment',
- response.status === 200 && html.includes('SSR Start Mode'),
+ response.status === 200 && html.includes('SSR App Mode'),
);
record(
mode,
diff --git a/examples/start-ssr/tsconfig.json b/examples/app-ssr/tsconfig.json
similarity index 100%
rename from examples/start-ssr/tsconfig.json
rename to examples/app-ssr/tsconfig.json
diff --git a/examples/start-ssr/vite.config.ts b/examples/app-ssr/vite.config.ts
similarity index 91%
rename from examples/start-ssr/vite.config.ts
rename to examples/app-ssr/vite.config.ts
index 6b069f8..9fc1b1e 100644
--- a/examples/start-ssr/vite.config.ts
+++ b/examples/app-ssr/vite.config.ts
@@ -6,7 +6,7 @@ import { type Plugin } from 'vite';
import { defineConfig } from 'vitest/config';
import solidPlugin from '@solidjs/vite-plugin';
-// Start-mode kitchen sink: `start: {}` + `ssr: true` adds the serving layer on
+// App-mode kitchen sink: `app: {}` + `ssr: true` adds the serving layer on
// top of the SSR transforms, and `serverFunctions` composes with it. No
// entry files, no index.html, no dev server script — the plugin generates
// default entries around src/App.tsx, a dev middleware streams the render
@@ -17,20 +17,20 @@ import solidPlugin from '@solidjs/vite-plugin';
// dispatches endpoint requests to the server-function runtime before SSR.
//
// Test knobs (all exercised by test/run.mjs):
-// - SSR_DOCUMENT swaps the document shell via the `start.document` escape hatch.
+// - SSR_DOCUMENT swaps the document shell via the `app.document` escape hatch.
// - SERVER_FN_ENDPOINT overrides the server-function endpoint.
// - SERVER_FN_CONFIGURE pins src/serverConfig.ts into the handler graph via
// `serverFunctions.configure` (configure mode).
-// - SSR_MIDDLEWARE=1 wires src/middleware.ts through `start.middleware`
+// - SSR_MIDDLEWARE=1 wires src/middleware.ts through `app.middleware`
// (middleware and preview modes): a fetch-style chain fronting every
// dispatch path with getRequestEvent() live inside it.
-// - SSR_SETUP=1 wires src/setup.tsx through `start.setup` (middleware mode):
+// - SSR_SETUP=1 wires src/setup.tsx through `app.setup` (middleware mode):
// the per-request app-setup hook, awaited between the middleware chain and
// renderToStream with the shared request event in hand.
// - SERVER_FN_DEV_MIDDLEWARE=0 disables the built-in dev middleware via
// `serverFunctions.devMiddleware` (no-middleware mode) — endpoint dispatch
// becomes the host's job, like a Cloudflare-style environment plugin.
-// - SSR_DEVTOOLS=0 disables the development toolbar via `start.devtools`
+// - SSR_DEVTOOLS=0 disables the development toolbar via `app.devtools`
// (dev-mode off sub-run); by default the workspace's @solidjs/start-devtools
// install is auto-detected and the toolbar mounts in dev.
// - BUILD_SSR_FIRST installs an adversarial `builder.buildApp` that builds
@@ -117,13 +117,13 @@ export default defineConfig({
solidPlugin({
compiler: jsxCompiler,
ssr: true,
- start: serverComponents
- ? { app: 'src/frames/FramesApp.tsx' }
+ app: serverComponents
+ ? { root: 'src/frames/FramesApp.tsx' }
: process.env.SSR_DOCUMENT
? { document: process.env.SSR_DOCUMENT }
: {
external: !!process.env.SOLID_EXTERNAL,
- // CSS_FILTER (css-filter mode) exercises `start.css.filter`
+ // CSS_FILTER (css-filter mode) exercises `app.css.filter`
// against a temp app (src/CssLibApp.tsx, written by the test)
// whose graph pulls a temp node_modules package with CSS
// (test-css-lib, also written by the test):
@@ -139,14 +139,14 @@ export default defineConfig({
? { css: { filter: { exclude: /App\.tsx$/ } } }
: {}),
...(process.env.CSS_FILTER === 'include'
- ? { app: 'src/CssLibApp.tsx', css: { filter: { include: /test-css-lib/ } } }
+ ? { root: 'src/CssLibApp.tsx', css: { filter: { include: /test-css-lib/ } } }
: {}),
...(process.env.CSS_FILTER === 'conflict'
? { css: { filter: { include: /App\.tsx$/, exclude: /App\.tsx$/ } } }
: {}),
- ...(process.env.CSS_FILTER === 'default' ? { app: 'src/CssLibApp.tsx' } : {}),
+ ...(process.env.CSS_FILTER === 'default' ? { root: 'src/CssLibApp.tsx' } : {}),
// SSR_DEVTOOLS=0 (dev-mode sub-run) opts out of the development
- // toolbar via `start.devtools`. Without the knob the workspace's
+ // toolbar via `app.devtools`. Without the knob the workspace's
// @solidjs/start-devtools install is auto-detected, so plain dev
// runs double as coverage for the default-on wiring.
...(process.env.SSR_DEVTOOLS === '0' ? { devtools: false } : {}),
@@ -154,7 +154,7 @@ export default defineConfig({
// chain fronting every dispatch path — page SSR, /_server,
// preview — with getRequestEvent() live inside it.
...(process.env.SSR_MIDDLEWARE
- ? { middleware: './src/middleware.ts', errorBoundary: false }
+ ? { middleware: './src/middleware.ts', productionErrorBoundary: false }
: {}),
// SSR_SETUP=1 (middleware mode): the per-request app-setup
// hook — src/setup.tsx runs between the middleware chain and
diff --git a/examples/start-env/.env b/examples/env/.env
similarity index 100%
rename from examples/start-env/.env
rename to examples/env/.env
diff --git a/examples/start-env/env.async.ts b/examples/env/env.async.ts
similarity index 100%
rename from examples/start-env/env.async.ts
rename to examples/env/env.async.ts
diff --git a/examples/start-env/env.badprefix.ts b/examples/env/env.badprefix.ts
similarity index 100%
rename from examples/start-env/env.badprefix.ts
rename to examples/env/env.badprefix.ts
diff --git a/examples/start-env/env.fail.ts b/examples/env/env.fail.ts
similarity index 100%
rename from examples/start-env/env.fail.ts
rename to examples/env/env.fail.ts
diff --git a/examples/start-env/env.failclient.ts b/examples/env/env.failclient.ts
similarity index 100%
rename from examples/start-env/env.failclient.ts
rename to examples/env/env.failclient.ts
diff --git a/examples/start-env/env.serverprefix.ts b/examples/env/env.serverprefix.ts
similarity index 100%
rename from examples/start-env/env.serverprefix.ts
rename to examples/env/env.serverprefix.ts
diff --git a/examples/start-env/env.ts b/examples/env/env.ts
similarity index 91%
rename from examples/start-env/env.ts
rename to examples/env/env.ts
index 795fca7..08d8ae9 100644
--- a/examples/start-env/env.ts
+++ b/examples/env/env.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
import * as v from 'valibot';
-// The start-mode env schema: a plain object of Standard Schema validators —
+// The app-mode env schema: a plain object of Standard Schema validators —
// nothing imported from the plugin, and the validator libraries are mixed
// on purpose (zod for the server side, valibot for the client side) to
// prove the Standard Schema seam: any compliant library works, per key.
diff --git a/examples/start-env/package.json b/examples/env/package.json
similarity index 93%
rename from examples/start-env/package.json
rename to examples/env/package.json
index f6f222f..95981e3 100644
--- a/examples/start-env/package.json
+++ b/examples/env/package.json
@@ -1,5 +1,5 @@
{
- "name": "example-start-env",
+ "name": "example-env",
"private": "true",
"type": "module",
"scripts": {
diff --git a/examples/start-env/solid-env.d.ts b/examples/env/solid-env.d.ts
similarity index 93%
rename from examples/start-env/solid-env.d.ts
rename to examples/env/solid-env.d.ts
index 6ab2fdf..926f22e 100644
--- a/examples/start-env/solid-env.d.ts
+++ b/examples/env/solid-env.d.ts
@@ -1,4 +1,4 @@
-// Generated by @solidjs/vite-plugin (start.env) — do not edit.
+// Generated by @solidjs/vite-plugin (env) — do not edit.
// Regenerated on every dev server and build start from env.ts.
// Keep this file (and the schema) inside your tsconfig "include".
diff --git a/examples/start-env/src/App.tsx b/examples/env/src/App.tsx
similarity index 100%
rename from examples/start-env/src/App.tsx
rename to examples/env/src/App.tsx
diff --git a/examples/start-env/src/BadApp.tsx b/examples/env/src/BadApp.tsx
similarity index 100%
rename from examples/start-env/src/BadApp.tsx
rename to examples/env/src/BadApp.tsx
diff --git a/examples/start-env/src/Document.tsx b/examples/env/src/Document.tsx
similarity index 92%
rename from examples/start-env/src/Document.tsx
rename to examples/env/src/Document.tsx
index bd3a13c..99fe813 100644
--- a/examples/start-env/src/Document.tsx
+++ b/examples/env/src/Document.tsx
@@ -6,7 +6,7 @@ export default function Document(props: { children?: JSX.Element }) {
- Start Env
+ App Env
{props.children}
diff --git a/examples/start-env/src/LeakApp.tsx b/examples/env/src/LeakApp.tsx
similarity index 100%
rename from examples/start-env/src/LeakApp.tsx
rename to examples/env/src/LeakApp.tsx
diff --git a/examples/start-env/src/middleware.ts b/examples/env/src/middleware.ts
similarity index 100%
rename from examples/start-env/src/middleware.ts
rename to examples/env/src/middleware.ts
diff --git a/examples/start-env/test/run.mjs b/examples/env/test/run.mjs
similarity index 96%
rename from examples/start-env/test/run.mjs
rename to examples/env/test/run.mjs
index 8ca2896..6655db4 100644
--- a/examples/start-env/test/run.mjs
+++ b/examples/env/test/run.mjs
@@ -1,5 +1,5 @@
-// Start-mode typed-env fixture test: proves `start.env` gives a start-mode app
-// first-party typed environment variables from a root env.ts of Standard
+// Typed-env fixture test for the top-level `env` option in both app modes.
+// It uses a root env.ts of Standard
// Schema validators (zod + valibot mixed per key), with the validator never
// reaching any bundle:
// - dev (ssr mode): the app SSRs with virtual:env/client values, a
@@ -160,7 +160,7 @@ function readDistFiles(dir) {
}
// ---------------------------------------------------------------------------
-// CDP driver (compact copy of the start-ssr harness's)
+// CDP driver (compact copy of the app-ssr harness's)
// ---------------------------------------------------------------------------
async function connectChrome() {
let target;
@@ -232,7 +232,7 @@ async function browserCheck(mode, origin, checks) {
const chrome = startProcess(CHROME, [
'--headless=new',
`--remote-debugging-port=${CDP_PORT}`,
- `--user-data-dir=/tmp/start-env-chrome-${mode}`,
+ `--user-data-dir=/tmp/solid-env-chrome-${mode}`,
'--no-first-run',
'--disable-extensions',
'about:blank',
@@ -539,19 +539,30 @@ async function clientMode() {
// ---------------------------------------------------------------------------
const requested = process.argv[2];
const modes = requested ? [requested] : ['dev', 'guards', 'prod', 'client'];
-// Config-level: `start.env: false` removes the env plugin entirely; the
-// default (probe) includes it under start mode; without `start` there is no
-// env layer at all.
+// Config-level: `env: false` removes the env plugin entirely; the default
+// probes for a schema independently of app mode.
{
const { default: solid } = await import('@solidjs/vite-plugin');
const names = (opts) => solid(opts).map((p) => p.name);
record(
'config',
'gating',
- 'env plugin gated on start (+ env: false opt-out)',
- names({ start: true }).includes('solid:start-env') &&
- !names({ start: { env: false } }).includes('solid:start-env') &&
- !names({}).includes('solid:start-env'),
+ 'env plugin is top-level (+ env: false opt-out)',
+ names({}).includes('solid:env') &&
+ names({ env: true }).includes('solid:env') &&
+ !names({ env: false }).includes('solid:env'),
+ );
+ let migrationError = '';
+ try {
+ names({ app: { env: true } });
+ } catch (error) {
+ migrationError = String(error);
+ }
+ record(
+ 'config',
+ 'migration',
+ 'app.env reports the top-level replacement',
+ migrationError.includes('`app.env` has moved to the top level'),
);
}
try {
diff --git a/examples/start-env/tsconfig.json b/examples/env/tsconfig.json
similarity index 100%
rename from examples/start-env/tsconfig.json
rename to examples/env/tsconfig.json
diff --git a/examples/start-env/vite.config.ts b/examples/env/vite.config.ts
similarity index 76%
rename from examples/start-env/vite.config.ts
rename to examples/env/vite.config.ts
index 24df58c..e667cac 100644
--- a/examples/start-env/vite.config.ts
+++ b/examples/env/vite.config.ts
@@ -1,19 +1,19 @@
import { defineConfig } from 'vite';
import solidPlugin from '@solidjs/vite-plugin';
-// Start-mode typed env: `start.env` is left unset here so the suite covers the
+// Top-level typed env is left unset here so the suite covers the
// convention — env.ts at the project root is probed and picked up with zero
// config. No loadEnv one-liner either: the plugin folds the .env files into
// process.env itself.
//
// Fixture knobs (all driven by test/run.mjs):
-// - CLIENT_MODE=1 flips to client start mode (same env layer; the build
+// - CLIENT_MODE=1 flips to client app mode (same env layer; the build
// output is a static dist/client).
// - ENV_APP overrides the app root: src/BadApp.tsx imports
// virtual:env/server from the client graph (must fail the build with the
// server-only error), src/LeakApp.tsx hard-codes the secret's literal
// value (must trip the client-chunk leak scan).
-// - ENV_SCHEMA points start.env at a fixture schema (explicit-path option):
+// - ENV_SCHEMA points env at a fixture schema (explicit-path option):
// env.fail.ts requires a variable no .env provides (validation failure),
// env.badprefix.ts declares a client var without the VITE_ prefix
// (config-time prefix error), env.async.ts puts an async validator on a
@@ -27,11 +27,11 @@ export default defineConfig({
},
plugins: [
solidPlugin({
- start: {
+ app: {
middleware: './src/middleware.ts',
- ...(process.env.ENV_APP ? { app: process.env.ENV_APP } : {}),
- ...(process.env.ENV_SCHEMA ? { env: process.env.ENV_SCHEMA } : {}),
+ ...(process.env.ENV_APP ? { root: process.env.ENV_APP } : {}),
},
+ ...(process.env.ENV_SCHEMA ? { env: process.env.ENV_SCHEMA } : {}),
ssr: !process.env.CLIENT_MODE,
}),
],
diff --git a/examples/ssr/README.md b/examples/ssr/README.md
index 4019828..41ac697 100644
--- a/examples/ssr/README.md
+++ b/examples/ssr/README.md
@@ -1,12 +1,12 @@
# SSR example — manual wiring (the integrator path)
This example is the **integrator / meta-framework path**: `ssr: true` gives
-you the SSR transforms (hydratable client code, SSR server code) and *you*
+you the SSR transforms (hydratable client code, SSR server code) and _you_
own everything else — a middleware-mode Vite dev server embedded in your own
`server.js`, your own production server, and manual manifest handling (the
authored `/src/entry-client.tsx` script reference is rewritten to the hashed
-asset the classic way). This is the escape hatch the `start` option
-(`start: true` + `ssr: true`, see `examples/start-ssr`) is built on: if you are building a
+asset the classic way). This is the escape hatch the `app` option
+(`app: true` + `ssr: true`, see `examples/app-ssr`) is built on: if you are building a
framework, or need to control the server, start here.
What it demonstrates:
diff --git a/examples/ssr/test/boundary.mjs b/examples/ssr/test/boundary.mjs
index de50429..56bcd46 100644
--- a/examples/ssr/test/boundary.mjs
+++ b/examples/ssr/test/boundary.mjs
@@ -91,7 +91,7 @@ async function runBuild({ entry, ssr }) {
// must succeed quietly (still claiming the specifier, so the scanner does
// not chase it as a missing bare dependency, which would abort the scan all
// the same). Regression: cold-start "Failed to run dependency scan" banners
-// on apps whose 'use server' modules reach server-only code (the start-ssr
+// on apps whose 'use server' modules reach server-only code (the app-ssr
// suite's dev mode covers the end-to-end cold start).
{
const server = await createServer({
@@ -107,7 +107,9 @@ async function runBuild({ entry, ssr }) {
let devError = null;
try {
- await server.environments.client.transformRequest('/test/boundary-fixtures/server-only-import.ts');
+ await server.environments.client.transformRequest(
+ '/test/boundary-fixtures/server-only-import.ts',
+ );
} catch (error) {
devError = error;
}
diff --git a/examples/ssr/test/run.mjs b/examples/ssr/test/run.mjs
index d2aee19..25919ed 100644
--- a/examples/ssr/test/run.mjs
+++ b/examples/ssr/test/run.mjs
@@ -2,8 +2,8 @@
// path (`ssr: true` + your own middleware-mode dev server and production
// server, see server.js) still works end to end. Deliberately lean — the
// heavy assertions (streaming order, hydration, HMR, server functions, CSS
-// dedup) live in the start-ssr suite; this one guards the escape hatch the
-// `start` option is built on:
+// dedup) live in the app-ssr suite; this one guards the escape hatch the
+// `app` option is built on:
// - dev: `node server.js` (Vite in middleware mode) serves the SSR'd
// document with the Vite client injected,
// - prod: the classic two-step `vite build` (client) + `vite build --ssr`
@@ -34,7 +34,8 @@ const SERVER_FN_RUNTIME_PROBES = [
'createServerReference',
'configureServerFunctionsClient',
];
-const findServerFnProbe = (source) => SERVER_FN_RUNTIME_PROBES.find((probe) => source.includes(probe));
+const findServerFnProbe = (source) =>
+ SERVER_FN_RUNTIME_PROBES.find((probe) => source.includes(probe));
const children = new Set();
function cleanup(code = 0) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 042b187..0c304c2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -103,7 +103,7 @@ importers:
specifier: ^8.2.1
version: 8.2.1(@types/node@24.13.3)(esbuild@0.28.2)
- examples/css-matrix:
+ examples/app-client:
dependencies:
'@solidjs/web':
specifier: 'catalog:'
@@ -119,7 +119,7 @@ importers:
specifier: ^8.2.1
version: 8.2.1(@types/node@24.13.3)(esbuild@0.28.2)
- examples/ssr:
+ examples/env:
dependencies:
'@solidjs/web':
specifier: 'catalog:'
@@ -127,15 +127,24 @@ importers:
solid-js:
specifier: 'catalog:'
version: 2.0.0-rc.2
+ valibot:
+ specifier: ^1.1.0
+ version: 1.4.2(typescript@5.9.3)
+ zod:
+ specifier: ^4.1.0
+ version: 4.4.3
devDependencies:
'@solidjs/vite-plugin':
specifier: workspace:*
version: link:../..
+ esbuild:
+ specifier: ^0.28.0
+ version: 0.28.2
vite:
specifier: ^8.2.1
version: 8.2.1(@types/node@24.13.3)(esbuild@0.28.2)
- examples/start-client:
+ examples/app-ssr:
dependencies:
'@solidjs/web':
specifier: 'catalog:'
@@ -147,11 +156,17 @@ importers:
'@solidjs/vite-plugin':
specifier: workspace:*
version: link:../..
+ jsdom:
+ specifier: ^26.1.0
+ version: 26.1.0
vite:
specifier: ^8.2.1
version: 8.2.1(@types/node@24.13.3)(esbuild@0.28.2)
+ vitest:
+ specifier: ^4.1.11
+ version: 4.1.11(@types/node@24.13.3)(@vitest/browser-playwright@4.1.11)(jsdom@26.1.0)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.2))
- examples/start-env:
+ examples/css-matrix:
dependencies:
'@solidjs/web':
specifier: 'catalog:'
@@ -159,24 +174,15 @@ importers:
solid-js:
specifier: 'catalog:'
version: 2.0.0-rc.2
- valibot:
- specifier: ^1.1.0
- version: 1.4.2(typescript@5.9.3)
- zod:
- specifier: ^4.1.0
- version: 4.4.3
devDependencies:
'@solidjs/vite-plugin':
specifier: workspace:*
version: link:../..
- esbuild:
- specifier: ^0.28.0
- version: 0.28.2
vite:
specifier: ^8.2.1
version: 8.2.1(@types/node@24.13.3)(esbuild@0.28.2)
- examples/start-ssr:
+ examples/ssr:
dependencies:
'@solidjs/web':
specifier: 'catalog:'
@@ -188,15 +194,9 @@ importers:
'@solidjs/vite-plugin':
specifier: workspace:*
version: link:../..
- jsdom:
- specifier: ^26.1.0
- version: 26.1.0
vite:
specifier: ^8.2.1
version: 8.2.1(@types/node@24.13.3)(esbuild@0.28.2)
- vitest:
- specifier: ^4.1.11
- version: 4.1.11(@types/node@24.13.3)(@vitest/browser-playwright@4.1.11)(jsdom@26.1.0)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.2))
examples/vite-8:
dependencies:
diff --git a/src/dev-manifest.ts b/src/dev-manifest.ts
index fb0a92f..bf5f254 100644
--- a/src/dev-manifest.ts
+++ b/src/dev-manifest.ts
@@ -281,7 +281,7 @@ export async function collectDevStyleSources(
* Walks the SSR module graph from `files` (root-relative or absolute) and
* returns inline-style descriptors for every transitively imported CSS
* module — the same shape the dev asset resolver answers with for lazy
- * modules. Used by SSR start mode's dev middleware to inline the root entry's
+ * modules. Used by SSR app mode's dev middleware to inline the root entry's
* CSS into `` so server-painted content is styled from the first byte
* (no FOUC while waiting for Vite's client-side style injection).
*/
@@ -416,6 +416,7 @@ export function createDevAssetResolver(
};
return {
resolve,
- resolveSync: (key: string) => resolved.get(key) ?? { js: [devModuleUrl(root, base, key)], css: [] },
+ resolveSync: (key: string) =>
+ resolved.get(key) ?? { js: [devModuleUrl(root, base, key)], css: [] },
};
}
diff --git a/src/start-env.ts b/src/env.ts
similarity index 97%
rename from src/start-env.ts
rename to src/env.ts
index 7eff8f4..19b5ff4 100644
--- a/src/start-env.ts
+++ b/src/env.ts
@@ -1,4 +1,4 @@
-// Typed, validated environment variables as a start-mode feature (`start.env`):
+// Typed, validated environment variables (`env`):
// an `env.ts` at the project root default-exports `{ server, client }` maps
// of Standard Schema validators (zod, valibot, arktype — mixable per key),
// and the plugin exposes the validated values through two virtual modules:
@@ -290,7 +290,7 @@ function generateTypes(schema: EnvSchema, envFileAbs: string): void {
];
const content =
- `// Generated by @solidjs/vite-plugin (start.env) — do not edit.\n` +
+ `// Generated by @solidjs/vite-plugin (env) — do not edit.\n` +
`// Regenerated on every dev server and build start from ${path.basename(envFileAbs)}.\n` +
`// Keep this file (and the schema) inside your tsconfig "include".\n\n` +
moduleBlock(CLIENT_ENV_ID, clientFields) +
@@ -311,12 +311,10 @@ function generateTypes(schema: EnvSchema, envFileAbs: string): void {
}
/**
- * Start-mode typed env (the `start.env` option). Returns no plugin when the
- * feature is off (`env: false`, or nothing to probe); the feature is
- * start-only by construction — the option lives on `start`, so a bare
- * `ssr: true` setup has no env layer (documented).
+ * Typed env support for the top-level `env` option. Returns no active hooks
+ * when the feature is off (`env: false`, or nothing to probe).
*/
-export function startEnv(option: boolean | string | undefined): Plugin[] {
+export function envPlugin(option: boolean | string | undefined): Plugin[] {
if (option === false) return [];
let root = process.cwd();
@@ -336,12 +334,12 @@ export function startEnv(option: boolean | string | undefined): Plugin[] {
if (typeof option === 'string') {
const absolute = path.isAbsolute(option) ? option : path.resolve(root, option);
if (!existsSync(absolute)) {
- throw new Error(`[@solidjs/vite-plugin] start.env does not exist: ${option}`);
+ throw new Error(`[@solidjs/vite-plugin] env does not exist: ${option}`);
}
const relative = path.relative(root, absolute).split(path.sep).join('/');
if (relative.startsWith('..')) {
throw new Error(
- `[@solidjs/vite-plugin] start.env must live inside the Vite root: ${option}`,
+ `[@solidjs/vite-plugin] env must live inside the Vite root: ${option}`,
);
}
envFileAbs = absolute;
@@ -360,9 +358,9 @@ export function startEnv(option: boolean | string | undefined): Plugin[] {
}
if (option === true) {
throw new Error(
- `[@solidjs/vite-plugin] start.env is enabled but no schema file was found: add ` +
+ `[@solidjs/vite-plugin] env is enabled but no schema file was found: add ` +
`${ENV_FILE_CANDIDATES.join(' or ')} at the project root (default-exporting ` +
- `{ server, client } maps of Standard Schema validators), or point start.env ` +
+ `{ server, client } maps of Standard Schema validators), or point env ` +
`at a path.`,
);
}
@@ -523,7 +521,7 @@ export function startEnv(option: boolean | string | undefined): Plugin[] {
function envModuleCode(values: Record) {
return {
code:
- `// Generated by @solidjs/vite-plugin (start.env)\n` +
+ `// Generated by @solidjs/vite-plugin (env)\n` +
`export const env = Object.freeze(${JSON.stringify(values)});\n` +
`export default env;`,
moduleType: 'js' as const,
@@ -554,7 +552,7 @@ export function startEnv(option: boolean | string | undefined): Plugin[] {
if (!serverKeys.length) {
return {
code:
- `// Generated by @solidjs/vite-plugin (start.env) — server env.\n` +
+ `// Generated by @solidjs/vite-plugin (env) — server env.\n` +
`${baked}\n` +
`export const env = Object.freeze(__env);\n` +
`export default env;`,
@@ -563,7 +561,7 @@ export function startEnv(option: boolean | string | undefined): Plugin[] {
}
return {
code: [
- `// Generated by @solidjs/vite-plugin (start.env) — server env.`,
+ `// Generated by @solidjs/vite-plugin (env) — server env.`,
`// Server values are read from process.env and validated at boot;`,
`// client (public) values are baked at build time. Boot validation is`,
`// synchronous on purpose: a top-level await here would force esnext`,
@@ -600,7 +598,7 @@ export function startEnv(option: boolean | string | undefined): Plugin[] {
return [
{
- name: 'solid:start-env',
+ name: 'solid:env',
config(userConfig, env) {
root = path.resolve(userConfig.root || process.cwd());
diff --git a/src/index.ts b/src/index.ts
index f2b53dd..95bb762 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -15,14 +15,14 @@ import {
import { boundaryModules } from './boundary-modules.js';
import { serverFunctions, type ServerFunctionsOptions } from './server-functions/index.js';
-import { SSR_HANDLER_ID, startServe, type StartOptions } from './ssr/index.js';
-import { startEnv } from './start-env.js';
+import { SSR_HANDLER_ID, appServe, type AppOptions } from './ssr/index.js';
+import { envPlugin } from './env.js';
export { devStylePatch } from './dev-manifest.js';
export { serverFunctions };
export type { ServerFunctionsOptions };
export type { ServerFunctionsFilter } from './server-functions/index.js';
-export type { StartOptions };
+export type { AppOptions };
import path from 'path';
import type { FilterPattern, Plugin, ViteDevServer } from 'vite';
import { createFilter, defaultClientConditions, defaultServerConditions } from 'vite';
@@ -225,40 +225,54 @@ export interface Options {
/**
* Whether the app is server-rendered — one meaning everywhere.
*
- * Without {@link start}: the legacy transform-only flag, unchanged.
+ * Without {@link app}: the legacy transform-only flag, unchanged.
* `true` enables the SSR transforms (hydratable client code, SSR server
* code) — you provide the entries and the server yourself.
*
- * With {@link start}: selects the start mode. `true` is SSR start mode
+ * With {@link app}: selects the app mode. `true` is SSR app mode
* (per-request streaming render + hydration); `false`/omitted is client
- * mode (a static document shell + client-side `render()`). Flipping a
- * start-mode project between SPA and SSR is toggling this one boolean.
+ * mode (a static document shell + client-side `render()`). Flipping an
+ * app-mode project between SPA and SSR means toggling this one boolean.
*
* The flag describes the app's initial document, not the internal
* pipelines — client mode still compiles the document shell through the
* SSR transforms to serve/prerender it.
*
- * Objects are no longer accepted: start-mode options moved to {@link start}
+ * Objects are no longer accepted: app-mode options moved to {@link app}
* (`ssr: { ... }` from 3.0.0-next.23 and earlier becomes
- * `start: { ... }, ssr: true`).
+ * `app: { ... }, ssr: true`).
*
* @default false
*/
ssr?: boolean;
/**
- * Start mode — Start as a mode of the plugin: it owns entries, dev
- * serving, and the build — no index.html, no mount file, no server
- * wiring. `start: true` is the zero-config spelling, sugar for the empty
- * options bag `start: {}` (both mean the identical start mode with
+ * Typed, validated environment variables. A schema file, conventionally
+ * `env.ts` or `env.js` at the project root, default-exports `{ server?,
+ * client? }` maps of Standard Schema validators. Validated values are
+ * exposed through `virtual:env/server` and `virtual:env/client`.
+ *
+ * This capability is independent of app mode. `true` requires the
+ * conventional file, a string selects an explicit schema path, and
+ * `false` disables automatic probing.
+ *
+ * @default undefined (probe env.ts / env.js; off when absent)
+ */
+ env?: boolean | string;
+
+ /**
+ * App mode lets the plugin own entries, dev
+ * serving, and the build: no index.html, no mount file, no server
+ * wiring. `app: true` is the zero-config spelling, sugar for the empty
+ * options bag `app: {}` (both mean the identical app mode with
* defaults; `false`/absent is off). Conventions (shared by both modes,
* so projects flip between them by toggling {@link ssr}): `src/App.*`
- * (or `start.app`) is the root component; `src/Document.*` (or
- * `start.document`) is the optional document shell; authored
- * `src/entry-server.*` / `src/entry-client.*` (or `start.entryServer` /
- * `start.entryClient`) replace the generated entries.
+ * (or `app.root`) is the root component; `src/Document.*` (or
+ * `app.document`) is the optional document shell; authored
+ * `src/entry-server.*` / `src/entry-client.*` (or `app.entries.server` /
+ * `app.entries.client`) replace the generated entries.
*
- * With `ssr: true` — SSR start mode:
+ * With `ssr: true` — SSR app mode:
*
* - Dev: a middleware on the Vite dev server streams the rendered app for
* HTML-accepting GET requests — `vite` just works, no server file.
@@ -286,13 +300,13 @@ export interface Options {
* `serverFunctions` is enabled, in which case `dist/server` is kept and
* its `handleRequest` serves the endpoint (pages stay static).
* - Client code stays non-hydratable (`generate: 'dom'`), exactly like a
- * plain SPA; server-only options (`entryServer`, `external`) are inert.
+ * plain SPA; server-only options (`entries.server`, `external`) are inert.
* - `vite preview` serves the static build with history fallback (and
* dispatches the server-function endpoint through the kept handler).
*
* @default undefined
*/
- start?: boolean | StartOptions;
+ app?: boolean | AppOptions;
/**
* JSX compiler backend to use. The default `"native"` compiles through
@@ -376,7 +390,7 @@ export interface Options {
* components (experimental) — `"use server"` functions returning a
* component, served over the same endpoint. They come essentially for
* free: the endpoint transform is installed automatically, and with
- * SSR start mode (the `start` option with `ssr: true`) and generated entries
+ * SSR app mode (the `app` option with `ssr: true`) and generated entries
* the document wiring is emitted too. See
* {@link ServerFunctionsOptions.components}.
*
@@ -449,8 +463,8 @@ function getSolidOptions(
// `generate` still follows the transform's own ssr flag, so explicit
// node-environment tests (renderToString) keep their server codegen.
solidOptions = { generate: isSsr ? 'ssr' : 'dom', hydratable: false };
- } else if (options.start && !options.ssr) {
- // Client start mode: client code compiles exactly like a plain SPA
+ } else if (options.app && !options.ssr) {
+ // Client app mode: client code compiles exactly like a plain SPA
// (dom, non-hydratable — nothing hydrates); only the document shell
// render goes through the SSR transforms, also non-hydratable since
// the shell is inert HTML the client never claims.
@@ -545,11 +559,18 @@ function normalizeEmittedLazyEntries(manifest: Record) {
}
export default function solidPlugin(options: Partial = {}): Plugin[] {
+ if ((options as Partial & { start?: unknown }).start !== undefined) {
+ throw new Error(
+ '[@solidjs/vite-plugin] `start` has been renamed to `app`. Example: ' +
+ '`solid({ start: true })` becomes `solid({ app: true })`, and ' +
+ '`start: { app: "./src/App.tsx" }` becomes `app: { root: "./src/App.tsx" }`.',
+ );
+ }
if (typeof options.ssr === 'object') {
throw new Error(
'[@solidjs/vite-plugin] `ssr` now only accepts a boolean ("is the app server-rendered"); ' +
- 'move start-mode options to `start: {}` and set `ssr: true`. Example: ' +
- '`solid({ ssr: { document: … } })` becomes `solid({ start: { document: … }, ssr: true })`.',
+ 'move app-mode options to `app: {}` and set `ssr: true`. Example: ' +
+ '`solid({ ssr: { document: … } })` becomes `solid({ app: { document: … }, ssr: true })`.',
);
}
// Recreated in configResolved: relative include/exclude patterns must
@@ -558,12 +579,18 @@ export default function solidPlugin(options: Partial = {}): Plugin[] {
let filter = createFilter(options.include, options.exclude);
const serverComponents =
typeof options.serverFunctions === 'object' && !!options.serverFunctions.components;
- // `start: true` is sugar for the empty options bag — one start mode,
+ // `app: true` is sugar for the empty options bag — one app mode,
// two spellings — so normalize here and let everything downstream see a
// single shape (`false` behaves exactly like omission).
- const startOptions: StartOptions | null =
- options.start === true ? {} : options.start || null;
- const styleFilterOptions = startOptions?.css?.filter;
+ const appOptions: AppOptions | null =
+ options.app === true ? {} : options.app || null;
+ if (appOptions && 'env' in appOptions) {
+ throw new Error(
+ '[@solidjs/vite-plugin] `app.env` has moved to the top level. Example: ' +
+ '`solid({ app: { env: true } })` becomes `solid({ app: true, env: true })`.',
+ );
+ }
+ const styleFilterOptions = appOptions?.css?.filter;
// The CSS crawl walks the module graph from the app's own entries, so a
// plain createFilter allowlist can't express the option's purpose (opting
// node_modules graphs in): a bare `include` would reject the app sources
@@ -585,9 +612,9 @@ export default function solidPlugin(options: Partial = {}): Plugin[] {
};
let styleFilter = createStyleFilter();
const filterDevStyles = (id: string) => styleFilter(id);
- // `start.external` only means something when a server side exists to hand
- // over (SSR start mode); in client mode it is a documented no-op.
- const externalDevServer = !!options.ssr && !!startOptions?.external;
+ // `app.external` only means something when a server side exists to hand
+ // over (SSR app mode); in client mode it is a documented no-op.
+ const externalDevServer = !!options.ssr && !!appOptions?.external;
let needHmr = false;
let replaceDev = false;
@@ -863,13 +890,13 @@ export default function solidPlugin(options: Partial = {}): Plugin[] {
projectRoot = config.root;
filter = createFilter(options.include, options.exclude, { resolve: projectRoot });
styleFilter = createStyleFilter(projectRoot);
- if (serverComponents && !(options.start && options.ssr)) {
+ if (serverComponents && !(options.app && options.ssr)) {
config.logger.warn(
- '[@solidjs/vite-plugin] serverFunctions.components is set without SSR start mode (the `start` ' +
+ '[@solidjs/vite-plugin] serverFunctions.components is set without SSR app mode (the `app` ' +
'option with `ssr: true`), so the plugin only installs the endpoint response transform ' +
'(server functions returning components stream correctly). The document wiring — render ' +
'plugin, bootstrap script, and the client-side installServerComponents() call — is ' +
- "emitted by SSR start mode's generated entries; without it, server components only mount " +
+ "emitted by SSR app mode's generated entries; without it, server components only mount " +
'from post-boot streams and your client code must call installServerComponents() itself.',
);
}
@@ -887,7 +914,7 @@ export default function solidPlugin(options: Partial = {}): Plugin[] {
// registry keyed by project root — or, from isolated module runners
// that don't share globals with this process, through the HTTP bridge
// endpoint the middleware serves.
- if (options.ssr || options.start) {
+ if (options.ssr || options.app) {
registerDevAssetResolver(
server.config.root,
createDevAssetResolver(server, filterDevStyles),
@@ -1186,25 +1213,23 @@ export default function solidPlugin(options: Partial = {}): Plugin[] {
...serverFunctions(options.serverFunctions === true ? {} : options.serverFunctions, {
devMiddleware: true,
externalDevServer,
- // With start mode on (either variant), the dev middleware dispatches
+ // With app mode on (either variant), the dev middleware dispatches
// the endpoint through the SSR handler so user middleware and the
// stub-backed request event front it exactly like page SSR.
- ...(startOptions ? { ssrHandler: SSR_HANDLER_ID } : {}),
+ ...(appOptions ? { ssrHandler: SSR_HANDLER_ID } : {}),
}),
mainPlugin,
]
: [boundaryModules(), mainPlugin];
- // The `start` option opts into start-mode serving on top of the transforms;
+ plugins.push(...envPlugin(options.env));
+
+ // The `app` option opts into app-mode serving on top of the transforms;
// the `ssr` boolean picks the mode (a bare `ssr: true` keeps the
// historical transform-only behavior).
- if (startOptions) {
+ if (appOptions) {
plugins.push(
- // Typed env (`start.env`) rides both start modes: config-time
- // validation, the virtual:env/{server,client} modules, generated
- // types, and the client-bundle leak scan.
- ...startEnv(startOptions.env),
- ...startServe(startOptions, {
+ ...appServe(appOptions, {
serverFunctions: !!options.serverFunctions,
serverComponents,
ssr: !!options.ssr,
@@ -1217,7 +1242,7 @@ export default function solidPlugin(options: Partial = {}): Plugin[] {
// Server builds read the client manifest — `virtual:solid-manifest` bakes
// dist/client/.vite/manifest.json in, and the persisted server-function
// manifest merges the client build's discoveries — so the client
- // environment must build first. Start mode's own orchestration already
+ // environment must build first. App mode's own orchestration already
// orders it that way (environment definition order), but a composed setup
// whose orchestrator builds server environments first (e.g.
// @cloudflare/vite-plugin's buildApp, which builds workers before client)
@@ -1244,7 +1269,7 @@ export default function solidPlugin(options: Partial = {}): Plugin[] {
// exists either way when the server environments build).
// - Building anything from a hook suppresses Vite's own
// build-all-environments fallback (it only runs when *no* environment
- // is built), so a setup with no real orchestrator — e.g. start mode's
+ // is built), so a setup with no real orchestrator — e.g. app mode's
// plain `builder: {}` — would end up with only the client built. The
// post-order hook reinstates exactly that fallback: when nothing but
// our own client build has happened and no other plugin stakes a claim
diff --git a/src/server-functions/index.ts b/src/server-functions/index.ts
index faeac2f..7109c79 100644
--- a/src/server-functions/index.ts
+++ b/src/server-functions/index.ts
@@ -95,14 +95,14 @@ export interface ServerFunctionsOptions {
*
* When a provider owns the dev server's `ssr` environment (it isn't
* runnable), the middleware already stands down automatically — no need
- * to set this. See `start.external` for the whole-server switch.
+ * to set this. See `app.external` for the whole-server switch.
*
* @default true (stands down automatically when the `ssr` dev environment isn't runnable)
*/
devMiddleware?: boolean;
/**
* Path to a server-only module (resolved relative to the Vite root, like
- * `start.document`) that the generated
+ * `app.document`) that the generated
* `virtual:solid-server-function-handler` module side-effect imports
* before configuring the runtime. A guaranteed pre-dispatch home for
* server-side registration — typically `configureServerFunctionsServer`
@@ -141,8 +141,8 @@ export interface ServerFunctionsOptions {
* at boot with zero endpoint requests) needs three more pieces: the
* render must run with the server-component render plugin, the document
* must carry the bootstrap script, and the client must call
- * `installServerComponents()` before hydrating. With SSR start mode (the
- * main plugin's `start` option with `ssr: true`) and generated entries
+ * `installServerComponents()` before hydrating. With SSR app mode (the
+ * main plugin's `app` option with `ssr: true`) and generated entries
* the plugin emits all three. With authored entries those pieces live in
* your entry files — import them from `@solidjs/web/frames` (see the
* README).
@@ -542,7 +542,7 @@ export function serverFunctions(
}
// Dispatch through a module evaluated in the SSR environment so
// the handler shares the registry instance with the app modules.
- // With SSR start mode active the main plugin threads its handler id
+ // With SSR app mode active the main plugin threads its handler id
// in, and dispatch goes through `handleRequest` instead — one
// middleware chain and one stub-backed request event front the
// endpoint exactly as they front page SSR.
diff --git a/src/ssr/index.ts b/src/ssr/index.ts
index ed308b5..eef339c 100644
--- a/src/ssr/index.ts
+++ b/src/ssr/index.ts
@@ -1,12 +1,12 @@
-// Start-mode serving for plain Vite apps: `solid({ start: {...} })` (or the
-// zero-config sugar `start: true`) adds a serving layer with conventional
+// App-mode serving for plain Vite apps: `solid({ app: {...} })` (or the
+// zero-config sugar `app: true`) adds a serving layer with conventional
// entries so no hand-rolled wiring is needed, and the plugin's `ssr`
// boolean picks the mode — `ssr: true` server-renders the app per request;
// `ssr: false`/omitted is client mode (the same conventions, but the
// document shell is served/prerendered empty and the app `render()`s
// client-side). The flip between them is that one boolean.
//
-// SSR mode (`start` + `ssr: true`):
+// SSR mode (`app` + `ssr: true`):
// - Dev: runnable SSR environments are served by a Vite middleware. Provider-
// owned environments serve through `virtual:solid-ssr-handler` instead.
// Both paths inject the Vite client, dev style patch, and entry CSS as
@@ -21,13 +21,13 @@
// - Entries are conventional with escape hatches: `src/entry-server.*` /
// `src/entry-client.*` are used when present (or set explicitly); when
// absent, default entries are generated from a single root component
-// (`start.app`, defaulting to `src/App.*`) wrapped in a document shell
-// (`start.document`, defaulting to `src/Document.*`, else a built-in one).
+// (`app.root`, defaulting to `src/App.*`) wrapped in a document shell
+// (`app.document`, defaulting to `src/Document.*`, else a built-in one).
// - When `serverFunctions` is also enabled, the handler composes the
// endpoint on every surface; the runnable-dev server-function middleware
// pre-loads the referenced module, then dispatches through this handler.
// - Every dispatch runs under a stub-backed request event
-// (`createRequestEvent`) with the optional `start.middleware` chain fronting
+// (`createRequestEvent`) with the optional `app.middleware` chain fronting
// it, and page responses go through the runtime's `createSSRResponse`
// head lifecycle (commit at shell flush, real pre-flush redirects, the
// script fallback post-flush).
@@ -35,7 +35,7 @@
// else through the built handler — the production path, middleware
// included, with no server file needed.
//
-// Client mode (`start` without `ssr: true`) rides the same machinery with
+// Client mode (`app` without `ssr: true`) rides the same machinery with
// three deltas: the generated server entry renders the document shell
// WITHOUT the app (dev serving doubles as history fallback, and a
// post-build hook prerenders it once into dist/client/index.html), the
@@ -69,22 +69,22 @@ import {
import { joinBase, sendWebResponse, webRequestFromNode } from '../http.js';
/**
- * Options for the main plugin's `start` option (`start: true` is
+ * Options for the main plugin's `app` option (`app: true` is
* sugar for the empty bag). One bag serves both modes — the plugin's `ssr`
* boolean picks between them, so flipping a project between
* client-rendered and server-rendered is toggling that boolean, never
- * reshaping this object. Server-only options (`entryServer`, `external`)
+ * reshaping this object. Server-only options (`entries.server`, `external`)
* are documented no-ops in client mode: they stay in the config across a
* flip instead of erroring.
*/
-export interface StartOptions {
+export interface AppOptions {
/**
* Root component module for generated entries (the zero-config path).
* Resolved relative to the Vite root.
*
* @default "src/App.{tsx,jsx,ts,js}" (also probes lowercase "src/app.*")
*/
- app?: string;
+ root?: string;
/** Options for development CSS crawling. */
css?: {
/**
@@ -108,29 +108,22 @@ export interface StartOptions {
exclude?: FilterPattern;
};
};
- /**
- * Server entry module. Must export `render(request?, context?)` returning
- * a `renderToStream` result, an HTML string, or a `Response`.
- * `context.clientEntry` carries the resolved client entry URL.
- *
- * Server mode only — ignored in client mode, where the server entry is
- * always generated (it renders the document shell without the app, for
- * dev serving and the build-time prerender). Conventional
- * `src/entry-server.*` files are likewise ignored there.
- *
- * @default "src/entry-server.{tsx,jsx,ts,js,mjs}" when present, else a
- * generated entry rendering ` `
- */
- entryServer?: string;
- /**
- * Client entry module. In SSR mode it hydrates; in client mode it mounts
- * (a generated one calls `render()`), and it stands alone — no pairing
- * rule with a server entry.
- *
- * @default "src/entry-client.{tsx,jsx,ts,js,mjs}" when present, else a
- * generated entry
- */
- entryClient?: string;
+ /** Explicit rendering entries. Conventional entry files are used when omitted. */
+ entries?: {
+ /**
+ * Client entry module. In SSR mode it hydrates; in client mode it mounts.
+ *
+ * @default "src/entry-client.{tsx,jsx,ts,js,mjs}" when present, else a generated entry
+ */
+ client?: string;
+ /**
+ * Server entry module. Must export `render(request?, context?)`.
+ * Ignored in client mode, where the server entry renders only the shell.
+ *
+ * @default "src/entry-server.{tsx,jsx,ts,js,mjs}" when present, else a generated entry
+ */
+ server?: string;
+ };
/**
* Document shell component wrapping the app in generated entries. Receives
* `props.children` and must render the full `` document including
@@ -187,43 +180,6 @@ export interface StartOptions {
* @default undefined
*/
setup?: string;
- /**
- * Typed, validated environment variables. A schema file — conventionally
- * `env.ts` (or `env.js`) at the project root, probed automatically —
- * default-exports `{ server?, client? }` maps of Standard Schema
- * validators (zod, valibot, arktype, mixable per key), and the plugin
- * exposes the validated values through `virtual:env/server` (all vars,
- * server module graphs only — a client-graph import is a hard error) and
- * `virtual:env/client` (the `VITE_`-prefixed `client` side; the prefix is
- * enforced at config time). Validation runs at config/build time in node
- * only against Vite's `loadEnv` merge of the `.env*` files (with
- * `process.env` winning), which the plugin also folds into `process.env`
- * itself — no `loadEnv` boilerplate in vite.config. Failures fail the
- * build / render the dev error overlay with the per-key report, and a
- * `solid-env.d.ts` is generated next to the schema so both virtual
- * modules are fully typed by inference.
- *
- * Client values are baked as plain JSON (that's what `VITE_` means); no
- * validator ships in a client bundle, and a client-build leak scan
- * errors when a server value shows up in a client chunk. Server values
- * are NOT baked: `virtual:env/server` reads `process.env` at server boot
- * and validates through your schema (imported into the server bundle
- * only), so platform-injected vars work and secrets rotate without a
- * rebuild — no secret exists in any dist artifact. Build-time server
- * failures are a deferred-to-boot warning; dev failures stay hard.
- * Boot validation is synchronous — the generated module carries no
- * top-level await, so server bundles work on non-esnext targets
- * (Nitro's node-server preset needs no `esnext` override) — which is
- * why async validators are rejected for `server` keys at config time
- * (`client` keys may stay async: they are awaited at build time).
- *
- * `true` requires the conventional file (error when missing); a string
- * is an explicit schema path; `false` disables even the probing.
- * Env is a start-mode feature: without `start` there is no env layer.
- *
- * @default undefined (probe env.ts / env.js; off when absent)
- */
- env?: boolean | string;
/**
* Enable the development toolbar. By default it is enabled when
* `@solidjs/start-devtools` is installed. Setting this to `true` requires
@@ -239,10 +195,10 @@ export interface StartOptions {
*
* @default true
*/
- errorBoundary?: boolean;
+ productionErrorBoundary?: boolean;
/**
* Let a host integration own the server environment — build wiring and
- * HTTP serving alike. The plugin skips its start-mode server-build config and
+ * HTTP serving alike. The plugin skips its app-mode server-build config and
* stands its dev middlewares down (SSR serving and the server-function
* endpoint); the generated `virtual:solid-ssr-handler` self-serves
* instead, inlining dev styles through a virtual module and composing the
@@ -268,10 +224,10 @@ export interface StartOptions {
external?: boolean;
}
-// Server-only start-mode request handler; also the server bundle's entry so a
+// Server-only app-mode request handler; also the server bundle's entry so a
// production server is one import away from `Request -> Response`. Exported
// for the main plugin to thread into the server-function dev middleware,
-// which dispatches through it when SSR start mode is active (one middleware
+// which dispatches through it when SSR app mode is active (one middleware
// chain and one request event across both dispatch paths).
export const SSR_HANDLER_ID = 'virtual:solid-ssr-handler';
const HANDLER_ID = SSR_HANDLER_ID;
@@ -282,7 +238,7 @@ const HANDLER_ID = SSR_HANDLER_ID;
// such seam — every unhandled request renders — but production also has no
// Vite pipeline to fall back to.
const DEV_FALLTHROUGH_HEADER = 'x-solid-dev-fallthrough';
-// Private protocol between the two generated modules when `start.setup` is
+// Private protocol between the two generated modules when `app.setup` is
// async: the entry hands the handler the renderToStream result under this
// key, because a promise resolving to the stream BARE would adopt the
// stream's thenable (which waits for the complete render) and buffer it.
@@ -316,11 +272,11 @@ function probe(root: string, stem: string, extensions: string[]): string | null
function normalizeUserPath(root: string, spec: string, option: string): string {
const absolute = path.isAbsolute(spec) ? spec : path.resolve(root, spec);
if (!existsSync(absolute)) {
- throw new Error(`[@solidjs/vite-plugin] start.${option} does not exist: ${spec}`);
+ throw new Error(`[@solidjs/vite-plugin] app.${option} does not exist: ${spec}`);
}
const relative = path.relative(root, absolute).split(path.sep).join('/');
if (relative.startsWith('..')) {
- throw new Error(`[@solidjs/vite-plugin] start.${option} must live inside the Vite root: ${spec}`);
+ throw new Error(`[@solidjs/vite-plugin] app.${option} must live inside the Vite root: ${spec}`);
}
return relative;
}
@@ -338,15 +294,15 @@ interface ResolvedEntries {
document: string | null;
}
-function resolveEntries(root: string, options: StartOptions, clientMode: boolean): ResolvedEntries {
- const explicitClient = options.entryClient
- ? normalizeUserPath(root, options.entryClient, 'entryClient')
+function resolveEntries(root: string, options: AppOptions, clientMode: boolean): ResolvedEntries {
+ const explicitClient = options.entries?.client
+ ? normalizeUserPath(root, options.entries.client, 'entries.client')
: null;
if (clientMode) {
// Client mode: the server entry is always generated (it renders the
// document shell only — no App — for dev serving and the build-time
- // prerender); `start.entryServer` and conventional src/entry-server.*
+ // prerender); `app.entries.server` and conventional src/entry-server.*
// files are documented no-ops here, so a project flipping the `ssr`
// boolean never has to touch them. No entry pairing rule either: an
// authored client entry stands alone. The document resolves in every
@@ -364,13 +320,13 @@ function resolveEntries(root: string, options: StartOptions, clientMode: boolean
document: document ? path.resolve(root, document) : null,
};
}
- const app = options.app
- ? normalizeUserPath(root, options.app, 'app')
+ const app = options.root
+ ? normalizeUserPath(root, options.root, 'root')
: (probe(root, 'src/App', APP_EXTENSIONS) ?? probe(root, 'src/app', APP_EXTENSIONS));
if (!app) {
throw new Error(
- `[@solidjs/vite-plugin] the \`start\` option needs an app root: add src/App.tsx ` +
- `(or set start.app), or provide a src/entry-client.* entry.`,
+ `[@solidjs/vite-plugin] the \`app\` option needs a root component: add src/App.tsx ` +
+ `(or set app.root), or provide a src/entry-client.* entry.`,
);
}
return {
@@ -382,8 +338,8 @@ function resolveEntries(root: string, options: StartOptions, clientMode: boolean
};
}
- const explicitServer = options.entryServer
- ? normalizeUserPath(root, options.entryServer, 'entryServer')
+ const explicitServer = options.entries?.server
+ ? normalizeUserPath(root, options.entries.server, 'entries.server')
: null;
const entryServer = explicitServer ?? probe(root, 'src/entry-server', ENTRY_EXTENSIONS);
const entryClient = explicitClient ?? probe(root, 'src/entry-client', ENTRY_EXTENSIONS);
@@ -399,18 +355,18 @@ function resolveEntries(root: string, options: StartOptions, clientMode: boolean
const missing = entryServer ? 'entry-client' : 'entry-server';
throw new Error(
`[@solidjs/vite-plugin] found ${found} but no ${missing}; entry files come in pairs. ` +
- `Provide both (src/entry-server.* and src/entry-client.*, or the start.entryServer / ` +
- `start.entryClient options) or neither (to generate both from start.app).`,
+ `Provide both (src/entry-server.* and src/entry-client.*, or app.entries.server / ` +
+ `app.entries.client) or neither (to generate both from app.root).`,
);
}
- const app = options.app
- ? normalizeUserPath(root, options.app, 'app')
+ const app = options.root
+ ? normalizeUserPath(root, options.root, 'root')
: (probe(root, 'src/App', APP_EXTENSIONS) ?? probe(root, 'src/app', APP_EXTENSIONS));
if (!app) {
throw new Error(
- `[@solidjs/vite-plugin] the \`start\` option needs an app root: add src/App.tsx ` +
- `(or set start.app), or provide src/entry-server.* and src/entry-client.* entries.`,
+ `[@solidjs/vite-plugin] the \`app\` option needs a root component: add src/App.tsx ` +
+ `(or set app.root), or provide src/entry-server.* and src/entry-client.* entries.`,
);
}
const document = options.document
@@ -426,8 +382,8 @@ function resolveEntries(root: string, options: StartOptions, clientMode: boolean
};
}
-export function startServe(
- options: StartOptions,
+export function appServe(
+ options: AppOptions,
internal: {
serverFunctions?: boolean;
serverComponents?: boolean;
@@ -435,7 +391,7 @@ export function startServe(
styleFilter?: DevStyleFilter;
} = {},
): Plugin[] {
- // Client mode (the `start` option without `ssr: true`) rides this exact
+ // Client mode (the `app` option without `ssr: true`) rides this exact
// plugin with three deltas: the generated server entry renders the
// document shell WITHOUT the app (dev serving doubles as history
// fallback, and a post-build hook prerenders it once into
@@ -454,7 +410,7 @@ export function startServe(
// the server-function handler module either way). Everything is gated
// codegen: with the option off, none of these imports exist anywhere.
const serverComponents = !!internal.serverComponents;
- const errorBoundary = options.errorBoundary !== false;
+ const productionErrorBoundary = options.productionErrorBoundary !== false;
const styleFilter = internal.styleFilter;
let devtoolsEnabled = false;
let devtoolsResolutions: Partial<
@@ -505,8 +461,8 @@ export function startServe(
devtoolsIds[consumer] = id;
if (!id && options.devtools === true) {
throw new Error(
- '[@solidjs/vite-plugin] start.devtools requires @solidjs/start-devtools. ' +
- 'Install it as a development dependency or set start.devtools to false.',
+ '[@solidjs/vite-plugin] app.devtools requires @solidjs/start-devtools. ' +
+ 'Install it as a development dependency or set app.devtools to false.',
);
}
return id !== null;
@@ -613,14 +569,14 @@ export function startServe(
}
function errorBoundaryImport(): string[] {
- return isBuild && errorBoundary
+ return isBuild && productionErrorBoundary
? [`import { DefaultErrorBoundary } from ${JSON.stringify(ERROR_BOUNDARY_ID)};`]
: [];
}
function documentTree(root: string, wrapper?: string): string[] {
const content = wrapper ? `<${wrapper}><${root} />${wrapper}>` : `<${root} />`;
- return isBuild && errorBoundary
+ return isBuild && productionErrorBoundary
? [
` `,
` `,
@@ -647,7 +603,7 @@ export function startServe(
``,
`export function render(request, context) {`,
` return renderToStream(() => (`,
- ...(isBuild && errorBoundary
+ ...(isBuild && productionErrorBoundary
? [
` `,
` `,
@@ -678,7 +634,7 @@ export function startServe(
...(setupPath
? [
`if (typeof setup !== 'function') {`,
- ` throw new Error('[@solidjs/vite-plugin] start.setup must default-export a function ' +`,
+ ` throw new Error('[@solidjs/vite-plugin] app.setup must default-export a function ' +`,
` '((event, App) => Component | void | Promise<...>): ' + ${JSON.stringify(options.setup)});`,
`}`,
``,
@@ -743,7 +699,7 @@ export function startServe(
`import App from ${JSON.stringify(app)};`,
``,
`render(() => ${
- isBuild && errorBoundary
+ isBuild && productionErrorBoundary
? ' '
: toolbar
? ' '
@@ -898,7 +854,7 @@ export function startServe(
`const middlewares = Array.isArray(middlewareModule) ? middlewareModule : [middlewareModule];`,
`for (const mw of middlewares) {`,
` if (typeof mw !== 'function') {`,
- ` throw new Error('[@solidjs/vite-plugin] start.middleware must default-export a function or an array of functions: ' + ${JSON.stringify(middlewarePath)});`,
+ ` throw new Error('[@solidjs/vite-plugin] app.middleware must default-export a function or an array of functions: ' + ${JSON.stringify(middlewarePath)});`,
` }`,
`}`,
`const runMiddleware = composeMiddleware(middlewares);`,
@@ -1035,7 +991,7 @@ export function startServe(
` }`,
...(setupPath
? [
- // start.setup's async path boxes the stream (see the generated
+ // app.setup's async path boxes the stream (see the generated
// entry): a bare promise resolution would adopt the stream's
// thenable and buffer the whole render.
` if (result && result.${STREAM_BOX}) result = result.${STREAM_BOX};`,
@@ -1105,7 +1061,7 @@ export function startServe(
middlewarePath = options.middleware
? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware'))
: null;
- // Server-mode only, like `entryServer`/`external` (a documented
+ // Server-mode only, like `entries.server`/`external` (a documented
// no-op in client mode so configs survive the `ssr` boolean flip).
setupPath =
!clientMode && options.setup
@@ -1115,9 +1071,9 @@ export function startServe(
// An authored entry-server owns its render function — the seam the
// hook needs does not exist there.
throw new Error(
- '[@solidjs/vite-plugin] start.setup only applies to generated entries: your ' +
+ '[@solidjs/vite-plugin] app.setup only applies to generated entries: your ' +
'entry-server owns render() already, so call your setup step there instead ' +
- `(remove start.setup or the authored entry): ${options.setup}`,
+ `(remove app.setup or the authored entry): ${options.setup}`,
);
}
if (env.isPreview) {
@@ -1360,7 +1316,7 @@ export function startServe(
// Vite's preview statics serve dist/client (see the config hook) and
// everything else — pages, the server-function endpoint, middleware
// included — dispatches through the built handler, exactly like a
- // deployed server. Hosts owning the server build (`start.external`)
+ // deployed server. Hosts owning the server build (`app.external`)
// preview through their own runner instead.
// Client mode: pages are the static index.html (preview's own
// history fallback serves them before this post middleware runs);
@@ -1427,7 +1383,7 @@ export function startServe(
const pageRequest = req.method === 'GET' && accept.includes('text/html');
// Production dispatches every request through the handler, so
// dev must too or API routes and no-JS form POSTs served by
- // `start.middleware` are unreachable under `vite dev`. Without
+ // `app.middleware` are unreachable under `vite dev`. Without
// a middleware chain, non-page requests have nothing to reach —
// they stay on Vite's pipeline (404s) instead of rendering HTML.
if (!pageRequest && !middlewarePath) return next();
@@ -1472,7 +1428,7 @@ export function startServe(
...(clientMode
? [
{
- name: 'solid:start/prerender',
+ name: 'solid:app/prerender',
apply: 'build',
buildApp: {
// Post order: this hook owns the whole client-mode app build (the
diff --git a/virtual-solid-manifest.d.ts b/virtual-solid-manifest.d.ts
index 51e2c11..81b5be9 100644
--- a/virtual-solid-manifest.d.ts
+++ b/virtual-solid-manifest.d.ts
@@ -1,18 +1,18 @@
-declare module "virtual:solid-manifest" {
- import type { ViteManifest } from "@solidjs/vite-plugin";
+declare module 'virtual:solid-manifest' {
+ import type { ViteManifest } from '@solidjs/vite-plugin';
const manifest: ViteManifest;
export default manifest;
}
// Side-effect module: importing it loads every module containing server
// functions so their registrations exist before requests are dispatched.
-declare module "virtual:solid-server-function-manifest" {}
+declare module 'virtual:solid-server-function-manifest' {}
// Server-only handler (SSR builds). Importing it registers every
// server function (via the manifest above), scopes each request with
// provideRequestEvent, and configures the endpoint; mount
// `handleServerFunctionRequest` on the endpoint in your server.
-declare module "virtual:solid-server-function-handler" {
+declare module 'virtual:solid-server-function-handler' {
/** The resolved endpoint path (plugin `endpoint` option joined with Vite `base`). */
export const endpoint: string;
export function handleServerFunctionRequest(
@@ -31,7 +31,7 @@ declare module "virtual:solid-server-function-handler" {
): Promise;
}
-// Server-only start-mode request handler (the `start` option). It is the SSR
+// Server-only app-mode request handler (the `app` option). It is the SSR
// build's entry, so a production server imports it from the built bundle
// (e.g. `./dist/server/server.js`) rather than by this id; importing the
// id directly also works from custom server code in SSR builds.
@@ -39,7 +39,7 @@ declare module "virtual:solid-server-function-handler" {
// provideRequestEvent, resolves hashed client assets through the build
// manifest, and — when `serverFunctions` is enabled — serves the
// server-function endpoint ahead of SSR.
-declare module "virtual:solid-ssr-handler" {
+declare module 'virtual:solid-ssr-handler' {
export function handleRequest(
request: Request,
options?: {