@solidjs/vite-plugin 3.0.0-next.29 → 3.0.0-next.31
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 +53 -4
- package/dist/cjs/index.cjs +245 -49
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +247 -51
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/src/dev-manifest.d.ts +4 -3
- package/dist/types/src/devtools/index.d.ts +5 -0
- package/dist/types/src/environment.d.ts +3 -0
- package/dist/types/src/http.d.ts +8 -1
- package/dist/types/src/ssr/index.d.ts +42 -1
- package/package.json +9 -4
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
|
}
|
|
@@ -143,6 +177,7 @@ function joinBase(base, pathname) {
|
|
|
143
177
|
* dynamically imported modules register their own styles when they render.
|
|
144
178
|
*/
|
|
145
179
|
|
|
180
|
+
const defaultStyleFilter = id => !id.includes('node_modules');
|
|
146
181
|
// The resolver is created plugin-side (it closes over the dev server) but is
|
|
147
182
|
// called from the SSR module runner, which only shares `globalThis` with the
|
|
148
183
|
// plugin when it runs in-process (the default). The primary channel is a
|
|
@@ -286,13 +321,15 @@ async function getModuleNode(env, file, importer) {
|
|
|
286
321
|
return;
|
|
287
322
|
}
|
|
288
323
|
}
|
|
289
|
-
async function collectModuleDeps(env, file, deps, crawled, onFile, importer) {
|
|
324
|
+
async function collectModuleDeps(env, file, deps, crawled, filter, onFile, importer) {
|
|
290
325
|
crawled.add(file);
|
|
291
326
|
const node = await getModuleNode(env, file, importer);
|
|
292
327
|
if (!node?.id || deps.has(node)) return;
|
|
293
328
|
deps.add(node);
|
|
294
|
-
|
|
295
|
-
if (
|
|
329
|
+
const isCss = cssFileRegExp.test(node.url.split('?')[0]);
|
|
330
|
+
if (!isCss && node.file && !node.id.startsWith('\0') && !filter(node.file)) return;
|
|
331
|
+
if (node.file) onFile?.(node.file);
|
|
332
|
+
if (isCss) return;
|
|
296
333
|
if (!node.transformResult) {
|
|
297
334
|
await env.transformRequest(node.url).catch(() => {});
|
|
298
335
|
}
|
|
@@ -303,7 +340,7 @@ async function collectModuleDeps(env, file, deps, crawled, onFile, importer) {
|
|
|
303
340
|
// from dynamicDeps — dynamic imports load their own styles when rendered.
|
|
304
341
|
for (const dep of directDeps) {
|
|
305
342
|
if (crawled.has(dep)) continue;
|
|
306
|
-
await collectModuleDeps(env, dep, deps, crawled, onFile, node.id);
|
|
343
|
+
await collectModuleDeps(env, dep, deps, crawled, filter, onFile, node.id);
|
|
307
344
|
}
|
|
308
345
|
}
|
|
309
346
|
function injectQuery(url, query) {
|
|
@@ -311,11 +348,11 @@ function injectQuery(url, query) {
|
|
|
311
348
|
}
|
|
312
349
|
|
|
313
350
|
/** Discovers ambient CSS in an entry graph without choosing how it is transported. */
|
|
314
|
-
async function collectDevStyleSources(env, files, onFile) {
|
|
351
|
+
async function collectDevStyleSources(env, files, onFile, filter = defaultStyleFilter) {
|
|
315
352
|
const deps = new Set();
|
|
316
353
|
const crawled = new Set();
|
|
317
354
|
for (const file of files) {
|
|
318
|
-
await collectModuleDeps(env, file, deps, crawled, onFile);
|
|
355
|
+
await collectModuleDeps(env, file, deps, crawled, filter, onFile);
|
|
319
356
|
}
|
|
320
357
|
const css = [];
|
|
321
358
|
const seen = new Set();
|
|
@@ -342,11 +379,11 @@ async function collectDevStyleSources(env, files, onFile) {
|
|
|
342
379
|
* CSS into `<head>` so server-painted content is styled from the first byte
|
|
343
380
|
* (no FOUC while waiting for Vite's client-side style injection).
|
|
344
381
|
*/
|
|
345
|
-
async function collectDevStyles(server, files) {
|
|
382
|
+
async function collectDevStyles(server, files, filter = defaultStyleFilter) {
|
|
346
383
|
const ssrEnv = server.environments?.ssr;
|
|
347
384
|
const clientEnv = server.environments?.client;
|
|
348
385
|
if (!ssrEnv || !clientEnv) return [];
|
|
349
|
-
const sources = await collectDevStyleSources(ssrEnv, files.map(file => path.resolve(server.config.root, file)));
|
|
386
|
+
const sources = await collectDevStyleSources(ssrEnv, files.map(file => path.resolve(server.config.root, file)), undefined, filter);
|
|
350
387
|
const css = [];
|
|
351
388
|
for (const source of sources) {
|
|
352
389
|
// `?direct` yields the compiled stylesheet text (what Vite serves for
|
|
@@ -402,7 +439,7 @@ function devModuleUrl(root, base, key) {
|
|
|
402
439
|
// drive letter on Windows: /@fs/C:/…).
|
|
403
440
|
return joinBase(base, '/@fs/' + absolute.replace(/^\//, '') + query);
|
|
404
441
|
}
|
|
405
|
-
function createDevAssetResolver(server) {
|
|
442
|
+
function createDevAssetResolver(server, filter = defaultStyleFilter) {
|
|
406
443
|
// Server-side lazy() re-requests a module's assets on every retry of a
|
|
407
444
|
// suspended render pass (retries re-create the component). The build
|
|
408
445
|
// manifest answers those repeats synchronously and the pass converges; an
|
|
@@ -436,7 +473,7 @@ function createDevAssetResolver(server) {
|
|
|
436
473
|
// The module's dev URL doubles as its client entry: modulepreload
|
|
437
474
|
// hint and hydration module-map value.
|
|
438
475
|
const js = [devModuleUrl(root, base, key)];
|
|
439
|
-
const css = await collectDevStyles(server, [key]);
|
|
476
|
+
const css = await collectDevStyles(server, [key], filter);
|
|
440
477
|
return {
|
|
441
478
|
js,
|
|
442
479
|
css
|
|
@@ -529,6 +566,11 @@ function boundaryModules() {
|
|
|
529
566
|
function isRunnableEnvironment(environment) {
|
|
530
567
|
return !!environment && typeof environment === 'object' && 'runner' in environment;
|
|
531
568
|
}
|
|
569
|
+
function getEnvironmentConsumer(environment, options) {
|
|
570
|
+
const consumer = environment?.config?.consumer;
|
|
571
|
+
if (consumer === 'client' || consumer === 'server') return consumer;
|
|
572
|
+
return options?.ssr ? 'server' : 'client';
|
|
573
|
+
}
|
|
532
574
|
|
|
533
575
|
// The `"use server"` directive compiler. This wraps the native
|
|
534
576
|
// `transformDirectives` pass from @dom-expressions/compiler (Rust/Oxc); the
|
|
@@ -965,12 +1007,12 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
965
1007
|
const relative = path.relative(root, entry).split(path.sep).join('/');
|
|
966
1008
|
return relative.startsWith('..') ? '/@fs/' + entry : '/' + relative;
|
|
967
1009
|
}
|
|
968
|
-
const
|
|
1010
|
+
const startPlugins = [{
|
|
969
1011
|
name: 'solid:server-functions/handler',
|
|
970
1012
|
enforce: 'pre',
|
|
971
1013
|
resolveId(source, _importer, opts) {
|
|
972
1014
|
if (source === HANDLER_ID$1) {
|
|
973
|
-
if (
|
|
1015
|
+
if (getEnvironmentConsumer(this.environment, opts) !== 'server') {
|
|
974
1016
|
this.error(`${HANDLER_ID$1} is server-only; import it from your server entry (SSR build).`);
|
|
975
1017
|
}
|
|
976
1018
|
return {
|
|
@@ -981,7 +1023,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
981
1023
|
return null;
|
|
982
1024
|
},
|
|
983
1025
|
load(id, opts) {
|
|
984
|
-
if (id === HANDLER_ID$1 && opts
|
|
1026
|
+
if (id === HANDLER_ID$1 && getEnvironmentConsumer(this.environment, opts) === 'server') {
|
|
985
1027
|
const externalDev = this.environment.mode === 'dev' && (internal.externalDevServer || !isRunnableEnvironment(this.environment));
|
|
986
1028
|
return handlerModuleCode(isBuild || externalDev);
|
|
987
1029
|
}
|
|
@@ -989,7 +1031,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
989
1031
|
}
|
|
990
1032
|
}];
|
|
991
1033
|
if (installDevMiddleware) {
|
|
992
|
-
|
|
1034
|
+
startPlugins.push({
|
|
993
1035
|
name: 'solid:server-functions/dev-middleware',
|
|
994
1036
|
apply: 'serve',
|
|
995
1037
|
configureServer(server) {
|
|
@@ -1034,7 +1076,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1034
1076
|
nativeEvent: req
|
|
1035
1077
|
}
|
|
1036
1078
|
};
|
|
1037
|
-
const response = internal.ssrHandler ? await handler.handleRequest(webRequestFromNode(req, dispatchUrl), dispatchOptions) : await handler.handleServerFunctionRequest(webRequestFromNode(req, dispatchUrl), dispatchOptions);
|
|
1079
|
+
const response = internal.ssrHandler ? await handler.handleRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions) : await handler.handleServerFunctionRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions);
|
|
1038
1080
|
await sendWebResponse(res, response);
|
|
1039
1081
|
})().catch(error => {
|
|
1040
1082
|
if (error instanceof Error) server.ssrFixStacktrace(error);
|
|
@@ -1101,7 +1143,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1101
1143
|
return null;
|
|
1102
1144
|
},
|
|
1103
1145
|
async load(id, opts) {
|
|
1104
|
-
const mode = opts
|
|
1146
|
+
const mode = getEnvironmentConsumer(this.environment, opts);
|
|
1105
1147
|
if (id === manifestId) {
|
|
1106
1148
|
if (isBuild && mode === 'server') {
|
|
1107
1149
|
// Merge the client build's persisted discoveries at load time,
|
|
@@ -1124,7 +1166,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1124
1166
|
name: 'solid:server-functions/compiler',
|
|
1125
1167
|
enforce: 'pre',
|
|
1126
1168
|
async transform(code, fileId, opts) {
|
|
1127
|
-
const mode = opts
|
|
1169
|
+
const mode = getEnvironmentConsumer(this.environment, opts);
|
|
1128
1170
|
const [id] = fileId.split('?');
|
|
1129
1171
|
if (!filter(id)) {
|
|
1130
1172
|
return null;
|
|
@@ -1157,7 +1199,17 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1157
1199
|
}
|
|
1158
1200
|
return null;
|
|
1159
1201
|
}
|
|
1160
|
-
}, ...
|
|
1202
|
+
}, ...startPlugins];
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
const DEVTOOLS_PACKAGE = '@solidjs/start-devtools';
|
|
1206
|
+
const DEVTOOLS_ID = 'virtual:solid-devtools';
|
|
1207
|
+
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
|
+
function devtoolsMountModuleCode() {
|
|
1212
|
+
return [`import ${JSON.stringify(DEVTOOLS_ID)};`, `import { mountDevToolbar } from '${DEVTOOLS_PACKAGE}';`, `mountDevToolbar();`].join('\n');
|
|
1161
1213
|
}
|
|
1162
1214
|
|
|
1163
1215
|
// Start-mode serving for plain Vite apps: `solid({ start: {...} })` (or the
|
|
@@ -1242,6 +1294,7 @@ const RESOLVED_DEV_STYLES_ID = '\0' + DEV_STYLES_ID;
|
|
|
1242
1294
|
const ENTRY_SERVER_ID = 'virtual:solid-ssr-entry-server.tsx';
|
|
1243
1295
|
const ENTRY_CLIENT_ID = 'virtual:solid-ssr-entry-client.tsx';
|
|
1244
1296
|
const DOCUMENT_ID = 'virtual:solid-ssr-document.tsx';
|
|
1297
|
+
const ERROR_BOUNDARY_ID = 'virtual:solid-ssr-error-boundary.tsx';
|
|
1245
1298
|
const MANIFEST_ID = 'virtual:solid-manifest';
|
|
1246
1299
|
const SERVER_FUNCTION_HANDLER_ID = 'virtual:solid-server-function-handler';
|
|
1247
1300
|
const STORAGE_SOURCE = '@solidjs/web/storage';
|
|
@@ -1353,6 +1406,12 @@ function startServe(options, internal = {}) {
|
|
|
1353
1406
|
// the server-function handler module either way). Everything is gated
|
|
1354
1407
|
// codegen: with the option off, none of these imports exist anywhere.
|
|
1355
1408
|
const serverComponents = !!internal.serverComponents;
|
|
1409
|
+
const errorBoundary = options.errorBoundary !== false;
|
|
1410
|
+
const styleFilter = internal.styleFilter;
|
|
1411
|
+
let devtools = false;
|
|
1412
|
+
let devtoolsResolution;
|
|
1413
|
+
/** Resolved module id of `@solidjs/start-devtools` once detection succeeds. */
|
|
1414
|
+
let devtoolsId = null;
|
|
1356
1415
|
// `external` is server-mode-only (documented no-op in client mode, so a
|
|
1357
1416
|
// host-integrated config survives the `ssr` boolean flip untouched).
|
|
1358
1417
|
const externalServer = !clientMode && !!options.external;
|
|
@@ -1369,6 +1428,40 @@ function startServe(options, internal = {}) {
|
|
|
1369
1428
|
if (!entries) throw new Error('[@solidjs/vite-plugin] SSR entries not resolved yet');
|
|
1370
1429
|
return entries;
|
|
1371
1430
|
}
|
|
1431
|
+
async function resolveDevtools(resolve, importer) {
|
|
1432
|
+
if (devtools !== undefined) return devtools;
|
|
1433
|
+
// Detect from the app graph first (the documented install location), then
|
|
1434
|
+
// from the plugin's own file: in pnpm-isolated apps a copy that is only a
|
|
1435
|
+
// dependency of the plugin is not reachable from the app's importers. The
|
|
1436
|
+
// resolved id is kept so the virtual modules' imports of the package can
|
|
1437
|
+
// be delegated to it (see resolveId).
|
|
1438
|
+
devtoolsResolution ??= (async () => ((await resolve(DEVTOOLS_PACKAGE, importer)) ?? (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))))))?.id ?? null)();
|
|
1439
|
+
devtoolsId = await devtoolsResolution;
|
|
1440
|
+
devtools = devtoolsId !== null;
|
|
1441
|
+
if (!devtools && options.devtools === true) {
|
|
1442
|
+
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
|
+
}
|
|
1444
|
+
return devtools;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Cheap root-walk probe mirroring how the optimizer resolves bare
|
|
1449
|
+
* `optimizeDeps.include` entries: is @solidjs/start-devtools reachable from
|
|
1450
|
+
* the Vite root? Detection proper (resolveDevtools) runs later with a real
|
|
1451
|
+
* importer; this only decides whether the toolbar graph can be pre-bundled
|
|
1452
|
+
* at scan time — it hangs off virtual modules the scanner never sees, so
|
|
1453
|
+
* first-request discovery would force a re-optimize + full page reload.
|
|
1454
|
+
*/
|
|
1455
|
+
function devtoolsReachableFromRoot(dir) {
|
|
1456
|
+
for (let current = dir;;) {
|
|
1457
|
+
if (fs.existsSync(path.join(current, 'node_modules', DEVTOOLS_PACKAGE, 'package.json'))) {
|
|
1458
|
+
return true;
|
|
1459
|
+
}
|
|
1460
|
+
const parent = path.dirname(current);
|
|
1461
|
+
if (parent === current) return false;
|
|
1462
|
+
current = parent;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1372
1465
|
|
|
1373
1466
|
/** Import specifier for generated code: absolute for files, id for virtuals. */
|
|
1374
1467
|
function entryServerSpec() {
|
|
@@ -1408,7 +1501,7 @@ function startServe(options, internal = {}) {
|
|
|
1408
1501
|
return generated ? [app, ...(document ? [document] : [])] : [path.resolve(root, entryServer)];
|
|
1409
1502
|
}
|
|
1410
1503
|
async function devStylesModuleCode(environment, watchFile) {
|
|
1411
|
-
const styles = await collectDevStyleSources(environment, styleRoots(), watchFile);
|
|
1504
|
+
const styles = await collectDevStyleSources(environment, styleRoots(), watchFile, styleFilter);
|
|
1412
1505
|
if (!styles.length) return `export default '';`;
|
|
1413
1506
|
const imports = styles.map((style, index) => {
|
|
1414
1507
|
const specifier = style.url.includes('?') ? `${style.url}&inline` : `${style.url}?inline`;
|
|
@@ -1416,19 +1509,26 @@ function startServe(options, internal = {}) {
|
|
|
1416
1509
|
});
|
|
1417
1510
|
return [...imports, `const ids = ${JSON.stringify(styles.map(style => style.id))};`, `const css = [${styles.map((_, index) => `css${index}`).join(', ')}];`, `const escapeAttr = value => value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');`, `export default css.map((content, index) => {`, ` const id = escapeAttr(ids[index]);`, ` return '<style data-asset="' + id + '" data-vite-dev-id="' + id + '">' +`, ` content.replace(/<\\/(style)/gi, '<\\\\/$1') + '</style>';`, `}).join('');`].join('\n');
|
|
1418
1511
|
}
|
|
1512
|
+
function errorBoundaryImport() {
|
|
1513
|
+
return isBuild && errorBoundary ? [`import { DefaultErrorBoundary } from ${JSON.stringify(ERROR_BOUNDARY_ID)};`] : [];
|
|
1514
|
+
}
|
|
1515
|
+
function documentTree(root, wrapper) {
|
|
1516
|
+
const content = wrapper ? `<${wrapper}><${root} /></${wrapper}>` : `<${root} />`;
|
|
1517
|
+
return isBuild && errorBoundary ? [` <DefaultErrorBoundary>`, ` <Document>`, ` <DefaultErrorBoundary>`, ` ${content}`, ` </DefaultErrorBoundary>`, ` </Document>`, ` </DefaultErrorBoundary>`] : [` <Document>`, ` ${content}`, ` </Document>`];
|
|
1518
|
+
}
|
|
1419
1519
|
function generatedEntryServerCode() {
|
|
1420
1520
|
if (clientMode) {
|
|
1421
1521
|
// The client-mode shell: the document without the app. Rendered per
|
|
1422
1522
|
// request in dev (any HTML GET gets it — history-fallback semantics)
|
|
1423
1523
|
// and once at build time into dist/client/index.html. The client
|
|
1424
1524
|
// entry script is injected by the handler, exactly like SSR mode.
|
|
1425
|
-
return [`import { renderToStream } from '@solidjs/web';`, `import manifest from ${JSON.stringify(MANIFEST_ID)};`, `import Document from ${JSON.stringify(documentSpec())};`, ``, `export function render(request, context) {`, ` return renderToStream(() => <Document
|
|
1525
|
+
return [`import { renderToStream } from '@solidjs/web';`, `import manifest from ${JSON.stringify(MANIFEST_ID)};`, `import Document from ${JSON.stringify(documentSpec())};`, ...errorBoundaryImport(), ``, `export function render(request, context) {`, ` return renderToStream(() => (`, ...(isBuild && errorBoundary ? [` <DefaultErrorBoundary>`, ` <Document />`, ` </DefaultErrorBoundary>`] : [` <Document />`]), ` ), { manifest });`, `}`].join('\n');
|
|
1426
1526
|
}
|
|
1427
1527
|
const {
|
|
1428
1528
|
app
|
|
1429
1529
|
} = requireEntries();
|
|
1430
1530
|
const streamOptions = `{ manifest${serverComponents ? ', plugins: [ServerComponentPlugin]' : ''} }`;
|
|
1431
|
-
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)};`, ...(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 ? [
|
|
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 ? [
|
|
1432
1532
|
// Direct (in-process) server-function calls made during document
|
|
1433
1533
|
// SSR must resolve to inline-renderable components; the endpoint
|
|
1434
1534
|
// response transform is installed separately by the
|
|
@@ -1442,9 +1542,9 @@ function startServe(options, internal = {}) {
|
|
|
1442
1542
|
// the *complete* render) and buffers the stream — so it crosses
|
|
1443
1543
|
// boxed under a private key the generated handler unboxes
|
|
1444
1544
|
// (both modules are ours).
|
|
1445
|
-
`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(() => (`,
|
|
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');
|
|
1446
1546
|
}
|
|
1447
|
-
function generatedEntryClientCode() {
|
|
1547
|
+
function generatedEntryClientCode(toolbar) {
|
|
1448
1548
|
const {
|
|
1449
1549
|
app
|
|
1450
1550
|
} = requireEntries();
|
|
@@ -1454,13 +1554,13 @@ function startServe(options, internal = {}) {
|
|
|
1454
1554
|
// app cannot claim server DOM anyway. The entry script is injected
|
|
1455
1555
|
// without `async` (plain module = deferred), so document.body is
|
|
1456
1556
|
// complete when this runs.
|
|
1457
|
-
return [`import { render } from '@solidjs/web';`, `import App from ${JSON.stringify(app)};`, ``, `render(() => <App
|
|
1557
|
+
return [`import { render } from '@solidjs/web';`, ...errorBoundaryImport(), ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_ID)};`] : []), `import App from ${JSON.stringify(app)};`, ``, `render(() => ${isBuild && errorBoundary ? '<DefaultErrorBoundary><App /></DefaultErrorBoundary>' : toolbar ? '<DevToolbar><App /></DevToolbar>' : '<App />'}, document.body);`].join('\n');
|
|
1458
1558
|
}
|
|
1459
|
-
return [`import { hydrate } from '@solidjs/web';`, ...(serverComponents ? [`import { installServerComponents } from '@solidjs/web/frames';`] : []), `import Document from ${JSON.stringify(documentSpec())};`, `import App from ${JSON.stringify(app)};`, ``, ...(serverComponents ? [
|
|
1559
|
+
return [`import { hydrate } from '@solidjs/web';`, ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_ID)};`] : []), ...(serverComponents ? [`import { installServerComponents } from '@solidjs/web/frames';`] : []), ...errorBoundaryImport(), `import Document from ${JSON.stringify(documentSpec())};`, `import App from ${JSON.stringify(app)};`, ``, ...(serverComponents ? [
|
|
1460
1560
|
// Installs the t=0 document-adoption registry and the transport
|
|
1461
1561
|
// policy (component responses morph their boundary instead of
|
|
1462
1562
|
// decoding as data). Must run before hydrate().
|
|
1463
|
-
`installServerComponents();`, ``] : []), `hydrate(() => (`,
|
|
1563
|
+
`installServerComponents();`, ``] : []), `hydrate(() => (`, ...documentTree('App', toolbar ? 'DevToolbar' : undefined), `), document);`].join('\n');
|
|
1464
1564
|
}
|
|
1465
1565
|
|
|
1466
1566
|
// Built-in document shell: minimal, hydration-ready. The client entry
|
|
@@ -1471,6 +1571,7 @@ function startServe(options, internal = {}) {
|
|
|
1471
1571
|
// HydrationScript is covered too: the handler strips the event-capture
|
|
1472
1572
|
// script from the client-mode shell.)
|
|
1473
1573
|
const documentShellCode = [...(clientMode ? [] : [`import { HydrationScript } from '@solidjs/web';`, ``]), `export default function Document(props) {`, ` return (`, ` <html lang="en">`, ` <head>`, ` <meta charset="utf-8" />`, ` <meta name="viewport" content="width=device-width, initial-scale=1.0" />`, ...(clientMode ? [] : [` <HydrationScript />`]), ` </head>`, ` <body>{props.children}</body>`, ` </html>`, ` );`, `}`].join('\n');
|
|
1574
|
+
const errorBoundaryCode = [`import { Errored } from 'solid-js';`, `import { httpStatus, isServer } from '@solidjs/web';`, ``, `function ErrorFallback(props) {`, ` console.error(props.error());`, ` httpStatus(500);`, ` return (`, ` <span style="font-size:1.5em;text-align:center;position:fixed;left:0;bottom:55%;width:100%">`, ` {isServer ? '500 | Internal Server Error' : 'Error | Uncaught Client Exception'}`, ` </span>`, ` );`, `}`, ``, `export function DefaultErrorBoundary(props) {`, ` return (`, ` <Errored fallback={(error) => <ErrorFallback error={error} />}>`, ` {props.children}`, ` </Errored>`, ` );`, `}`].join('\n');
|
|
1474
1575
|
|
|
1475
1576
|
// The handler module: dev and prod share the render/response plumbing;
|
|
1476
1577
|
// they differ in how the client entry URL is known (baked dev URL vs a
|
|
@@ -1522,7 +1623,7 @@ function startServe(options, internal = {}) {
|
|
|
1522
1623
|
// head-open splice actively broke hydration — a script ahead of the
|
|
1523
1624
|
// authored <head> elements claims as the first walked child and drifts
|
|
1524
1625
|
// every positional claim after it.
|
|
1525
|
-
lines.push(``, `function createHtmlChunkTransform(clientEntry, extraHead) {`, ` let first = true;`, ` let injected = false;`, ` return (chunk) => {`);
|
|
1626
|
+
lines.push(``, `function escapeAttribute(value) {`, ` return value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');`, `}`, ``, `function createHtmlChunkTransform(clientEntry, extraHead, nonce) {`, ` const nonceAttr = nonce ? ' nonce="' + escapeAttribute(nonce) + '"' : '';`, ` let first = true;`, ` let injected = false;`, ` return (chunk) => {`);
|
|
1526
1627
|
if (!generated) {
|
|
1527
1628
|
// Authored entries reference the client entry by its dev path (the
|
|
1528
1629
|
// `<script src="/src/entry-client.tsx">` convention); rewrite it to
|
|
@@ -1552,7 +1653,7 @@ function startServe(options, internal = {}) {
|
|
|
1552
1653
|
// scripts default to deferred execution, which is exactly right for a
|
|
1553
1654
|
// fresh render-into-body mount (hydration, by contrast, wants to
|
|
1554
1655
|
// start as early as possible).
|
|
1555
|
-
headParts.push(`(clientEntry ? '<script type="module" src="' + clientEntry + '"${clientMode ? '' : ' async'}></' + 'script>' : '')`);
|
|
1656
|
+
headParts.push(`(clientEntry ? '<script type="module"' + nonceAttr + ' src="' + clientEntry + '"${clientMode ? '' : ' async'}></' + 'script>' : '')`);
|
|
1556
1657
|
}
|
|
1557
1658
|
if (headParts.length) {
|
|
1558
1659
|
lines.push(` chunk = chunk.replace('</head>', ${headParts.join(' + ')} + '</head>');`);
|
|
@@ -1606,7 +1707,7 @@ function startServe(options, internal = {}) {
|
|
|
1606
1707
|
// The runtime's response-head lifecycle: commit at shell flush,
|
|
1607
1708
|
// pre-flush Location as a real redirect, post-flush Location as the
|
|
1608
1709
|
// script fallback; the transform injects the doctype/head pieces.
|
|
1609
|
-
` return createSSRResponse(result, event, {`, ` responseInit: options.responseInit,`, ` nonce: options.nonce,`, ` transformChunk: createHtmlChunkTransform(clientEntry, options.devHead),`, ` });`, `}`, ``, `export async function handleRequest(request, options = {}) {`,
|
|
1710
|
+
` return createSSRResponse(result, event, {`, ` responseInit: options.responseInit,`, ` nonce: options.nonce,`, ` transformChunk: createHtmlChunkTransform(clientEntry, options.devHead, options.nonce),`, ` });`, `}`, ``, `export async function handleRequest(request, options = {}) {`,
|
|
1610
1711
|
// `options.event` is the public wrapper->event extension seam: extra
|
|
1611
1712
|
// fields (conventionally `nativeEvent`, the platform's raw request
|
|
1612
1713
|
// object) spread over the event's defaults at creation, so hosts and
|
|
@@ -1636,6 +1737,9 @@ function startServe(options, internal = {}) {
|
|
|
1636
1737
|
enforce: 'pre',
|
|
1637
1738
|
config(userConfig, env) {
|
|
1638
1739
|
root = path.resolve(userConfig.root || process.cwd());
|
|
1740
|
+
devtools = env.command === 'serve' && !env.isPreview && options.devtools !== false ? undefined : false;
|
|
1741
|
+
devtoolsResolution = undefined;
|
|
1742
|
+
devtoolsId = null;
|
|
1639
1743
|
entries = resolveEntries(root, options, clientMode);
|
|
1640
1744
|
middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
|
|
1641
1745
|
// Server-mode only, like `entryServer`/`external` (a documented
|
|
@@ -1754,7 +1858,15 @@ function startServe(options, internal = {}) {
|
|
|
1754
1858
|
}
|
|
1755
1859
|
} : {}),
|
|
1756
1860
|
optimizeDeps: {
|
|
1757
|
-
entries: scanEntries
|
|
1861
|
+
entries: scanEntries,
|
|
1862
|
+
// Like the refresh runtime in the main plugin: the toolbar
|
|
1863
|
+
// graph is injected behind virtual modules the scanner never
|
|
1864
|
+
// crawls, so pre-bundle it (and the server-functions runtime
|
|
1865
|
+
// the virtual module pulls in) up front — first-request
|
|
1866
|
+
// discovery would re-optimize and full-reload the page.
|
|
1867
|
+
...(devtools === undefined && devtoolsReachableFromRoot(root) ? {
|
|
1868
|
+
include: [DEVTOOLS_PACKAGE, '@solidjs/web/server-functions']
|
|
1869
|
+
} : {})
|
|
1758
1870
|
}
|
|
1759
1871
|
})
|
|
1760
1872
|
};
|
|
@@ -1764,7 +1876,7 @@ function startServe(options, internal = {}) {
|
|
|
1764
1876
|
base = config.base;
|
|
1765
1877
|
isBuild = config.command === 'build';
|
|
1766
1878
|
},
|
|
1767
|
-
resolveId(source) {
|
|
1879
|
+
resolveId(source, importer) {
|
|
1768
1880
|
if (source === HANDLER_ID) {
|
|
1769
1881
|
return {
|
|
1770
1882
|
id: HANDLER_ID,
|
|
@@ -1777,33 +1889,94 @@ function startServe(options, internal = {}) {
|
|
|
1777
1889
|
moduleSideEffects: true
|
|
1778
1890
|
};
|
|
1779
1891
|
}
|
|
1780
|
-
if (source === ENTRY_SERVER_ID || source === ENTRY_CLIENT_ID || source === DOCUMENT_ID) {
|
|
1892
|
+
if (source === ENTRY_SERVER_ID || source === ENTRY_CLIENT_ID || source === DOCUMENT_ID || source === ERROR_BOUNDARY_ID) {
|
|
1781
1893
|
return {
|
|
1782
1894
|
id: source,
|
|
1783
1895
|
moduleSideEffects: source === ENTRY_CLIENT_ID
|
|
1784
1896
|
};
|
|
1785
1897
|
}
|
|
1898
|
+
if (devtools && (source === DEVTOOLS_ID || source === DEVTOOLS_MOUNT_ID)) {
|
|
1899
|
+
return {
|
|
1900
|
+
id: source,
|
|
1901
|
+
moduleSideEffects: true
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
// The virtual devtools modules import the package by its bare name,
|
|
1905
|
+
// but a virtual importer gives Vite no directory to walk, so the
|
|
1906
|
+
// specifier would only resolve from the Vite root — which fails in
|
|
1907
|
+
// pnpm-isolated apps where the package is not a root-level install.
|
|
1908
|
+
// Delegate to the resolution captured at detection time instead.
|
|
1909
|
+
if (devtoolsId && source === DEVTOOLS_PACKAGE && (importer === DEVTOOLS_ID || importer === DEVTOOLS_MOUNT_ID)) {
|
|
1910
|
+
return {
|
|
1911
|
+
id: devtoolsId
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1786
1914
|
return null;
|
|
1787
1915
|
},
|
|
1788
1916
|
async load(id, opts) {
|
|
1917
|
+
const consumer = getEnvironmentConsumer(this.environment, opts);
|
|
1789
1918
|
if (id === HANDLER_ID) {
|
|
1790
|
-
if (
|
|
1919
|
+
if (consumer !== 'server') {
|
|
1791
1920
|
this.error(`${HANDLER_ID} is server-only; import it from server code (SSR build).`);
|
|
1792
1921
|
}
|
|
1793
1922
|
const externalDev = !isBuild && this.environment.mode === 'dev' && (externalServer || !isRunnableEnvironment(this.environment));
|
|
1794
1923
|
return handlerModuleCode(externalDev);
|
|
1795
1924
|
}
|
|
1796
1925
|
if (id === RESOLVED_DEV_STYLES_ID) {
|
|
1797
|
-
if (
|
|
1926
|
+
if (consumer !== 'server' || this.environment.mode !== 'dev') {
|
|
1798
1927
|
this.error(`${DEV_STYLES_ID} is only available to the development server handler.`);
|
|
1799
1928
|
}
|
|
1800
1929
|
return devStylesModuleCode(this.environment, file => this.addWatchFile(file));
|
|
1801
1930
|
}
|
|
1802
1931
|
if (id === ENTRY_SERVER_ID) return generatedEntryServerCode();
|
|
1803
|
-
if (id === ENTRY_CLIENT_ID)
|
|
1932
|
+
if (id === ENTRY_CLIENT_ID) {
|
|
1933
|
+
const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1934
|
+
skipSelf: true
|
|
1935
|
+
}), requireEntries().app);
|
|
1936
|
+
return generatedEntryClientCode(toolbar);
|
|
1937
|
+
}
|
|
1804
1938
|
if (id === DOCUMENT_ID) return documentShellCode;
|
|
1939
|
+
if (id === ERROR_BOUNDARY_ID) return errorBoundaryCode;
|
|
1940
|
+
if (id === DEVTOOLS_ID || id === DEVTOOLS_MOUNT_ID) {
|
|
1941
|
+
// A cold direct request (stale tab reload) can reach the virtual
|
|
1942
|
+
// module before the entry has triggered detection — run it here so
|
|
1943
|
+
// first-touch order doesn't matter.
|
|
1944
|
+
if (!isBuild && consumer === 'client' && devtools === undefined) {
|
|
1945
|
+
const {
|
|
1946
|
+
app,
|
|
1947
|
+
entryClient
|
|
1948
|
+
} = requireEntries();
|
|
1949
|
+
await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1950
|
+
skipSelf: true
|
|
1951
|
+
}), app ?? path.resolve(root, entryClient));
|
|
1952
|
+
}
|
|
1953
|
+
if (isBuild || !devtools || consumer !== 'client') {
|
|
1954
|
+
this.error(`${id} is only available to the development client.`);
|
|
1955
|
+
}
|
|
1956
|
+
return id === DEVTOOLS_ID ? devtoolsModuleCode() : devtoolsMountModuleCode();
|
|
1957
|
+
}
|
|
1805
1958
|
return null;
|
|
1806
1959
|
},
|
|
1960
|
+
async transform(code, id, opts) {
|
|
1961
|
+
if (isBuild || devtools === false) return null;
|
|
1962
|
+
const current = requireEntries();
|
|
1963
|
+
if (current.generated || getEnvironmentConsumer(this.environment, opts) !== 'client') {
|
|
1964
|
+
return null;
|
|
1965
|
+
}
|
|
1966
|
+
// Module ids are always forward-slashed; normalize the path.resolve
|
|
1967
|
+
// side too so the comparison holds on Windows.
|
|
1968
|
+
if (vite.normalizePath(id.split('?')[0]) !== vite.normalizePath(path.resolve(root, current.entryClient))) {
|
|
1969
|
+
return null;
|
|
1970
|
+
}
|
|
1971
|
+
const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1972
|
+
skipSelf: true
|
|
1973
|
+
}), id);
|
|
1974
|
+
if (!toolbar) return null;
|
|
1975
|
+
return {
|
|
1976
|
+
code: `import ${JSON.stringify(DEVTOOLS_MOUNT_ID)};\n${code}`,
|
|
1977
|
+
map: null
|
|
1978
|
+
};
|
|
1979
|
+
},
|
|
1807
1980
|
configurePreviewServer(server) {
|
|
1808
1981
|
// `vite build && vite preview` runs the production artifact as-is:
|
|
1809
1982
|
// Vite's preview statics serve dist/client (see the config hook) and
|
|
@@ -1828,7 +2001,7 @@ function startServe(options, internal = {}) {
|
|
|
1828
2001
|
// server-function endpoint) and hands the URL to application
|
|
1829
2002
|
// code, so restore the base — the deployed production handler
|
|
1830
2003
|
// receives base-prefixed URLs and preview must match it.
|
|
1831
|
-
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/')),
|
|
2004
|
+
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/'), res),
|
|
1832
2005
|
// Same event extension the dev middleware and a production
|
|
1833
2006
|
// Node entry pass: the raw Node request as `nativeEvent`.
|
|
1834
2007
|
{
|
|
@@ -1879,13 +2052,13 @@ function startServe(options, internal = {}) {
|
|
|
1879
2052
|
// Loaded through the SSR environment so the app, the request
|
|
1880
2053
|
// event storage, and the handler share one module registry.
|
|
1881
2054
|
const handler = await server.ssrLoadModule(HANDLER_ID);
|
|
1882
|
-
const styles = pageRequest ? await collectDevStyles(server, styleRoots()) : [];
|
|
2055
|
+
const styles = pageRequest ? await collectDevStyles(server, styleRoots(), styleFilter) : [];
|
|
1883
2056
|
const devHead = styles.map(renderDevStyleTag).join('');
|
|
1884
2057
|
// Post middlewares run after Vite's base middleware stripped
|
|
1885
2058
|
// the configured `base` from req.url; restore it so the app
|
|
1886
2059
|
// sees the same URLs in dev as in production (where the
|
|
1887
2060
|
// deployed handler receives base-prefixed requests).
|
|
1888
|
-
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/')), {
|
|
2061
|
+
const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/'), res), {
|
|
1889
2062
|
devHead,
|
|
1890
2063
|
pageRequest,
|
|
1891
2064
|
// The raw Node request on the event, matching what a
|
|
@@ -2568,6 +2741,7 @@ const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
|
|
|
2568
2741
|
const REFRESH_RUNTIME_SOURCE = 'solid-js/refresh';
|
|
2569
2742
|
const viteVersionMajor = +vite.version.split('.')[0];
|
|
2570
2743
|
const isVite8 = viteVersionMajor >= 8;
|
|
2744
|
+
const DEFAULT_STYLE_EXCLUDE = /node_modules/;
|
|
2571
2745
|
const VIRTUAL_MANIFEST_ID = 'virtual:solid-manifest';
|
|
2572
2746
|
const RESOLVED_VIRTUAL_MANIFEST_ID = '\0' + VIRTUAL_MANIFEST_ID;
|
|
2573
2747
|
|
|
@@ -2822,10 +2996,30 @@ function solidPlugin(options = {}) {
|
|
|
2822
2996
|
// `start: true` is sugar for the empty options bag — one start mode,
|
|
2823
2997
|
// two spellings — so normalize here and let everything downstream see a
|
|
2824
2998
|
// single shape (`false` behaves exactly like omission).
|
|
2825
|
-
const
|
|
2999
|
+
const startOptions = options.start === true ? {} : options.start || null;
|
|
3000
|
+
const styleFilterOptions = startOptions?.css?.filter;
|
|
3001
|
+
// The CSS crawl walks the module graph from the app's own entries, so a
|
|
3002
|
+
// plain createFilter allowlist can't express the option's purpose (opting
|
|
3003
|
+
// node_modules graphs in): a bare `include` would reject the app sources
|
|
3004
|
+
// the crawl has to traverse to ever reach the included package. Instead
|
|
3005
|
+
// `include` rescues files on top of the baseline (everything except
|
|
3006
|
+
// `exclude`, which defaults to node_modules), while a file matching both
|
|
3007
|
+
// patterns stays excluded — createFilter's own conflict rule.
|
|
3008
|
+
const createStyleFilter = resolve => {
|
|
3009
|
+
const opts = resolve === undefined ? undefined : {
|
|
3010
|
+
resolve
|
|
3011
|
+
};
|
|
3012
|
+
const base = vite.createFilter(undefined, styleFilterOptions?.exclude ?? DEFAULT_STYLE_EXCLUDE, opts);
|
|
3013
|
+
const include = styleFilterOptions?.include;
|
|
3014
|
+
const hasInclude = include != null && (!Array.isArray(include) || include.length > 0);
|
|
3015
|
+
const included = hasInclude ? vite.createFilter(include, styleFilterOptions?.exclude, opts) : null;
|
|
3016
|
+
return id => base(id) || (included ? included(id) : false);
|
|
3017
|
+
};
|
|
3018
|
+
let styleFilter = createStyleFilter();
|
|
3019
|
+
const filterDevStyles = id => styleFilter(id);
|
|
2826
3020
|
// `start.external` only means something when a server side exists to hand
|
|
2827
3021
|
// over (SSR start mode); in client mode it is a documented no-op.
|
|
2828
|
-
const externalDevServer = !!options.ssr && !!
|
|
3022
|
+
const externalDevServer = !!options.ssr && !!startOptions?.external;
|
|
2829
3023
|
let needHmr = false;
|
|
2830
3024
|
let replaceDev = false;
|
|
2831
3025
|
// The live dev server, kept so the dev manifest module can bake the bridge
|
|
@@ -3103,6 +3297,7 @@ function solidPlugin(options = {}) {
|
|
|
3103
3297
|
filter = vite.createFilter(options.include, options.exclude, {
|
|
3104
3298
|
resolve: projectRoot
|
|
3105
3299
|
});
|
|
3300
|
+
styleFilter = createStyleFilter(projectRoot);
|
|
3106
3301
|
if (serverComponents && !(options.start && options.ssr)) {
|
|
3107
3302
|
config.logger.warn('[@solidjs/vite-plugin] serverFunctions.components is set without SSR start mode (the `start` ' + '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 " + 'from post-boot streams and your client code must call installServerComponents() itself.');
|
|
3108
3303
|
}
|
|
@@ -3116,7 +3311,7 @@ function solidPlugin(options = {}) {
|
|
|
3116
3311
|
// that don't share globals with this process, through the HTTP bridge
|
|
3117
3312
|
// endpoint the middleware serves.
|
|
3118
3313
|
if (options.ssr || options.start) {
|
|
3119
|
-
registerDevAssetResolver(server.config.root, createDevAssetResolver(server));
|
|
3314
|
+
registerDevAssetResolver(server.config.root, createDevAssetResolver(server, filterDevStyles));
|
|
3120
3315
|
installDevManifestBridge(server);
|
|
3121
3316
|
}
|
|
3122
3317
|
if (!needHmr) return;
|
|
@@ -3232,7 +3427,7 @@ function solidPlugin(options = {}) {
|
|
|
3232
3427
|
}
|
|
3233
3428
|
},
|
|
3234
3429
|
async transform(source, id, transformOptions) {
|
|
3235
|
-
const isSsr = transformOptions
|
|
3430
|
+
const isSsr = getEnvironmentConsumer(this.environment, transformOptions) === 'server';
|
|
3236
3431
|
const currentFileExtension = getExtension(id);
|
|
3237
3432
|
const extensionsToWatch = options.extensions || [];
|
|
3238
3433
|
const allExtensions = extensionsToWatch.map(extension =>
|
|
@@ -3372,7 +3567,7 @@ function solidPlugin(options = {}) {
|
|
|
3372
3567
|
// With start mode on (either variant), the dev middleware dispatches
|
|
3373
3568
|
// the endpoint through the SSR handler so user middleware and the
|
|
3374
3569
|
// stub-backed request event front it exactly like page SSR.
|
|
3375
|
-
...(
|
|
3570
|
+
...(startOptions ? {
|
|
3376
3571
|
ssrHandler: SSR_HANDLER_ID
|
|
3377
3572
|
} : {})
|
|
3378
3573
|
}), mainPlugin] : [boundaryModules(), mainPlugin];
|
|
@@ -3380,15 +3575,16 @@ function solidPlugin(options = {}) {
|
|
|
3380
3575
|
// The `start` option opts into start-mode serving on top of the transforms;
|
|
3381
3576
|
// the `ssr` boolean picks the mode (a bare `ssr: true` keeps the
|
|
3382
3577
|
// historical transform-only behavior).
|
|
3383
|
-
if (
|
|
3578
|
+
if (startOptions) {
|
|
3384
3579
|
plugins.push(
|
|
3385
3580
|
// Typed env (`start.env`) rides both start modes: config-time
|
|
3386
3581
|
// validation, the virtual:env/{server,client} modules, generated
|
|
3387
3582
|
// types, and the client-bundle leak scan.
|
|
3388
|
-
...startEnv(
|
|
3583
|
+
...startEnv(startOptions.env), ...startServe(startOptions, {
|
|
3389
3584
|
serverFunctions: !!options.serverFunctions,
|
|
3390
3585
|
serverComponents,
|
|
3391
|
-
ssr: !!options.ssr
|
|
3586
|
+
ssr: !!options.ssr,
|
|
3587
|
+
styleFilter: filterDevStyles
|
|
3392
3588
|
}));
|
|
3393
3589
|
}
|
|
3394
3590
|
|