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

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('<title>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} />` : `<${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?: {