@solidjs/vite-plugin 3.0.0-next.31 → 3.0.0-next.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -8
- package/dist/cjs/index.cjs +131 -133
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +130 -132
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/src/devtools/index.d.ts +0 -2
- package/dist/types/src/environment.d.ts +2 -1
- package/package.json +10 -7
package/README.md
CHANGED
|
@@ -27,13 +27,13 @@ Join [solid discord](https://discord.com/invite/solidjs) and check the [troubles
|
|
|
27
27
|
|
|
28
28
|
## Requirements
|
|
29
29
|
|
|
30
|
-
This module is 100% ESM compatible and requires
|
|
30
|
+
This module is 100% ESM compatible and requires Node.js `^20.19.0 || >=22.12.0`.
|
|
31
31
|
|
|
32
|
-
You can check your current version
|
|
32
|
+
You can check your current Node.js version by running `node -v`. Use a version
|
|
33
|
+
manager such as [Volta](https://volta.sh/) or [nvm](https://github.com/nvm-sh/nvm)
|
|
34
|
+
to update it.
|
|
33
35
|
|
|
34
|
-
Supported Vite versions: **Vite
|
|
35
|
-
dropped; if you are on an older Vite, stay on an earlier release of this
|
|
36
|
-
plugin (2.x) or upgrade Vite.
|
|
36
|
+
Supported Vite versions: **Vite 8 and 9**.
|
|
37
37
|
|
|
38
38
|
## Quickstart
|
|
39
39
|
|
|
@@ -190,8 +190,26 @@ pnpm add -D @solidjs/start-devtools@next
|
|
|
190
190
|
```
|
|
191
191
|
|
|
192
192
|
Start mode detects the package automatically. Set `start: { devtools: true }`
|
|
193
|
-
to require it or `start: { devtools: false }` to disable
|
|
194
|
-
optional peer and the toolbar is not included in production
|
|
193
|
+
to require it or `start: { devtools: false }` to disable automatic integration.
|
|
194
|
+
The package is an optional peer and the toolbar is not included in production
|
|
195
|
+
builds.
|
|
196
|
+
|
|
197
|
+
Generated entries wrap the app automatically. With custom server and client
|
|
198
|
+
entries, place the development boundary around the app in the shared document
|
|
199
|
+
or root:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import { DevToolbar } from "@solidjs/start-devtools";
|
|
203
|
+
|
|
204
|
+
<body>
|
|
205
|
+
<DevToolbar>
|
|
206
|
+
<App />
|
|
207
|
+
</DevToolbar>
|
|
208
|
+
</body>;
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The package becomes a children-only passthrough in production, so no toolbar
|
|
212
|
+
code is included in either production bundle.
|
|
195
213
|
|
|
196
214
|
```tsx
|
|
197
215
|
// src/App.tsx — the entire app: a plain content component
|
|
@@ -212,7 +230,7 @@ With `ssr: true` — **SSR start mode**:
|
|
|
212
230
|
classic `vite build` + `vite build --ssr` two-step, work too.)
|
|
213
231
|
- **Build ordering**: server builds read the client manifest, so with `ssr`
|
|
214
232
|
enabled the plugin also orders builder-mode (environments API) app builds
|
|
215
|
-
client-first via a `buildApp` hook
|
|
233
|
+
client-first via a `buildApp` hook. That covers composed
|
|
216
234
|
setups whose own orchestrator builds server environments before the
|
|
217
235
|
client — e.g. @cloudflare/vite-plugin — with no hand-written ordering
|
|
218
236
|
plugin; setups without another orchestrator keep Vite's stock
|
|
@@ -636,6 +654,19 @@ production handler alike), and edits to the module hot-invalidate the
|
|
|
636
654
|
handler in dev. Config calls merge per key, so it composes with the
|
|
637
655
|
plugin's own runtime configuration.
|
|
638
656
|
|
|
657
|
+
Server-function requests are same-origin protected by `@solidjs/web`. To
|
|
658
|
+
allow another trusted origin, configure it in the same server-only module:
|
|
659
|
+
|
|
660
|
+
```ts
|
|
661
|
+
import { configureServerFunctionsServer } from '@solidjs/web/server-functions/server';
|
|
662
|
+
|
|
663
|
+
configureServerFunctionsServer({
|
|
664
|
+
csrf: { origin: ['https://app.example.com'] },
|
|
665
|
+
});
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
Set `csrf: false` only when another trusted layer protects the endpoint.
|
|
669
|
+
|
|
639
670
|
Meta-frameworks that need to control plugin ordering and dispatch requests
|
|
640
671
|
through their own server should use the standalone `serverFunctions()`
|
|
641
672
|
export instead, which never installs the dev middleware. See
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -528,18 +528,18 @@ function boundaryModules() {
|
|
|
528
528
|
// import graph — no directive transforms have run, so it walks
|
|
529
529
|
// straight through 'use server' modules into genuinely server-only
|
|
530
530
|
// code. That graph is legal once transforms split it, so the guard
|
|
531
|
-
// must not fire on the scan pass (`options.scan`,
|
|
532
|
-
// scanner
|
|
533
|
-
// scanner in v8). Still claim the specifier: resolving to the empty
|
|
531
|
+
// must not fire on the scan pass (`options.scan`, set by Rolldown's
|
|
532
|
+
// dependency scanner). Still claim the specifier: resolving to the empty
|
|
534
533
|
// virtual module keeps the scanner from chasing `server-only` /
|
|
535
534
|
// `client-only` as missing bare dependencies, which would abort the
|
|
536
535
|
// scan all the same. Real dev/build module graphs resolve without
|
|
537
536
|
// the flag and stay fully guarded.
|
|
538
537
|
const scan = !!options?.scan;
|
|
538
|
+
const server = this.environment.config.consumer === 'server';
|
|
539
539
|
if (id === 'server-only') {
|
|
540
|
-
if (!
|
|
540
|
+
if (!server && !scan) this.error(`[@solidjs/vite-plugin] Attempt to import 'server-only' in a client module: ${importer}. ` + `Code that uses this module must run only on the server — make sure it is only ` + `imported by server code (e.g. a server entry, a "use server" module, or code ` + `reached exclusively from them).`);
|
|
541
541
|
} else if (id === 'client-only') {
|
|
542
|
-
if (
|
|
542
|
+
if (server && !scan) this.error(`[@solidjs/vite-plugin] Attempt to import 'client-only' in a server module: ${importer}. ` + `Code that uses this module must run only in the browser — make sure it is only ` + `imported by client code (e.g. behind a client-only lazy boundary).`);
|
|
543
543
|
} else {
|
|
544
544
|
return null;
|
|
545
545
|
}
|
|
@@ -842,8 +842,6 @@ function invalidateModule(moduleGraph, path) {
|
|
|
842
842
|
}
|
|
843
843
|
}
|
|
844
844
|
function invalidateModules(server, result, manifest) {
|
|
845
|
-
// `environments` requires Vite 6+; older versions just miss the eager
|
|
846
|
-
// manifest invalidation (the debounced reload still converges).
|
|
847
845
|
if (server?.environments && result.invalidPreload) {
|
|
848
846
|
invalidateModule(server.environments.client.moduleGraph, manifest);
|
|
849
847
|
invalidateModule(server.environments.ssr.moduleGraph, manifest);
|
|
@@ -1036,7 +1034,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1036
1034
|
apply: 'serve',
|
|
1037
1035
|
configureServer(server) {
|
|
1038
1036
|
const ssrEnvironment = server.environments.ssr;
|
|
1039
|
-
if (internal.externalDevServer ||
|
|
1037
|
+
if (internal.externalDevServer || !isRunnableEnvironment(ssrEnvironment)) {
|
|
1040
1038
|
return;
|
|
1041
1039
|
}
|
|
1042
1040
|
server.middlewares.use((req, res, next) => {
|
|
@@ -1059,7 +1057,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1059
1057
|
const functionId = (typeof headerId === 'string' ? headerId.split('#')[0] : undefined) || url.searchParams.get('id');
|
|
1060
1058
|
if (functionId) {
|
|
1061
1059
|
const entry = moduleForFunctionId(functionId);
|
|
1062
|
-
if (entry) await
|
|
1060
|
+
if (entry) await ssrEnvironment.runner.import(moduleDevUrl(entry));
|
|
1063
1061
|
}
|
|
1064
1062
|
// Dispatch through a module evaluated in the SSR environment so
|
|
1065
1063
|
// the handler shares the registry instance with the app modules.
|
|
@@ -1067,7 +1065,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1067
1065
|
// in, and dispatch goes through `handleRequest` instead — one
|
|
1068
1066
|
// middleware chain and one stub-backed request event front the
|
|
1069
1067
|
// endpoint exactly as they front page SSR.
|
|
1070
|
-
const handler = await
|
|
1068
|
+
const handler = await ssrEnvironment.runner.import(internal.ssrHandler ?? HANDLER_ID$1);
|
|
1071
1069
|
// Both dispatch shapes carry the raw Node request on the event
|
|
1072
1070
|
// (the `options.event` seam), matching the SSR dev middleware
|
|
1073
1071
|
// and what a production Node entry passes.
|
|
@@ -1079,7 +1077,6 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1079
1077
|
const response = internal.ssrHandler ? await handler.handleRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions) : await handler.handleServerFunctionRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions);
|
|
1080
1078
|
await sendWebResponse(res, response);
|
|
1081
1079
|
})().catch(error => {
|
|
1082
|
-
if (error instanceof Error) server.ssrFixStacktrace(error);
|
|
1083
1080
|
next(error);
|
|
1084
1081
|
});
|
|
1085
1082
|
});
|
|
@@ -1203,13 +1200,9 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1203
1200
|
}
|
|
1204
1201
|
|
|
1205
1202
|
const DEVTOOLS_PACKAGE = '@solidjs/start-devtools';
|
|
1206
|
-
const DEVTOOLS_ID = 'virtual:solid-devtools';
|
|
1207
1203
|
const DEVTOOLS_MOUNT_ID = 'virtual:solid-devtools/mount';
|
|
1208
|
-
function devtoolsModuleCode() {
|
|
1209
|
-
return [`import * as serverFunctions from '@solidjs/web/server-functions';`, `import { DevToolbar, pushServerFunctionCall } from '${DEVTOOLS_PACKAGE}';`, `const observe = Reflect.get(serverFunctions, 'observeServerFunctionCalls');`, `if (typeof observe === 'function') observe(pushServerFunctionCall);`, `export { DevToolbar };`].join('\n');
|
|
1210
|
-
}
|
|
1211
1204
|
function devtoolsMountModuleCode() {
|
|
1212
|
-
return [`import
|
|
1205
|
+
return [`import { mountDevToolbar } from '${DEVTOOLS_PACKAGE}';`, `mountDevToolbar();`].join('\n');
|
|
1213
1206
|
}
|
|
1214
1207
|
|
|
1215
1208
|
// Start-mode serving for plain Vite apps: `solid({ start: {...} })` (or the
|
|
@@ -1226,7 +1219,7 @@ function devtoolsMountModuleCode() {
|
|
|
1226
1219
|
// Both paths inject the Vite client, dev style patch, and entry CSS as
|
|
1227
1220
|
// `<style data-vite-dev-id>` tags before the body can paint.
|
|
1228
1221
|
// - Prod: the plugin configures a full-app build (client + server bundles
|
|
1229
|
-
// via the Vite
|
|
1222
|
+
// via the Vite environments/builder API — a single `vite build` builds
|
|
1230
1223
|
// both) whose server entry is `virtual:solid-ssr-handler`: an
|
|
1231
1224
|
// adapter-agnostic named `handleRequest(Request) => Promise<Response>` plus
|
|
1232
1225
|
// a default Fetchable `{ fetch(request) }` export. Both scope each request
|
|
@@ -1408,10 +1401,9 @@ function startServe(options, internal = {}) {
|
|
|
1408
1401
|
const serverComponents = !!internal.serverComponents;
|
|
1409
1402
|
const errorBoundary = options.errorBoundary !== false;
|
|
1410
1403
|
const styleFilter = internal.styleFilter;
|
|
1411
|
-
let
|
|
1412
|
-
let
|
|
1413
|
-
|
|
1414
|
-
let devtoolsId = null;
|
|
1404
|
+
let devtoolsEnabled = false;
|
|
1405
|
+
let devtoolsResolutions = {};
|
|
1406
|
+
let devtoolsIds = {};
|
|
1415
1407
|
// `external` is server-mode-only (documented no-op in client mode, so a
|
|
1416
1408
|
// host-integrated config survives the `ssr` boolean flip untouched).
|
|
1417
1409
|
const externalServer = !clientMode && !!options.external;
|
|
@@ -1428,31 +1420,36 @@ function startServe(options, internal = {}) {
|
|
|
1428
1420
|
if (!entries) throw new Error('[@solidjs/vite-plugin] SSR entries not resolved yet');
|
|
1429
1421
|
return entries;
|
|
1430
1422
|
}
|
|
1431
|
-
async function resolveDevtools(resolve, importer) {
|
|
1432
|
-
if (
|
|
1423
|
+
async function resolveDevtools(resolve, importer, consumer) {
|
|
1424
|
+
if (!devtoolsEnabled) return false;
|
|
1433
1425
|
// Detect from the app graph first (the documented install location), then
|
|
1434
1426
|
// from the plugin's own file: in pnpm-isolated apps a copy that is only a
|
|
1435
1427
|
// dependency of the plugin is not reachable from the app's importers. The
|
|
1436
|
-
// resolved id is kept so
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1428
|
+
// resolved id is kept so imports from generated modules can use it.
|
|
1429
|
+
devtoolsResolutions[consumer] ??= (async () => {
|
|
1430
|
+
// Resolving from the plugin's own file never yields null when the
|
|
1431
|
+
// package is absent: it is declared an optional peer dependency, so
|
|
1432
|
+
// Vite answers with its `__vite-optional-peer-dep:` stub (an empty
|
|
1433
|
+
// module). Treat that stub as "not installed".
|
|
1434
|
+
const realId = resolved => resolved && !resolved.id.startsWith('__vite-optional-peer-dep:') ? resolved.id : null;
|
|
1435
|
+
return realId(await resolve(DEVTOOLS_PACKAGE, importer)) ?? realId(await resolve(DEVTOOLS_PACKAGE, node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))));
|
|
1436
|
+
})();
|
|
1437
|
+
const id = await devtoolsResolutions[consumer];
|
|
1438
|
+
devtoolsIds[consumer] = id;
|
|
1439
|
+
if (!id && options.devtools === true) {
|
|
1442
1440
|
throw new Error('[@solidjs/vite-plugin] start.devtools requires @solidjs/start-devtools. ' + 'Install it as a development dependency or set start.devtools to false.');
|
|
1443
1441
|
}
|
|
1444
|
-
return
|
|
1442
|
+
return id !== null;
|
|
1445
1443
|
}
|
|
1446
1444
|
|
|
1447
1445
|
/**
|
|
1448
|
-
* Cheap
|
|
1446
|
+
* Cheap walk-up probe mirroring how the optimizer resolves bare
|
|
1449
1447
|
* `optimizeDeps.include` entries: is @solidjs/start-devtools reachable from
|
|
1450
|
-
*
|
|
1448
|
+
* this directory? Detection proper (resolveDevtools) runs later with a real
|
|
1451
1449
|
* importer; this only decides whether the toolbar graph can be pre-bundled
|
|
1452
|
-
* at scan time
|
|
1453
|
-
* first-request discovery would force a re-optimize + full page reload.
|
|
1450
|
+
* at scan time.
|
|
1454
1451
|
*/
|
|
1455
|
-
function
|
|
1452
|
+
function devtoolsReachableFrom(dir) {
|
|
1456
1453
|
for (let current = dir;;) {
|
|
1457
1454
|
if (fs.existsSync(path.join(current, 'node_modules', DEVTOOLS_PACKAGE, 'package.json'))) {
|
|
1458
1455
|
return true;
|
|
@@ -1463,6 +1460,26 @@ function startServe(options, internal = {}) {
|
|
|
1463
1460
|
}
|
|
1464
1461
|
}
|
|
1465
1462
|
|
|
1463
|
+
/**
|
|
1464
|
+
* The `optimizeDeps.include` spec that pre-bundles the toolbar graph, or
|
|
1465
|
+
* null when it cannot be resolved at all. Pre-bundling it is not just a
|
|
1466
|
+
* warm-start nicety: the toolbar hangs off virtual modules the scanner
|
|
1467
|
+
* never crawls, so without an include the optimizer only discovers it on
|
|
1468
|
+
* first request. That re-optimize can pair chunks from different passes
|
|
1469
|
+
* whose shared minified exports disagree, taking down the whole client
|
|
1470
|
+
* entry graph. The spec must therefore cover every install shape
|
|
1471
|
+
* resolveDevtools accepts: bare when the app installs the package, and
|
|
1472
|
+
* Vite's nested-include form (`plugin > dep`) when it is only a dependency
|
|
1473
|
+
* of this plugin (pnpm-isolated installs).
|
|
1474
|
+
*/
|
|
1475
|
+
function devtoolsIncludeSpec(rootDir) {
|
|
1476
|
+
if (devtoolsReachableFrom(rootDir)) return DEVTOOLS_PACKAGE;
|
|
1477
|
+
if (devtoolsReachableFrom(path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))))) {
|
|
1478
|
+
return `@solidjs/vite-plugin > ${DEVTOOLS_PACKAGE}`;
|
|
1479
|
+
}
|
|
1480
|
+
return null;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1466
1483
|
/** Import specifier for generated code: absolute for files, id for virtuals. */
|
|
1467
1484
|
function entryServerSpec() {
|
|
1468
1485
|
const {
|
|
@@ -1516,7 +1533,7 @@ function startServe(options, internal = {}) {
|
|
|
1516
1533
|
const content = wrapper ? `<${wrapper}><${root} /></${wrapper}>` : `<${root} />`;
|
|
1517
1534
|
return isBuild && errorBoundary ? [` <DefaultErrorBoundary>`, ` <Document>`, ` <DefaultErrorBoundary>`, ` ${content}`, ` </DefaultErrorBoundary>`, ` </Document>`, ` </DefaultErrorBoundary>`] : [` <Document>`, ` ${content}`, ` </Document>`];
|
|
1518
1535
|
}
|
|
1519
|
-
function generatedEntryServerCode() {
|
|
1536
|
+
function generatedEntryServerCode(toolbar) {
|
|
1520
1537
|
if (clientMode) {
|
|
1521
1538
|
// The client-mode shell: the document without the app. Rendered per
|
|
1522
1539
|
// request in dev (any HTML GET gets it — history-fallback semantics)
|
|
@@ -1528,7 +1545,7 @@ function startServe(options, internal = {}) {
|
|
|
1528
1545
|
app
|
|
1529
1546
|
} = requireEntries();
|
|
1530
1547
|
const streamOptions = `{ manifest${serverComponents ? ', plugins: [ServerComponentPlugin]' : ''} }`;
|
|
1531
|
-
return [`import { renderToStream${setupPath ? ', getRequestEvent' : ''} } from '@solidjs/web';`, ...(serverComponents ? [`import { configureServerFunctionsServer } from '@solidjs/web/server-functions';`, `import { frameTransformDirectResult, ServerComponentPlugin } from '@solidjs/web/frames';`] : []), `import manifest from ${JSON.stringify(MANIFEST_ID)};`, `import Document from ${JSON.stringify(documentSpec())};`, `import App from ${JSON.stringify(app)};`, ...errorBoundaryImport(), ...(setupPath ? [`import setup from ${JSON.stringify(setupPath)};`] : []), ``, ...(setupPath ? [`if (typeof setup !== 'function') {`, ` throw new Error('[@solidjs/vite-plugin] start.setup must default-export a function ' +`, ` '((event, App) => Component | void | Promise<...>): ' + ${JSON.stringify(options.setup)});`, `}`, ``] : []), ...(serverComponents ? [
|
|
1548
|
+
return [`import { renderToStream${setupPath ? ', getRequestEvent' : ''} } from '@solidjs/web';`, ...(serverComponents ? [`import { configureServerFunctionsServer } from '@solidjs/web/server-functions';`, `import { frameTransformDirectResult, ServerComponentPlugin } from '@solidjs/web/frames';`] : []), `import manifest from ${JSON.stringify(MANIFEST_ID)};`, `import Document from ${JSON.stringify(documentSpec())};`, `import App from ${JSON.stringify(app)};`, ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_PACKAGE)};`] : []), ...errorBoundaryImport(), ...(setupPath ? [`import setup from ${JSON.stringify(setupPath)};`] : []), ``, ...(setupPath ? [`if (typeof setup !== 'function') {`, ` throw new Error('[@solidjs/vite-plugin] start.setup must default-export a function ' +`, ` '((event, App) => Component | void | Promise<...>): ' + ${JSON.stringify(options.setup)});`, `}`, ``] : []), ...(serverComponents ? [
|
|
1532
1549
|
// Direct (in-process) server-function calls made during document
|
|
1533
1550
|
// SSR must resolve to inline-renderable components; the endpoint
|
|
1534
1551
|
// response transform is installed separately by the
|
|
@@ -1542,7 +1559,7 @@ function startServe(options, internal = {}) {
|
|
|
1542
1559
|
// the *complete* render) and buffers the stream — so it crosses
|
|
1543
1560
|
// boxed under a private key the generated handler unboxes
|
|
1544
1561
|
// (both modules are ours).
|
|
1545
|
-
`export function render(request, context) {`, ` const prepared = setup(getRequestEvent(), App);`, ` if (prepared && typeof prepared.then === 'function') {`, ` return prepared.then((component) => ({ ${STREAM_BOX}: renderApp(component || App) }));`, ` }`, ` return renderApp(prepared || App);`, `}`, ``, `function renderApp(Root) {`, ` return renderToStream(() => (`, ...documentTree('Root'), ` ), ${streamOptions});`, `}`] : [`export function render(request, context) {`, ` return renderToStream(() => (`, ...documentTree('App'), ` ), ${streamOptions});`, `}`])].join('\n');
|
|
1562
|
+
`export function render(request, context) {`, ` const prepared = setup(getRequestEvent(), App);`, ` if (prepared && typeof prepared.then === 'function') {`, ` return prepared.then((component) => ({ ${STREAM_BOX}: renderApp(component || App) }));`, ` }`, ` return renderApp(prepared || App);`, `}`, ``, `function renderApp(Root) {`, ` return renderToStream(() => (`, ...documentTree('Root', toolbar ? 'DevToolbar' : undefined), ` ), ${streamOptions});`, `}`] : [`export function render(request, context) {`, ` return renderToStream(() => (`, ...documentTree('App', toolbar ? 'DevToolbar' : undefined), ` ), ${streamOptions});`, `}`])].join('\n');
|
|
1546
1563
|
}
|
|
1547
1564
|
function generatedEntryClientCode(toolbar) {
|
|
1548
1565
|
const {
|
|
@@ -1554,9 +1571,9 @@ function startServe(options, internal = {}) {
|
|
|
1554
1571
|
// app cannot claim server DOM anyway. The entry script is injected
|
|
1555
1572
|
// without `async` (plain module = deferred), so document.body is
|
|
1556
1573
|
// complete when this runs.
|
|
1557
|
-
return [`import { render } from '@solidjs/web';`, ...errorBoundaryImport(), ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(
|
|
1574
|
+
return [`import { render } from '@solidjs/web';`, ...errorBoundaryImport(), ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_PACKAGE)};`] : []), `import App from ${JSON.stringify(app)};`, ``, `render(() => ${isBuild && errorBoundary ? '<DefaultErrorBoundary><App /></DefaultErrorBoundary>' : toolbar ? '<DevToolbar><App /></DevToolbar>' : '<App />'}, document.body);`].join('\n');
|
|
1558
1575
|
}
|
|
1559
|
-
return [`import { hydrate } from '@solidjs/web';`, ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(
|
|
1576
|
+
return [`import { hydrate } from '@solidjs/web';`, ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_PACKAGE)};`] : []), ...(serverComponents ? [`import { installServerComponents } from '@solidjs/web/frames';`] : []), ...errorBoundaryImport(), `import Document from ${JSON.stringify(documentSpec())};`, `import App from ${JSON.stringify(app)};`, ``, ...(serverComponents ? [
|
|
1560
1577
|
// Installs the t=0 document-adoption registry and the transport
|
|
1561
1578
|
// policy (component responses morph their boundary instead of
|
|
1562
1579
|
// decoding as data). Must run before hydrate().
|
|
@@ -1737,9 +1754,9 @@ function startServe(options, internal = {}) {
|
|
|
1737
1754
|
enforce: 'pre',
|
|
1738
1755
|
config(userConfig, env) {
|
|
1739
1756
|
root = path.resolve(userConfig.root || process.cwd());
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1757
|
+
devtoolsEnabled = env.command === 'serve' && !env.isPreview && options.devtools !== false;
|
|
1758
|
+
devtoolsResolutions = {};
|
|
1759
|
+
devtoolsIds = {};
|
|
1743
1760
|
entries = resolveEntries(root, options, clientMode);
|
|
1744
1761
|
middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
|
|
1745
1762
|
// Server-mode only, like `entryServer`/`external` (a documented
|
|
@@ -1830,7 +1847,7 @@ function startServe(options, internal = {}) {
|
|
|
1830
1847
|
}
|
|
1831
1848
|
},
|
|
1832
1849
|
// Presence of `builder` makes a plain `vite build` build the
|
|
1833
|
-
// whole app (all environments: client then ssr)
|
|
1850
|
+
// whole app (all environments: client then ssr).
|
|
1834
1851
|
// A classic `vite build --ssr` invocation must stay a
|
|
1835
1852
|
// single-environment build, so it doesn't get the flag.
|
|
1836
1853
|
...(env.isSsrBuild ? {} : {
|
|
@@ -1860,23 +1877,32 @@ function startServe(options, internal = {}) {
|
|
|
1860
1877
|
optimizeDeps: {
|
|
1861
1878
|
entries: scanEntries,
|
|
1862
1879
|
// Like the refresh runtime in the main plugin: the toolbar
|
|
1863
|
-
// graph is injected behind
|
|
1864
|
-
//
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1880
|
+
// graph is injected behind modules the scanner never crawls,
|
|
1881
|
+
// so pre-bundle it and the server-functions runtime up front.
|
|
1882
|
+
...(() => {
|
|
1883
|
+
const spec = devtoolsEnabled ? devtoolsIncludeSpec(root) : null;
|
|
1884
|
+
return spec ? {
|
|
1885
|
+
include: [spec, '@solidjs/web/server-functions']
|
|
1886
|
+
} : {};
|
|
1887
|
+
})()
|
|
1870
1888
|
}
|
|
1871
1889
|
})
|
|
1872
1890
|
};
|
|
1873
1891
|
},
|
|
1892
|
+
configEnvironment(name, config) {
|
|
1893
|
+
if (name !== 'ssr') return;
|
|
1894
|
+
config.resolve ??= {};
|
|
1895
|
+
const noExternal = config.resolve.noExternal;
|
|
1896
|
+
if (noExternal !== true) {
|
|
1897
|
+
config.resolve.noExternal = [...(Array.isArray(noExternal) ? noExternal : noExternal ? [noExternal] : []), DEVTOOLS_PACKAGE];
|
|
1898
|
+
}
|
|
1899
|
+
},
|
|
1874
1900
|
configResolved(config) {
|
|
1875
1901
|
root = config.root;
|
|
1876
1902
|
base = config.base;
|
|
1877
1903
|
isBuild = config.command === 'build';
|
|
1878
1904
|
},
|
|
1879
|
-
resolveId(source, importer) {
|
|
1905
|
+
resolveId(source, importer, opts) {
|
|
1880
1906
|
if (source === HANDLER_ID) {
|
|
1881
1907
|
return {
|
|
1882
1908
|
id: HANDLER_ID,
|
|
@@ -1895,18 +1921,16 @@ function startServe(options, internal = {}) {
|
|
|
1895
1921
|
moduleSideEffects: source === ENTRY_CLIENT_ID
|
|
1896
1922
|
};
|
|
1897
1923
|
}
|
|
1898
|
-
if (
|
|
1924
|
+
if (devtoolsEnabled && source === DEVTOOLS_MOUNT_ID) {
|
|
1899
1925
|
return {
|
|
1900
1926
|
id: source,
|
|
1901
1927
|
moduleSideEffects: true
|
|
1902
1928
|
};
|
|
1903
1929
|
}
|
|
1904
|
-
//
|
|
1905
|
-
//
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
// Delegate to the resolution captured at detection time instead.
|
|
1909
|
-
if (devtoolsId && source === DEVTOOLS_PACKAGE && (importer === DEVTOOLS_ID || importer === DEVTOOLS_MOUNT_ID)) {
|
|
1930
|
+
// Generated modules have no directory for bare-package resolution.
|
|
1931
|
+
// Reuse the app-relative id captured during detection.
|
|
1932
|
+
const devtoolsId = devtoolsIds[getEnvironmentConsumer(this.environment, opts)];
|
|
1933
|
+
if (devtoolsId && source === DEVTOOLS_PACKAGE && (importer === ENTRY_SERVER_ID || importer === ENTRY_CLIENT_ID || importer === DEVTOOLS_MOUNT_ID)) {
|
|
1910
1934
|
return {
|
|
1911
1935
|
id: devtoolsId
|
|
1912
1936
|
};
|
|
@@ -1928,37 +1952,40 @@ function startServe(options, internal = {}) {
|
|
|
1928
1952
|
}
|
|
1929
1953
|
return devStylesModuleCode(this.environment, file => this.addWatchFile(file));
|
|
1930
1954
|
}
|
|
1931
|
-
if (id === ENTRY_SERVER_ID)
|
|
1955
|
+
if (id === ENTRY_SERVER_ID) {
|
|
1956
|
+
const toolbar = clientMode ? false : await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1957
|
+
skipSelf: true
|
|
1958
|
+
}), requireEntries().app, 'server');
|
|
1959
|
+
return generatedEntryServerCode(toolbar);
|
|
1960
|
+
}
|
|
1932
1961
|
if (id === ENTRY_CLIENT_ID) {
|
|
1933
1962
|
const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1934
1963
|
skipSelf: true
|
|
1935
|
-
}), requireEntries().app);
|
|
1964
|
+
}), requireEntries().app, 'client');
|
|
1936
1965
|
return generatedEntryClientCode(toolbar);
|
|
1937
1966
|
}
|
|
1938
1967
|
if (id === DOCUMENT_ID) return documentShellCode;
|
|
1939
1968
|
if (id === ERROR_BOUNDARY_ID) return errorBoundaryCode;
|
|
1940
|
-
if (id ===
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
// first-touch order doesn't matter.
|
|
1944
|
-
if (!isBuild && consumer === 'client' && devtools === undefined) {
|
|
1969
|
+
if (id === DEVTOOLS_MOUNT_ID) {
|
|
1970
|
+
let enabled = false;
|
|
1971
|
+
if (devtoolsEnabled && consumer === 'client') {
|
|
1945
1972
|
const {
|
|
1946
1973
|
app,
|
|
1947
1974
|
entryClient
|
|
1948
1975
|
} = requireEntries();
|
|
1949
|
-
await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1976
|
+
enabled = await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1950
1977
|
skipSelf: true
|
|
1951
|
-
}), app ?? path.resolve(root, entryClient));
|
|
1978
|
+
}), app ?? path.resolve(root, entryClient), 'client');
|
|
1952
1979
|
}
|
|
1953
|
-
if (
|
|
1980
|
+
if (!enabled) {
|
|
1954
1981
|
this.error(`${id} is only available to the development client.`);
|
|
1955
1982
|
}
|
|
1956
|
-
return
|
|
1983
|
+
return devtoolsMountModuleCode();
|
|
1957
1984
|
}
|
|
1958
1985
|
return null;
|
|
1959
1986
|
},
|
|
1960
1987
|
async transform(code, id, opts) {
|
|
1961
|
-
if (isBuild ||
|
|
1988
|
+
if (isBuild || !devtoolsEnabled) return null;
|
|
1962
1989
|
const current = requireEntries();
|
|
1963
1990
|
if (current.generated || getEnvironmentConsumer(this.environment, opts) !== 'client') {
|
|
1964
1991
|
return null;
|
|
@@ -1970,7 +1997,7 @@ function startServe(options, internal = {}) {
|
|
|
1970
1997
|
}
|
|
1971
1998
|
const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1972
1999
|
skipSelf: true
|
|
1973
|
-
}), id);
|
|
2000
|
+
}), id, 'client');
|
|
1974
2001
|
if (!toolbar) return null;
|
|
1975
2002
|
return {
|
|
1976
2003
|
code: `import ${JSON.stringify(DEVTOOLS_MOUNT_ID)};\n${code}`,
|
|
@@ -2034,7 +2061,7 @@ function startServe(options, internal = {}) {
|
|
|
2034
2061
|
// that gets the streamed SSR render.
|
|
2035
2062
|
return () => {
|
|
2036
2063
|
const ssrEnvironment = server.environments.ssr;
|
|
2037
|
-
if (externalServer ||
|
|
2064
|
+
if (externalServer || !isRunnableEnvironment(ssrEnvironment)) {
|
|
2038
2065
|
return;
|
|
2039
2066
|
}
|
|
2040
2067
|
server.middlewares.use((req, res, next) => {
|
|
@@ -2051,7 +2078,7 @@ function startServe(options, internal = {}) {
|
|
|
2051
2078
|
(async () => {
|
|
2052
2079
|
// Loaded through the SSR environment so the app, the request
|
|
2053
2080
|
// event storage, and the handler share one module registry.
|
|
2054
|
-
const handler = await
|
|
2081
|
+
const handler = await ssrEnvironment.runner.import(HANDLER_ID);
|
|
2055
2082
|
const styles = pageRequest ? await collectDevStyles(server, styleRoots(), styleFilter) : [];
|
|
2056
2083
|
const devHead = styles.map(renderDevStyleTag).join('');
|
|
2057
2084
|
// Post middlewares run after Vite's base middleware stripped
|
|
@@ -2076,7 +2103,6 @@ function startServe(options, internal = {}) {
|
|
|
2076
2103
|
if (response.headers.has(DEV_FALLTHROUGH_HEADER)) return next();
|
|
2077
2104
|
await sendWebResponse(res, response);
|
|
2078
2105
|
})().catch(error => {
|
|
2079
|
-
if (error instanceof Error) server.ssrFixStacktrace(error);
|
|
2080
2106
|
// Vite's error middleware renders the overlay-enabled 500 page.
|
|
2081
2107
|
next(error);
|
|
2082
2108
|
});
|
|
@@ -2166,7 +2192,7 @@ function startServe(options, internal = {}) {
|
|
|
2166
2192
|
// https://github.com/pyyupsk/vite-env), the design-correct prior art. The
|
|
2167
2193
|
// implementation is fresh against this plugin's machinery: Standard Schema
|
|
2168
2194
|
// is the only contract (no zod dependency or zod-specific paths), the
|
|
2169
|
-
// schema file loads through Vite's own `runnerImport
|
|
2195
|
+
// schema file loads through Vite's own `runnerImport`
|
|
2170
2196
|
// (no jiti), server-graph protection keys off the environment *consumer*
|
|
2171
2197
|
// rather than environment-name lists, and the types are inferred from the
|
|
2172
2198
|
// user's schema instead of introspected per-library.
|
|
@@ -2225,37 +2251,18 @@ function formatValidationError(issues, envFile, mode) {
|
|
|
2225
2251
|
return `[@solidjs/vite-plugin] env validation failed (${issues.length} issue${issues.length === 1 ? '' : 's'}) — schema: ${envFile}, mode: ${mode}\n\n` + lines.join('\n') + `\n\nSet the variables in your environment or .env files, or adjust the schema.`;
|
|
2226
2252
|
}
|
|
2227
2253
|
|
|
2228
|
-
/**
|
|
2229
|
-
* Loads the schema module at config time through Vite itself: `runnerImport`
|
|
2230
|
-
* (Vite 6.1+) evaluates TypeScript in-process with project resolution;
|
|
2231
|
-
* older Vite 6 falls back to `loadConfigFromFile`, the exact machinery that
|
|
2232
|
-
* loads vite.config.ts.
|
|
2233
|
-
*/
|
|
2254
|
+
/** Loads the schema module through Vite with project resolution. */
|
|
2234
2255
|
async function importSchemaModule(envFileAbs, root, mode) {
|
|
2235
|
-
const
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
} = await vite.runnerImport(envFileAbs, {
|
|
2241
|
-
root,
|
|
2242
|
-
mode
|
|
2243
|
-
});
|
|
2244
|
-
return {
|
|
2245
|
-
exported: module?.default,
|
|
2246
|
-
dependencies: (dependencies || []).map(dep => path.resolve(root, dep)).filter(dep => fs.existsSync(dep))
|
|
2247
|
-
};
|
|
2248
|
-
}
|
|
2249
|
-
const result = await vite.loadConfigFromFile({
|
|
2250
|
-
command: 'serve',
|
|
2256
|
+
const {
|
|
2257
|
+
module,
|
|
2258
|
+
dependencies
|
|
2259
|
+
} = await vite.runnerImport(envFileAbs, {
|
|
2260
|
+
root,
|
|
2251
2261
|
mode
|
|
2252
|
-
}
|
|
2253
|
-
if (!result) {
|
|
2254
|
-
throw new Error(`[@solidjs/vite-plugin] failed to load env schema from ${envFileAbs}`);
|
|
2255
|
-
}
|
|
2262
|
+
});
|
|
2256
2263
|
return {
|
|
2257
|
-
exported:
|
|
2258
|
-
dependencies:
|
|
2264
|
+
exported: module?.default,
|
|
2265
|
+
dependencies: dependencies.map(dep => path.resolve(root, dep)).filter(dep => fs.existsSync(dep))
|
|
2259
2266
|
};
|
|
2260
2267
|
}
|
|
2261
2268
|
function assertSchemaShape(exported, envFile, envPrefixes) {
|
|
@@ -2739,8 +2746,6 @@ const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
|
|
|
2739
2746
|
* solid-refresh#85 — is no longer used at all).
|
|
2740
2747
|
*/
|
|
2741
2748
|
const REFRESH_RUNTIME_SOURCE = 'solid-js/refresh';
|
|
2742
|
-
const viteVersionMajor = +vite.version.split('.')[0];
|
|
2743
|
-
const isVite8 = viteVersionMajor >= 8;
|
|
2744
2749
|
const DEFAULT_STYLE_EXCLUDE = /node_modules/;
|
|
2745
2750
|
const VIRTUAL_MANIFEST_ID = 'virtual:solid-manifest';
|
|
2746
2751
|
const RESOLVED_VIRTUAL_MANIFEST_ID = '\0' + VIRTUAL_MANIFEST_ID;
|
|
@@ -2928,12 +2933,22 @@ function getSolidOptions(options, isSsr, dev, isTestMode = false) {
|
|
|
2928
2933
|
hydratable: false
|
|
2929
2934
|
};
|
|
2930
2935
|
}
|
|
2936
|
+
|
|
2937
|
+
// Server components (serverFunctions.components) turn on the SSR-side
|
|
2938
|
+
// behavior-claims transform: ref/on* positions on intrinsic elements
|
|
2939
|
+
// compile to guarded `_bnd` claim holes instead of dropping. SSR-only
|
|
2940
|
+
// by construction (the dom generate ignores the flag), and apps without
|
|
2941
|
+
// the flag compile byte-for-byte as before.
|
|
2942
|
+
const serverComponents = typeof options.serverFunctions === 'object' && !!options.serverFunctions.components;
|
|
2931
2943
|
return {
|
|
2932
2944
|
moduleName: '@solidjs/web',
|
|
2933
2945
|
builtIns: SOLID_BUILT_INS,
|
|
2934
2946
|
contextToCustomElements: true,
|
|
2935
2947
|
wrapConditionals: true,
|
|
2936
2948
|
...solidOptions,
|
|
2949
|
+
...(serverComponents && solidOptions.generate === 'ssr' ? {
|
|
2950
|
+
serverComponents: true
|
|
2951
|
+
} : {}),
|
|
2937
2952
|
dev,
|
|
2938
2953
|
...(options.solid || {})
|
|
2939
2954
|
};
|
|
@@ -3231,44 +3246,28 @@ function solidPlugin(options = {}) {
|
|
|
3231
3246
|
// server-components runtime installs its response policy there).
|
|
3232
3247
|
...(command === 'serve' && serverComponents ? ['@solidjs/web/frames', '@solidjs/web/server-functions'] : []), ...solidPkgsConfig.optimizeDeps.include],
|
|
3233
3248
|
exclude: solidPkgsConfig.optimizeDeps.exclude,
|
|
3234
|
-
//
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
// (issue #262). The classic runtime is the only scan-safe lowering:
|
|
3240
|
-
// it emits bare `React.createElement` calls without injecting any
|
|
3241
|
-
// import, and the scan output is never executed — it only exists so
|
|
3242
|
-
// rolldown can walk the import graph.
|
|
3243
|
-
...(isVite8 ? {
|
|
3244
|
-
rolldownOptions: {
|
|
3245
|
-
transform: {
|
|
3246
|
-
jsx: {
|
|
3247
|
-
runtime: 'classic'
|
|
3248
|
-
}
|
|
3249
|
+
// Keep Solid TSX from injecting React's automatic runtime during scanning.
|
|
3250
|
+
rolldownOptions: {
|
|
3251
|
+
transform: {
|
|
3252
|
+
jsx: {
|
|
3253
|
+
runtime: 'classic'
|
|
3249
3254
|
}
|
|
3250
3255
|
}
|
|
3251
|
-
}
|
|
3256
|
+
}
|
|
3252
3257
|
},
|
|
3253
3258
|
...(Object.keys(test).length ? {
|
|
3254
3259
|
test
|
|
3255
3260
|
} : {})
|
|
3256
3261
|
};
|
|
3257
3262
|
},
|
|
3258
|
-
|
|
3259
|
-
async configEnvironment(name, config, opts) {
|
|
3263
|
+
configEnvironment(name, config, opts) {
|
|
3260
3264
|
config.resolve ??= {};
|
|
3261
3265
|
// Emulate Vite default fallback for `resolve.conditions` if not set
|
|
3262
3266
|
if (config.resolve.conditions == null) {
|
|
3263
|
-
// @ts-ignore These exports only exist in Vite 6
|
|
3264
|
-
const {
|
|
3265
|
-
defaultClientConditions,
|
|
3266
|
-
defaultServerConditions
|
|
3267
|
-
} = await import('vite');
|
|
3268
3267
|
if (config.consumer === 'client' || name === 'client' || opts.isSsrTargetWebworker) {
|
|
3269
|
-
config.resolve.conditions = [...defaultClientConditions];
|
|
3268
|
+
config.resolve.conditions = [...vite.defaultClientConditions];
|
|
3270
3269
|
} else {
|
|
3271
|
-
config.resolve.conditions = [...defaultServerConditions];
|
|
3270
|
+
config.resolve.conditions = [...vite.defaultServerConditions];
|
|
3272
3271
|
}
|
|
3273
3272
|
}
|
|
3274
3273
|
config.resolve.conditions = ['solid', ...(replaceDev ? ['development'] : []),
|
|
@@ -3599,8 +3598,7 @@ function solidPlugin(options = {}) {
|
|
|
3599
3598
|
// would bake a manifest-less fallback into the server bundle. Every user
|
|
3600
3599
|
// of such a setup had to hand-write this ordering plugin; absorb it.
|
|
3601
3600
|
//
|
|
3602
|
-
// Semantics
|
|
3603
|
-
// these, keeping its build-everything default):
|
|
3601
|
+
// Semantics:
|
|
3604
3602
|
// - The first hook builds the client environment first, but only where
|
|
3605
3603
|
// the ordering matters: a client build that emits a manifest and
|
|
3606
3604
|
// actually has an input. It runs at *normal* order, deliberately not
|