@solidjs/vite-plugin 3.0.0-next.28 → 3.0.0-next.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +56 -1
- package/dist/cjs/index.cjs +123 -14
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +123 -14
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/src/http.d.ts +8 -1
- package/dist/types/src/ssr/index.d.ts +5 -0
- package/package.json +2 -2
- package/virtual-solid-manifest.d.ts +21 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2026 Alexandre Mouton-Brady and Solid contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -222,6 +222,41 @@ The Fetchable wrapper deliberately accepts only the request. Hosts may pass
|
|
|
222
222
|
environment or execution-context arguments after it; those are not the
|
|
223
223
|
Solid options accepted by `handleRequest`'s second parameter.
|
|
224
224
|
|
|
225
|
+
Among those options, **`event`** is the supported public seam for extending
|
|
226
|
+
the request event: its fields spread into the event at creation, so a custom
|
|
227
|
+
server entry (or a host wrapper) can attach whatever its platform knows and
|
|
228
|
+
read it back anywhere in the request scope with `getRequestEvent()`. The
|
|
229
|
+
conventional field name is `nativeEvent` — the platform's raw request
|
|
230
|
+
object. A Node entry passes the `IncomingMessage`:
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
import { createServer } from 'node:http';
|
|
234
|
+
import { handleRequest } from './dist/server/server.js';
|
|
235
|
+
|
|
236
|
+
createServer(async (req, res) => {
|
|
237
|
+
const response = await handleRequest(webRequest(req), {
|
|
238
|
+
event: { nativeEvent: req },
|
|
239
|
+
});
|
|
240
|
+
// ... write response to res
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
// anywhere inside the request scope (middleware, setup, app code)
|
|
246
|
+
import { getRequestEvent } from '@solidjs/web';
|
|
247
|
+
const event = getRequestEvent();
|
|
248
|
+
event.nativeEvent; // the Node IncomingMessage the entry passed
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The plugin's own dev and preview middlewares (and the server-function dev
|
|
252
|
+
middleware) pass `event: { nativeEvent: req }` with the Node request, so
|
|
253
|
+
`getRequestEvent().nativeEvent` answers the same under `vite dev` and
|
|
254
|
+
`vite preview` as behind a Node entry written like the above. For the
|
|
255
|
+
client's IP on bare Node, read `event.nativeEvent.socket.remoteAddress`;
|
|
256
|
+
behind a proxy or load balancer that address is the proxy's, so read the
|
|
257
|
+
forwarding headers off `event.request` instead (`x-forwarded-for` and
|
|
258
|
+
friends) — only when you trust the proxy that set them.
|
|
259
|
+
|
|
225
260
|
- **Preview**: `vite build && vite preview` runs the production artifact
|
|
226
261
|
with no server file — Vite's preview statics serve `dist/client`, and
|
|
227
262
|
everything else (pages, the server-function endpoint, middleware)
|
|
@@ -361,7 +396,14 @@ import { env } from 'virtual:env/client'; // the VITE_-prefixed client vars
|
|
|
361
396
|
is fine). Platform-injected vars that don't exist at build time work,
|
|
362
397
|
secrets rotate without a rebuild, and no secret value exists in any
|
|
363
398
|
dist artifact; an invalid server environment fails boot with the same
|
|
364
|
-
per-key report.
|
|
399
|
+
per-key report. Boot validation is synchronous: the generated server
|
|
400
|
+
env module contains no top-level await, so the server bundle works
|
|
401
|
+
under any downstream build target (Nitro's node-server preset,
|
|
402
|
+
es2020 — no `esnext` override needed). The flip side: `server`
|
|
403
|
+
validators must be synchronous — an async refinement/transform on a
|
|
404
|
+
server key is rejected at config time with the fix in the message
|
|
405
|
+
(`client` keys may stay async; they are awaited at build time where
|
|
406
|
+
the values are baked).
|
|
365
407
|
- **Leaks are errors.** Importing `virtual:env/server` from a client
|
|
366
408
|
module graph is a hard error naming the importer (the app root and
|
|
367
409
|
everything it imports hydrate — they are client code; keep server env
|
|
@@ -434,6 +476,19 @@ Two explicit switches remain for custom host setups:
|
|
|
434
476
|
serving, hands only server-function dispatch in dev to the host. For
|
|
435
477
|
setups without `start`, or when only the endpoint should move.
|
|
436
478
|
|
|
479
|
+
**`virtual:solid-manifest`** exposes the client asset manifest that serving
|
|
480
|
+
works from — a server-side module, available in dev and in SSR builds. In
|
|
481
|
+
an SSR build its default export is the parsed client manifest
|
|
482
|
+
(`dist/client/.vite/manifest.json`), keyed by source path with the resolved
|
|
483
|
+
Vite `base` attached as `_base`; in dev it exports the live asset resolver
|
|
484
|
+
the plugin uses for dev CSS collection instead of a static object. This is
|
|
485
|
+
the seam for frameworks and routers that do their own asset gating —
|
|
486
|
+
deciding which scripts and styles a response carries, as the TanStack Start
|
|
487
|
+
integration does — without re-reading the manifest from disk or re-deriving
|
|
488
|
+
`base`. Ambient types ship with the plugin
|
|
489
|
+
(`/// <reference types="@solidjs/vite-plugin/virtual-solid-manifest" />`);
|
|
490
|
+
see that `.d.ts` and the exported `ViteManifest` type for the full shape.
|
|
491
|
+
|
|
437
492
|
Without `ssr: true` — **client mode** (experimental), the same conventions
|
|
438
493
|
with client-only rendering:
|
|
439
494
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -45,24 +45,54 @@ var babel__namespace = /*#__PURE__*/_interopNamespaceDefault(babel);
|
|
|
45
45
|
* different URL than the one node saw — the dev middlewares use it to
|
|
46
46
|
* restore the configured Vite `base` that the dev/preview base middleware
|
|
47
47
|
* stripped, so the handler always sees production-shaped URLs.
|
|
48
|
+
*
|
|
49
|
+
* Handles plain HTTP/1 *and* the HTTP/2 compat API: Vite's dev server uses
|
|
50
|
+
* `http2.createSecureServer({ allowHTTP1: true })` whenever `server.https`
|
|
51
|
+
* is set without a proxy, so under https the middlewares receive
|
|
52
|
+
* `Http2ServerRequest`s. The h2/protocol/abort techniques here are
|
|
53
|
+
* reimplemented from srvx's Node adapter (github.com/h3js/srvx,
|
|
54
|
+
* src/adapters/_node) — reference, not copied code.
|
|
48
55
|
*/
|
|
49
|
-
function webRequestFromNode(req, urlPath) {
|
|
50
|
-
|
|
56
|
+
function webRequestFromNode(req, urlPath, res) {
|
|
57
|
+
// TLS sockets (https and h2) expose `encrypted`; a Request whose url says
|
|
58
|
+
// http: on a TLS connection breaks secure-cookie logic, absolute
|
|
59
|
+
// redirects, and origin checks in application code.
|
|
60
|
+
const protocol = req.socket?.encrypted ? 'https' : 'http';
|
|
61
|
+
// HTTP/2 has no Host header — the authority travels in the `:authority`
|
|
62
|
+
// pseudo-header instead.
|
|
63
|
+
const host = req.headers.host ?? req.headers[':authority'] ?? 'localhost';
|
|
64
|
+
const url = new URL(urlPath ?? req.url ?? '/', `${protocol}://${host}`);
|
|
51
65
|
const headers = new Headers();
|
|
52
66
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
53
67
|
if (value === undefined) continue;
|
|
68
|
+
// HTTP/2 pseudo-headers (:method, :path, :authority, :scheme) are not
|
|
69
|
+
// legal field names — Headers#append throws a TypeError on them.
|
|
70
|
+
if (key[0] === ':') continue;
|
|
54
71
|
if (Array.isArray(value)) {
|
|
55
72
|
for (const item of value) headers.append(key, item);
|
|
56
73
|
} else {
|
|
57
74
|
headers.append(key, value);
|
|
58
75
|
}
|
|
59
76
|
}
|
|
77
|
+
// Surface client disconnects as the request's AbortSignal so handlers can
|
|
78
|
+
// cancel work (streamed SSR renders, in-flight fetches). The response's
|
|
79
|
+
// 'close' fires on normal completion too; `writableEnded` distinguishes a
|
|
80
|
+
// finished response from a client that went away.
|
|
81
|
+
let signal;
|
|
82
|
+
if (res) {
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
res.once('close', () => {
|
|
85
|
+
if (!res.writableEnded) controller.abort();
|
|
86
|
+
});
|
|
87
|
+
signal = controller.signal;
|
|
88
|
+
}
|
|
60
89
|
const method = req.method || 'GET';
|
|
61
90
|
const body = method === 'GET' || method === 'HEAD' ? undefined : node_stream.Readable.toWeb(req);
|
|
62
91
|
return new Request(url, {
|
|
63
92
|
method,
|
|
64
93
|
headers,
|
|
65
94
|
body,
|
|
95
|
+
signal,
|
|
66
96
|
// undici requires half-duplex for streamed request bodies.
|
|
67
97
|
...(body ? {
|
|
68
98
|
duplex: 'half'
|
|
@@ -77,7 +107,11 @@ async function sendWebResponse(res, response) {
|
|
|
77
107
|
if (key !== 'set-cookie') res.setHeader(key, value);
|
|
78
108
|
});
|
|
79
109
|
if (cookies && cookies.length) res.setHeader('set-cookie', cookies);
|
|
80
|
-
|
|
110
|
+
// HEAD gets the head only — and the body must be *cancelled*, not pumped:
|
|
111
|
+
// node discards HEAD body writes, so streaming a long (or endless) body
|
|
112
|
+
// into the void just burns the render. (Technique from srvx.)
|
|
113
|
+
if (!response.body || res.req?.method === 'HEAD') {
|
|
114
|
+
response.body?.cancel().catch(() => {});
|
|
81
115
|
res.end();
|
|
82
116
|
return;
|
|
83
117
|
}
|
|
@@ -934,7 +968,15 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
934
968
|
// the same value (config merges per key), but the handler graph cannot
|
|
935
969
|
// depend on that: in dev, a mutation from an already-open page can be
|
|
936
970
|
// the first request after a server restart.
|
|
937
|
-
`configureServerFunctionsServer({ provideEvent: provideRequestEvent, endpoint: ${JSON.stringify(resolvedEndpoint)}${components ? ', transformResult: frameTransformResult, transformFlightResult: frameTransformFlightResult, transformDirectResult: frameTransformDirectResult' : ''} });`, `export const endpoint = ${JSON.stringify(resolvedEndpoint)};`,
|
|
971
|
+
`configureServerFunctionsServer({ provideEvent: provideRequestEvent, endpoint: ${JSON.stringify(resolvedEndpoint)}${components ? ', transformResult: frameTransformResult, transformFlightResult: frameTransformFlightResult, transformDirectResult: frameTransformDirectResult' : ''} });`, `export const endpoint = ${JSON.stringify(resolvedEndpoint)};`,
|
|
972
|
+
// `options.event` is the same wrapper->event extension seam the SSR
|
|
973
|
+
// handler's handleRequest carries (conventionally `nativeEvent`, the
|
|
974
|
+
// platform's raw request object). The runtime's standalone handler
|
|
975
|
+
// creates its own event (`{ request, locals }`) with no init
|
|
976
|
+
// parameter, so the extension threads through its existing
|
|
977
|
+
// `createEvent` option instead — spread before `...options` so an
|
|
978
|
+
// explicit host-provided createEvent still wins.
|
|
979
|
+
`export function handleServerFunctionRequest(request, options) {`, ` const { event: eventInit, ...rest } = options || {};`, ` return handle(request, {`, ` provideEvent: provideRequestEvent,`, ` ...(eventInit ? { createEvent: (req) => ({ request: req, locals: {}, ...eventInit }) } : {}),`, ` ...rest,`, ` });`, `}`].join('\n');
|
|
938
980
|
}
|
|
939
981
|
|
|
940
982
|
// Function IDs are `xxHash32(root-relative path)-<count>` (see compile.ts),
|
|
@@ -1018,7 +1060,15 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1018
1060
|
// middleware chain and one stub-backed request event front the
|
|
1019
1061
|
// endpoint exactly as they front page SSR.
|
|
1020
1062
|
const handler = await server.ssrLoadModule(internal.ssrHandler ?? HANDLER_ID$1);
|
|
1021
|
-
|
|
1063
|
+
// Both dispatch shapes carry the raw Node request on the event
|
|
1064
|
+
// (the `options.event` seam), matching the SSR dev middleware
|
|
1065
|
+
// and what a production Node entry passes.
|
|
1066
|
+
const dispatchOptions = {
|
|
1067
|
+
event: {
|
|
1068
|
+
nativeEvent: req
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
const response = internal.ssrHandler ? await handler.handleRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions) : await handler.handleServerFunctionRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions);
|
|
1022
1072
|
await sendWebResponse(res, response);
|
|
1023
1073
|
})().catch(error => {
|
|
1024
1074
|
if (error instanceof Error) server.ssrFixStacktrace(error);
|
|
@@ -1590,7 +1640,14 @@ function startServe(options, internal = {}) {
|
|
|
1590
1640
|
// The runtime's response-head lifecycle: commit at shell flush,
|
|
1591
1641
|
// pre-flush Location as a real redirect, post-flush Location as the
|
|
1592
1642
|
// script fallback; the transform injects the doctype/head pieces.
|
|
1593
|
-
` return createSSRResponse(result, event, {`, ` responseInit: options.responseInit,`, ` nonce: options.nonce,`, ` transformChunk: createHtmlChunkTransform(clientEntry, options.devHead),`, ` });`, `}`, ``, `export async function handleRequest(request, options = {}) {`,
|
|
1643
|
+
` return createSSRResponse(result, event, {`, ` responseInit: options.responseInit,`, ` nonce: options.nonce,`, ` transformChunk: createHtmlChunkTransform(clientEntry, options.devHead),`, ` });`, `}`, ``, `export async function handleRequest(request, options = {}) {`,
|
|
1644
|
+
// `options.event` is the public wrapper->event extension seam: extra
|
|
1645
|
+
// fields (conventionally `nativeEvent`, the platform's raw request
|
|
1646
|
+
// object) spread over the event's defaults at creation, so hosts and
|
|
1647
|
+
// custom server entries can extend what getRequestEvent() answers
|
|
1648
|
+
// with — no new convention beyond createRequestEvent's own init
|
|
1649
|
+
// parameter (spreading undefined is a no-op).
|
|
1650
|
+
` const event = createRequestEvent(request, options.event);`,
|
|
1594
1651
|
// Middleware runs inside the request scope, after event creation —
|
|
1595
1652
|
// getRequestEvent() answers in middleware exactly as in app code, and
|
|
1596
1653
|
// nothing reaches the wire until the outermost middleware returns.
|
|
@@ -1805,7 +1862,14 @@ function startServe(options, internal = {}) {
|
|
|
1805
1862
|
// server-function endpoint) and hands the URL to application
|
|
1806
1863
|
// code, so restore the base — the deployed production handler
|
|
1807
1864
|
// receives base-prefixed URLs and preview must match it.
|
|
1808
|
-
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/'))
|
|
1865
|
+
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/'), res),
|
|
1866
|
+
// Same event extension the dev middleware and a production
|
|
1867
|
+
// Node entry pass: the raw Node request as `nativeEvent`.
|
|
1868
|
+
{
|
|
1869
|
+
event: {
|
|
1870
|
+
nativeEvent: req
|
|
1871
|
+
}
|
|
1872
|
+
});
|
|
1809
1873
|
// Preview's compression middleware buffers whole responses;
|
|
1810
1874
|
// opting HTML out keeps SSR streaming observable, matching
|
|
1811
1875
|
// production behavior.
|
|
@@ -1855,9 +1919,16 @@ function startServe(options, internal = {}) {
|
|
|
1855
1919
|
// the configured `base` from req.url; restore it so the app
|
|
1856
1920
|
// sees the same URLs in dev as in production (where the
|
|
1857
1921
|
// deployed handler receives base-prefixed requests).
|
|
1858
|
-
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/')), {
|
|
1922
|
+
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/'), res), {
|
|
1859
1923
|
devHead,
|
|
1860
|
-
pageRequest
|
|
1924
|
+
pageRequest,
|
|
1925
|
+
// The raw Node request on the event, matching what a
|
|
1926
|
+
// production Node server entry passes through the
|
|
1927
|
+
// `options.event` seam — getRequestEvent().nativeEvent
|
|
1928
|
+
// answers the same in dev as deployed.
|
|
1929
|
+
event: {
|
|
1930
|
+
nativeEvent: req
|
|
1931
|
+
}
|
|
1861
1932
|
});
|
|
1862
1933
|
// A non-page request the chain never handled: the terminal
|
|
1863
1934
|
// dispatch answered with the marked 404 — hand it back to
|
|
@@ -1938,6 +2009,11 @@ function startServe(options, internal = {}) {
|
|
|
1938
2009
|
// time work, secrets rotate without a rebuild, and no secret value exists
|
|
1939
2010
|
// in any dist artifact. Build-time server-value failures downgrade to a
|
|
1940
2011
|
// warning (boot enforces); dev failures stay hard errors — dev IS runtime.
|
|
2012
|
+
// Boot validation is synchronous by design: the generated server module
|
|
2013
|
+
// contains no top-level await (a TLA chunk forces esnext on downstream
|
|
2014
|
+
// bundlers — Nitro's node-server preset rejects it), which is why async
|
|
2015
|
+
// validators are rejected for `server` keys (client keys may stay async;
|
|
2016
|
+
// they are awaited at build time where the values are baked).
|
|
1941
2017
|
//
|
|
1942
2018
|
// A failed validation fails the build; in dev it renders Vite's error
|
|
1943
2019
|
// overlay with the per-key report (the virtual modules throw it on load)
|
|
@@ -2208,7 +2284,23 @@ function startEnv(option) {
|
|
|
2208
2284
|
for (const side of ['server', 'client']) {
|
|
2209
2285
|
for (const [key, validator] of Object.entries(schema[side] ?? {})) {
|
|
2210
2286
|
let result = validator['~standard'].validate(raw[key]);
|
|
2211
|
-
if (result instanceof Promise)
|
|
2287
|
+
if (result instanceof Promise) {
|
|
2288
|
+
// Async validation is fine for `client` keys — their values are
|
|
2289
|
+
// baked right here at build time, where awaiting costs nothing.
|
|
2290
|
+
// `server` keys validate process.env at boot through generated
|
|
2291
|
+
// code that is deliberately synchronous (a top-level await in the
|
|
2292
|
+
// server env chunk forces esnext on every downstream bundle
|
|
2293
|
+
// target — Nitro's node-server preset rejects it outright), so a
|
|
2294
|
+
// Promise-returning server validator could only ever fail at
|
|
2295
|
+
// deploy boot. Async-ness is a property of the schema, not the
|
|
2296
|
+
// value, so fail fast here with the fix in the message. Boot
|
|
2297
|
+
// still backstops (schemas whose sync prefix short-circuits at
|
|
2298
|
+
// build time can go async on real values).
|
|
2299
|
+
if (side === 'server') {
|
|
2300
|
+
throw new Error(`[@solidjs/vite-plugin] server env var "${key}" in ${envFile} uses an async ` + `validator (validate() returned a Promise). Server env is validated ` + `synchronously at boot — the generated module contains no top-level ` + `await, so server bundles work on non-esnext targets — which async ` + `validators cannot do. Make the validator synchronous (drop async ` + `refinements/transforms), or run the async check in application code.`);
|
|
2301
|
+
}
|
|
2302
|
+
result = await result;
|
|
2303
|
+
}
|
|
2212
2304
|
if (result.issues && result.issues.length) {
|
|
2213
2305
|
for (const issue of result.issues) {
|
|
2214
2306
|
const at = (issue.path ?? []).map(segment => typeof segment === 'object' && segment !== null && 'key' in segment ? String(segment.key) : String(segment)).join('.');
|
|
@@ -2285,6 +2377,16 @@ function startEnv(option) {
|
|
|
2285
2377
|
// means. Platform-injected vars that don't exist at build time work,
|
|
2286
2378
|
// secrets rotate without a rebuild, and no secret value exists in any
|
|
2287
2379
|
// dist artifact.
|
|
2380
|
+
//
|
|
2381
|
+
// The generated code is deliberately free of top-level await. Module
|
|
2382
|
+
// init is the only point where "validated and frozen before any
|
|
2383
|
+
// importer's body runs" can be guaranteed — user server modules read
|
|
2384
|
+
// `env.KEY` at their own top level — and the only async thing here is
|
|
2385
|
+
// Standard Schema's option to return a Promise from validate(). A TLA
|
|
2386
|
+
// chunk breaks every downstream bundler with a non-esnext target
|
|
2387
|
+
// (Nitro's node-server preset in practice), so validation runs
|
|
2388
|
+
// synchronously and a Promise-returning server validator is itself a
|
|
2389
|
+
// boot issue (build/config time rejects it earlier when detectable).
|
|
2288
2390
|
function serverEnvModuleCode(loaded) {
|
|
2289
2391
|
const serverKeys = Object.keys(loaded.schema.server ?? {});
|
|
2290
2392
|
const baked = `const __env = ${JSON.stringify(loaded.client)};`;
|
|
@@ -2295,7 +2397,7 @@ function startEnv(option) {
|
|
|
2295
2397
|
};
|
|
2296
2398
|
}
|
|
2297
2399
|
return {
|
|
2298
|
-
code: [`// Generated by @solidjs/vite-plugin (start.env) — server env.`, `// Server values are read from process.env and validated at boot;`, `// client (public) values are baked at build time.`, `import __schema from ${JSON.stringify(envFileAbs)};`, baked, `const __issues = [];`, `for (const __key of ${JSON.stringify(serverKeys)}) {`, `
|
|
2400
|
+
code: [`// Generated by @solidjs/vite-plugin (start.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`, `// on every downstream bundle target (Nitro's node-server preset and`, `// anything else below esnext rejects a TLA chunk outright).`, `import __schema from ${JSON.stringify(envFileAbs)};`, baked, `const __issues = [];`, `for (const __key of ${JSON.stringify(serverKeys)}) {`, ` const __result = __schema.server[__key]['~standard'].validate(process.env[__key]);`, ` if (__result && typeof __result.then === 'function') {`, ` __issues.push(' \\u2717 ' + __key + ': validator returned a Promise \\u2014 async validators are not supported for server keys (boot validation is synchronous so the server bundle carries no top-level await); make this validator synchronous');`, ` } else if (__result.issues && __result.issues.length) {`, ` for (const __issue of __result.issues) __issues.push(' \\u2717 ' + __key + ': ' + __issue.message);`, ` } else {`, ` __env[__key] = __result.value;`, ` }`, `}`, `if (__issues.length) {`, ` throw new Error(`, ` '[@solidjs/vite-plugin] server env validation failed at boot (' + __issues.length +`, ` ' issue' + (__issues.length === 1 ? '' : 's') + ') \\u2014 schema: ' + ${JSON.stringify(envFile)} +`, ` '\\n\\n' + __issues.join('\\n') +`, ` '\\n\\nServer env is read from process.env at boot, not baked at build time: set the ' +`, ` 'variables in the server process environment.'`, ` );`, `}`, `export const env = Object.freeze(__env);`, `export default env;`].join('\n'),
|
|
2299
2401
|
moduleType: 'js'
|
|
2300
2402
|
};
|
|
2301
2403
|
}
|
|
@@ -2971,12 +3073,19 @@ function solidPlugin(options = {}) {
|
|
|
2971
3073
|
exclude: solidPkgsConfig.optimizeDeps.exclude,
|
|
2972
3074
|
// Vite 8+ uses Rolldown for dependency scanning. Rolldown defaults to
|
|
2973
3075
|
// React's automatic JSX runtime for .tsx files, injecting a
|
|
2974
|
-
// react/jsx-dev-runtime import
|
|
2975
|
-
//
|
|
3076
|
+
// react/jsx-dev-runtime import that fails to resolve and aborts the
|
|
3077
|
+
// scan. 'preserve' is no fix: the scanner re-parses the transformed
|
|
3078
|
+
// output as plain JS, so any preserved JSX is a hard parse error
|
|
3079
|
+
// (issue #262). The classic runtime is the only scan-safe lowering:
|
|
3080
|
+
// it emits bare `React.createElement` calls without injecting any
|
|
3081
|
+
// import, and the scan output is never executed — it only exists so
|
|
3082
|
+
// rolldown can walk the import graph.
|
|
2976
3083
|
...(isVite8 ? {
|
|
2977
3084
|
rolldownOptions: {
|
|
2978
3085
|
transform: {
|
|
2979
|
-
jsx:
|
|
3086
|
+
jsx: {
|
|
3087
|
+
runtime: 'classic'
|
|
3088
|
+
}
|
|
2980
3089
|
}
|
|
2981
3090
|
}
|
|
2982
3091
|
} : {})
|