@solidjs/vite-plugin 3.0.0-next.30 → 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 +205 -43
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +207 -45
- 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/ssr/index.d.ts +42 -1
- package/package.json +8 -3
package/dist/esm/index.mjs
CHANGED
|
@@ -6,8 +6,8 @@ import { mergeAndConcat } from 'merge-anything';
|
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { Readable } from 'node:stream';
|
|
9
|
-
import { createFilter, loadEnv, version } from 'vite';
|
|
10
|
-
import { pathToFileURL } from 'node:url';
|
|
9
|
+
import { createFilter, normalizePath, loadEnv, version } from 'vite';
|
|
10
|
+
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
11
11
|
import { crawlFrameworkPkgs } from 'vitefu';
|
|
12
12
|
|
|
13
13
|
// Node <-> web-standard request/response bridging shared by the plugin's dev
|
|
@@ -153,6 +153,7 @@ function joinBase(base, pathname) {
|
|
|
153
153
|
* dynamically imported modules register their own styles when they render.
|
|
154
154
|
*/
|
|
155
155
|
|
|
156
|
+
const defaultStyleFilter = id => !id.includes('node_modules');
|
|
156
157
|
// The resolver is created plugin-side (it closes over the dev server) but is
|
|
157
158
|
// called from the SSR module runner, which only shares `globalThis` with the
|
|
158
159
|
// plugin when it runs in-process (the default). The primary channel is a
|
|
@@ -296,13 +297,15 @@ async function getModuleNode(env, file, importer) {
|
|
|
296
297
|
return;
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
|
-
async function collectModuleDeps(env, file, deps, crawled, onFile, importer) {
|
|
300
|
+
async function collectModuleDeps(env, file, deps, crawled, filter, onFile, importer) {
|
|
300
301
|
crawled.add(file);
|
|
301
302
|
const node = await getModuleNode(env, file, importer);
|
|
302
303
|
if (!node?.id || deps.has(node)) return;
|
|
303
304
|
deps.add(node);
|
|
304
|
-
|
|
305
|
-
if (
|
|
305
|
+
const isCss = cssFileRegExp.test(node.url.split('?')[0]);
|
|
306
|
+
if (!isCss && node.file && !node.id.startsWith('\0') && !filter(node.file)) return;
|
|
307
|
+
if (node.file) onFile?.(node.file);
|
|
308
|
+
if (isCss) return;
|
|
306
309
|
if (!node.transformResult) {
|
|
307
310
|
await env.transformRequest(node.url).catch(() => {});
|
|
308
311
|
}
|
|
@@ -313,7 +316,7 @@ async function collectModuleDeps(env, file, deps, crawled, onFile, importer) {
|
|
|
313
316
|
// from dynamicDeps — dynamic imports load their own styles when rendered.
|
|
314
317
|
for (const dep of directDeps) {
|
|
315
318
|
if (crawled.has(dep)) continue;
|
|
316
|
-
await collectModuleDeps(env, dep, deps, crawled, onFile, node.id);
|
|
319
|
+
await collectModuleDeps(env, dep, deps, crawled, filter, onFile, node.id);
|
|
317
320
|
}
|
|
318
321
|
}
|
|
319
322
|
function injectQuery(url, query) {
|
|
@@ -321,11 +324,11 @@ function injectQuery(url, query) {
|
|
|
321
324
|
}
|
|
322
325
|
|
|
323
326
|
/** Discovers ambient CSS in an entry graph without choosing how it is transported. */
|
|
324
|
-
async function collectDevStyleSources(env, files, onFile) {
|
|
327
|
+
async function collectDevStyleSources(env, files, onFile, filter = defaultStyleFilter) {
|
|
325
328
|
const deps = new Set();
|
|
326
329
|
const crawled = new Set();
|
|
327
330
|
for (const file of files) {
|
|
328
|
-
await collectModuleDeps(env, file, deps, crawled, onFile);
|
|
331
|
+
await collectModuleDeps(env, file, deps, crawled, filter, onFile);
|
|
329
332
|
}
|
|
330
333
|
const css = [];
|
|
331
334
|
const seen = new Set();
|
|
@@ -352,11 +355,11 @@ async function collectDevStyleSources(env, files, onFile) {
|
|
|
352
355
|
* CSS into `<head>` so server-painted content is styled from the first byte
|
|
353
356
|
* (no FOUC while waiting for Vite's client-side style injection).
|
|
354
357
|
*/
|
|
355
|
-
async function collectDevStyles(server, files) {
|
|
358
|
+
async function collectDevStyles(server, files, filter = defaultStyleFilter) {
|
|
356
359
|
const ssrEnv = server.environments?.ssr;
|
|
357
360
|
const clientEnv = server.environments?.client;
|
|
358
361
|
if (!ssrEnv || !clientEnv) return [];
|
|
359
|
-
const sources = await collectDevStyleSources(ssrEnv, files.map(file => path.resolve(server.config.root, file)));
|
|
362
|
+
const sources = await collectDevStyleSources(ssrEnv, files.map(file => path.resolve(server.config.root, file)), undefined, filter);
|
|
360
363
|
const css = [];
|
|
361
364
|
for (const source of sources) {
|
|
362
365
|
// `?direct` yields the compiled stylesheet text (what Vite serves for
|
|
@@ -412,7 +415,7 @@ function devModuleUrl(root, base, key) {
|
|
|
412
415
|
// drive letter on Windows: /@fs/C:/…).
|
|
413
416
|
return joinBase(base, '/@fs/' + absolute.replace(/^\//, '') + query);
|
|
414
417
|
}
|
|
415
|
-
function createDevAssetResolver(server) {
|
|
418
|
+
function createDevAssetResolver(server, filter = defaultStyleFilter) {
|
|
416
419
|
// Server-side lazy() re-requests a module's assets on every retry of a
|
|
417
420
|
// suspended render pass (retries re-create the component). The build
|
|
418
421
|
// manifest answers those repeats synchronously and the pass converges; an
|
|
@@ -446,7 +449,7 @@ function createDevAssetResolver(server) {
|
|
|
446
449
|
// The module's dev URL doubles as its client entry: modulepreload
|
|
447
450
|
// hint and hydration module-map value.
|
|
448
451
|
const js = [devModuleUrl(root, base, key)];
|
|
449
|
-
const css = await collectDevStyles(server, [key]);
|
|
452
|
+
const css = await collectDevStyles(server, [key], filter);
|
|
450
453
|
return {
|
|
451
454
|
js,
|
|
452
455
|
css
|
|
@@ -539,6 +542,11 @@ function boundaryModules() {
|
|
|
539
542
|
function isRunnableEnvironment(environment) {
|
|
540
543
|
return !!environment && typeof environment === 'object' && 'runner' in environment;
|
|
541
544
|
}
|
|
545
|
+
function getEnvironmentConsumer(environment, options) {
|
|
546
|
+
const consumer = environment?.config?.consumer;
|
|
547
|
+
if (consumer === 'client' || consumer === 'server') return consumer;
|
|
548
|
+
return options?.ssr ? 'server' : 'client';
|
|
549
|
+
}
|
|
542
550
|
|
|
543
551
|
// The `"use server"` directive compiler. This wraps the native
|
|
544
552
|
// `transformDirectives` pass from @dom-expressions/compiler (Rust/Oxc); the
|
|
@@ -975,12 +983,12 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
975
983
|
const relative = path.relative(root, entry).split(path.sep).join('/');
|
|
976
984
|
return relative.startsWith('..') ? '/@fs/' + entry : '/' + relative;
|
|
977
985
|
}
|
|
978
|
-
const
|
|
986
|
+
const startPlugins = [{
|
|
979
987
|
name: 'solid:server-functions/handler',
|
|
980
988
|
enforce: 'pre',
|
|
981
989
|
resolveId(source, _importer, opts) {
|
|
982
990
|
if (source === HANDLER_ID$1) {
|
|
983
|
-
if (
|
|
991
|
+
if (getEnvironmentConsumer(this.environment, opts) !== 'server') {
|
|
984
992
|
this.error(`${HANDLER_ID$1} is server-only; import it from your server entry (SSR build).`);
|
|
985
993
|
}
|
|
986
994
|
return {
|
|
@@ -991,7 +999,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
991
999
|
return null;
|
|
992
1000
|
},
|
|
993
1001
|
load(id, opts) {
|
|
994
|
-
if (id === HANDLER_ID$1 && opts
|
|
1002
|
+
if (id === HANDLER_ID$1 && getEnvironmentConsumer(this.environment, opts) === 'server') {
|
|
995
1003
|
const externalDev = this.environment.mode === 'dev' && (internal.externalDevServer || !isRunnableEnvironment(this.environment));
|
|
996
1004
|
return handlerModuleCode(isBuild || externalDev);
|
|
997
1005
|
}
|
|
@@ -999,7 +1007,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
999
1007
|
}
|
|
1000
1008
|
}];
|
|
1001
1009
|
if (installDevMiddleware) {
|
|
1002
|
-
|
|
1010
|
+
startPlugins.push({
|
|
1003
1011
|
name: 'solid:server-functions/dev-middleware',
|
|
1004
1012
|
apply: 'serve',
|
|
1005
1013
|
configureServer(server) {
|
|
@@ -1111,7 +1119,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1111
1119
|
return null;
|
|
1112
1120
|
},
|
|
1113
1121
|
async load(id, opts) {
|
|
1114
|
-
const mode = opts
|
|
1122
|
+
const mode = getEnvironmentConsumer(this.environment, opts);
|
|
1115
1123
|
if (id === manifestId) {
|
|
1116
1124
|
if (isBuild && mode === 'server') {
|
|
1117
1125
|
// Merge the client build's persisted discoveries at load time,
|
|
@@ -1134,7 +1142,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1134
1142
|
name: 'solid:server-functions/compiler',
|
|
1135
1143
|
enforce: 'pre',
|
|
1136
1144
|
async transform(code, fileId, opts) {
|
|
1137
|
-
const mode = opts
|
|
1145
|
+
const mode = getEnvironmentConsumer(this.environment, opts);
|
|
1138
1146
|
const [id] = fileId.split('?');
|
|
1139
1147
|
if (!filter(id)) {
|
|
1140
1148
|
return null;
|
|
@@ -1167,7 +1175,17 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1167
1175
|
}
|
|
1168
1176
|
return null;
|
|
1169
1177
|
}
|
|
1170
|
-
}, ...
|
|
1178
|
+
}, ...startPlugins];
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const DEVTOOLS_PACKAGE = '@solidjs/start-devtools';
|
|
1182
|
+
const DEVTOOLS_ID = 'virtual:solid-devtools';
|
|
1183
|
+
const DEVTOOLS_MOUNT_ID = 'virtual:solid-devtools/mount';
|
|
1184
|
+
function devtoolsModuleCode() {
|
|
1185
|
+
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');
|
|
1186
|
+
}
|
|
1187
|
+
function devtoolsMountModuleCode() {
|
|
1188
|
+
return [`import ${JSON.stringify(DEVTOOLS_ID)};`, `import { mountDevToolbar } from '${DEVTOOLS_PACKAGE}';`, `mountDevToolbar();`].join('\n');
|
|
1171
1189
|
}
|
|
1172
1190
|
|
|
1173
1191
|
// Start-mode serving for plain Vite apps: `solid({ start: {...} })` (or the
|
|
@@ -1252,6 +1270,7 @@ const RESOLVED_DEV_STYLES_ID = '\0' + DEV_STYLES_ID;
|
|
|
1252
1270
|
const ENTRY_SERVER_ID = 'virtual:solid-ssr-entry-server.tsx';
|
|
1253
1271
|
const ENTRY_CLIENT_ID = 'virtual:solid-ssr-entry-client.tsx';
|
|
1254
1272
|
const DOCUMENT_ID = 'virtual:solid-ssr-document.tsx';
|
|
1273
|
+
const ERROR_BOUNDARY_ID = 'virtual:solid-ssr-error-boundary.tsx';
|
|
1255
1274
|
const MANIFEST_ID = 'virtual:solid-manifest';
|
|
1256
1275
|
const SERVER_FUNCTION_HANDLER_ID = 'virtual:solid-server-function-handler';
|
|
1257
1276
|
const STORAGE_SOURCE = '@solidjs/web/storage';
|
|
@@ -1363,6 +1382,12 @@ function startServe(options, internal = {}) {
|
|
|
1363
1382
|
// the server-function handler module either way). Everything is gated
|
|
1364
1383
|
// codegen: with the option off, none of these imports exist anywhere.
|
|
1365
1384
|
const serverComponents = !!internal.serverComponents;
|
|
1385
|
+
const errorBoundary = options.errorBoundary !== false;
|
|
1386
|
+
const styleFilter = internal.styleFilter;
|
|
1387
|
+
let devtools = false;
|
|
1388
|
+
let devtoolsResolution;
|
|
1389
|
+
/** Resolved module id of `@solidjs/start-devtools` once detection succeeds. */
|
|
1390
|
+
let devtoolsId = null;
|
|
1366
1391
|
// `external` is server-mode-only (documented no-op in client mode, so a
|
|
1367
1392
|
// host-integrated config survives the `ssr` boolean flip untouched).
|
|
1368
1393
|
const externalServer = !clientMode && !!options.external;
|
|
@@ -1379,6 +1404,40 @@ function startServe(options, internal = {}) {
|
|
|
1379
1404
|
if (!entries) throw new Error('[@solidjs/vite-plugin] SSR entries not resolved yet');
|
|
1380
1405
|
return entries;
|
|
1381
1406
|
}
|
|
1407
|
+
async function resolveDevtools(resolve, importer) {
|
|
1408
|
+
if (devtools !== undefined) return devtools;
|
|
1409
|
+
// Detect from the app graph first (the documented install location), then
|
|
1410
|
+
// from the plugin's own file: in pnpm-isolated apps a copy that is only a
|
|
1411
|
+
// dependency of the plugin is not reachable from the app's importers. The
|
|
1412
|
+
// resolved id is kept so the virtual modules' imports of the package can
|
|
1413
|
+
// be delegated to it (see resolveId).
|
|
1414
|
+
devtoolsResolution ??= (async () => ((await resolve(DEVTOOLS_PACKAGE, importer)) ?? (await resolve(DEVTOOLS_PACKAGE, fileURLToPath(import.meta.url))))?.id ?? null)();
|
|
1415
|
+
devtoolsId = await devtoolsResolution;
|
|
1416
|
+
devtools = devtoolsId !== null;
|
|
1417
|
+
if (!devtools && options.devtools === true) {
|
|
1418
|
+
throw new Error('[@solidjs/vite-plugin] start.devtools requires @solidjs/start-devtools. ' + 'Install it as a development dependency or set start.devtools to false.');
|
|
1419
|
+
}
|
|
1420
|
+
return devtools;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* Cheap root-walk probe mirroring how the optimizer resolves bare
|
|
1425
|
+
* `optimizeDeps.include` entries: is @solidjs/start-devtools reachable from
|
|
1426
|
+
* the Vite root? Detection proper (resolveDevtools) runs later with a real
|
|
1427
|
+
* importer; this only decides whether the toolbar graph can be pre-bundled
|
|
1428
|
+
* at scan time — it hangs off virtual modules the scanner never sees, so
|
|
1429
|
+
* first-request discovery would force a re-optimize + full page reload.
|
|
1430
|
+
*/
|
|
1431
|
+
function devtoolsReachableFromRoot(dir) {
|
|
1432
|
+
for (let current = dir;;) {
|
|
1433
|
+
if (existsSync(path.join(current, 'node_modules', DEVTOOLS_PACKAGE, 'package.json'))) {
|
|
1434
|
+
return true;
|
|
1435
|
+
}
|
|
1436
|
+
const parent = path.dirname(current);
|
|
1437
|
+
if (parent === current) return false;
|
|
1438
|
+
current = parent;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1382
1441
|
|
|
1383
1442
|
/** Import specifier for generated code: absolute for files, id for virtuals. */
|
|
1384
1443
|
function entryServerSpec() {
|
|
@@ -1418,7 +1477,7 @@ function startServe(options, internal = {}) {
|
|
|
1418
1477
|
return generated ? [app, ...(document ? [document] : [])] : [path.resolve(root, entryServer)];
|
|
1419
1478
|
}
|
|
1420
1479
|
async function devStylesModuleCode(environment, watchFile) {
|
|
1421
|
-
const styles = await collectDevStyleSources(environment, styleRoots(), watchFile);
|
|
1480
|
+
const styles = await collectDevStyleSources(environment, styleRoots(), watchFile, styleFilter);
|
|
1422
1481
|
if (!styles.length) return `export default '';`;
|
|
1423
1482
|
const imports = styles.map((style, index) => {
|
|
1424
1483
|
const specifier = style.url.includes('?') ? `${style.url}&inline` : `${style.url}?inline`;
|
|
@@ -1426,19 +1485,26 @@ function startServe(options, internal = {}) {
|
|
|
1426
1485
|
});
|
|
1427
1486
|
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');
|
|
1428
1487
|
}
|
|
1488
|
+
function errorBoundaryImport() {
|
|
1489
|
+
return isBuild && errorBoundary ? [`import { DefaultErrorBoundary } from ${JSON.stringify(ERROR_BOUNDARY_ID)};`] : [];
|
|
1490
|
+
}
|
|
1491
|
+
function documentTree(root, wrapper) {
|
|
1492
|
+
const content = wrapper ? `<${wrapper}><${root} /></${wrapper}>` : `<${root} />`;
|
|
1493
|
+
return isBuild && errorBoundary ? [` <DefaultErrorBoundary>`, ` <Document>`, ` <DefaultErrorBoundary>`, ` ${content}`, ` </DefaultErrorBoundary>`, ` </Document>`, ` </DefaultErrorBoundary>`] : [` <Document>`, ` ${content}`, ` </Document>`];
|
|
1494
|
+
}
|
|
1429
1495
|
function generatedEntryServerCode() {
|
|
1430
1496
|
if (clientMode) {
|
|
1431
1497
|
// The client-mode shell: the document without the app. Rendered per
|
|
1432
1498
|
// request in dev (any HTML GET gets it — history-fallback semantics)
|
|
1433
1499
|
// and once at build time into dist/client/index.html. The client
|
|
1434
1500
|
// entry script is injected by the handler, exactly like SSR mode.
|
|
1435
|
-
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
|
|
1501
|
+
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');
|
|
1436
1502
|
}
|
|
1437
1503
|
const {
|
|
1438
1504
|
app
|
|
1439
1505
|
} = requireEntries();
|
|
1440
1506
|
const streamOptions = `{ manifest${serverComponents ? ', plugins: [ServerComponentPlugin]' : ''} }`;
|
|
1441
|
-
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 ? [
|
|
1507
|
+
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 ? [
|
|
1442
1508
|
// Direct (in-process) server-function calls made during document
|
|
1443
1509
|
// SSR must resolve to inline-renderable components; the endpoint
|
|
1444
1510
|
// response transform is installed separately by the
|
|
@@ -1452,9 +1518,9 @@ function startServe(options, internal = {}) {
|
|
|
1452
1518
|
// the *complete* render) and buffers the stream — so it crosses
|
|
1453
1519
|
// boxed under a private key the generated handler unboxes
|
|
1454
1520
|
// (both modules are ours).
|
|
1455
|
-
`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(() => (`,
|
|
1521
|
+
`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');
|
|
1456
1522
|
}
|
|
1457
|
-
function generatedEntryClientCode() {
|
|
1523
|
+
function generatedEntryClientCode(toolbar) {
|
|
1458
1524
|
const {
|
|
1459
1525
|
app
|
|
1460
1526
|
} = requireEntries();
|
|
@@ -1464,13 +1530,13 @@ function startServe(options, internal = {}) {
|
|
|
1464
1530
|
// app cannot claim server DOM anyway. The entry script is injected
|
|
1465
1531
|
// without `async` (plain module = deferred), so document.body is
|
|
1466
1532
|
// complete when this runs.
|
|
1467
|
-
return [`import { render } from '@solidjs/web';`, `import App from ${JSON.stringify(app)};`, ``, `render(() => <App
|
|
1533
|
+
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');
|
|
1468
1534
|
}
|
|
1469
|
-
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 ? [
|
|
1535
|
+
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 ? [
|
|
1470
1536
|
// Installs the t=0 document-adoption registry and the transport
|
|
1471
1537
|
// policy (component responses morph their boundary instead of
|
|
1472
1538
|
// decoding as data). Must run before hydrate().
|
|
1473
|
-
`installServerComponents();`, ``] : []), `hydrate(() => (`,
|
|
1539
|
+
`installServerComponents();`, ``] : []), `hydrate(() => (`, ...documentTree('App', toolbar ? 'DevToolbar' : undefined), `), document);`].join('\n');
|
|
1474
1540
|
}
|
|
1475
1541
|
|
|
1476
1542
|
// Built-in document shell: minimal, hydration-ready. The client entry
|
|
@@ -1481,6 +1547,7 @@ function startServe(options, internal = {}) {
|
|
|
1481
1547
|
// HydrationScript is covered too: the handler strips the event-capture
|
|
1482
1548
|
// script from the client-mode shell.)
|
|
1483
1549
|
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');
|
|
1550
|
+
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');
|
|
1484
1551
|
|
|
1485
1552
|
// The handler module: dev and prod share the render/response plumbing;
|
|
1486
1553
|
// they differ in how the client entry URL is known (baked dev URL vs a
|
|
@@ -1532,7 +1599,7 @@ function startServe(options, internal = {}) {
|
|
|
1532
1599
|
// head-open splice actively broke hydration — a script ahead of the
|
|
1533
1600
|
// authored <head> elements claims as the first walked child and drifts
|
|
1534
1601
|
// every positional claim after it.
|
|
1535
|
-
lines.push(``, `function createHtmlChunkTransform(clientEntry, extraHead) {`, ` let first = true;`, ` let injected = false;`, ` return (chunk) => {`);
|
|
1602
|
+
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) => {`);
|
|
1536
1603
|
if (!generated) {
|
|
1537
1604
|
// Authored entries reference the client entry by its dev path (the
|
|
1538
1605
|
// `<script src="/src/entry-client.tsx">` convention); rewrite it to
|
|
@@ -1562,7 +1629,7 @@ function startServe(options, internal = {}) {
|
|
|
1562
1629
|
// scripts default to deferred execution, which is exactly right for a
|
|
1563
1630
|
// fresh render-into-body mount (hydration, by contrast, wants to
|
|
1564
1631
|
// start as early as possible).
|
|
1565
|
-
headParts.push(`(clientEntry ? '<script type="module" src="' + clientEntry + '"${clientMode ? '' : ' async'}></' + 'script>' : '')`);
|
|
1632
|
+
headParts.push(`(clientEntry ? '<script type="module"' + nonceAttr + ' src="' + clientEntry + '"${clientMode ? '' : ' async'}></' + 'script>' : '')`);
|
|
1566
1633
|
}
|
|
1567
1634
|
if (headParts.length) {
|
|
1568
1635
|
lines.push(` chunk = chunk.replace('</head>', ${headParts.join(' + ')} + '</head>');`);
|
|
@@ -1616,7 +1683,7 @@ function startServe(options, internal = {}) {
|
|
|
1616
1683
|
// The runtime's response-head lifecycle: commit at shell flush,
|
|
1617
1684
|
// pre-flush Location as a real redirect, post-flush Location as the
|
|
1618
1685
|
// script fallback; the transform injects the doctype/head pieces.
|
|
1619
|
-
` return createSSRResponse(result, event, {`, ` responseInit: options.responseInit,`, ` nonce: options.nonce,`, ` transformChunk: createHtmlChunkTransform(clientEntry, options.devHead),`, ` });`, `}`, ``, `export async function handleRequest(request, options = {}) {`,
|
|
1686
|
+
` return createSSRResponse(result, event, {`, ` responseInit: options.responseInit,`, ` nonce: options.nonce,`, ` transformChunk: createHtmlChunkTransform(clientEntry, options.devHead, options.nonce),`, ` });`, `}`, ``, `export async function handleRequest(request, options = {}) {`,
|
|
1620
1687
|
// `options.event` is the public wrapper->event extension seam: extra
|
|
1621
1688
|
// fields (conventionally `nativeEvent`, the platform's raw request
|
|
1622
1689
|
// object) spread over the event's defaults at creation, so hosts and
|
|
@@ -1646,6 +1713,9 @@ function startServe(options, internal = {}) {
|
|
|
1646
1713
|
enforce: 'pre',
|
|
1647
1714
|
config(userConfig, env) {
|
|
1648
1715
|
root = path.resolve(userConfig.root || process.cwd());
|
|
1716
|
+
devtools = env.command === 'serve' && !env.isPreview && options.devtools !== false ? undefined : false;
|
|
1717
|
+
devtoolsResolution = undefined;
|
|
1718
|
+
devtoolsId = null;
|
|
1649
1719
|
entries = resolveEntries(root, options, clientMode);
|
|
1650
1720
|
middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
|
|
1651
1721
|
// Server-mode only, like `entryServer`/`external` (a documented
|
|
@@ -1764,7 +1834,15 @@ function startServe(options, internal = {}) {
|
|
|
1764
1834
|
}
|
|
1765
1835
|
} : {}),
|
|
1766
1836
|
optimizeDeps: {
|
|
1767
|
-
entries: scanEntries
|
|
1837
|
+
entries: scanEntries,
|
|
1838
|
+
// Like the refresh runtime in the main plugin: the toolbar
|
|
1839
|
+
// graph is injected behind virtual modules the scanner never
|
|
1840
|
+
// crawls, so pre-bundle it (and the server-functions runtime
|
|
1841
|
+
// the virtual module pulls in) up front — first-request
|
|
1842
|
+
// discovery would re-optimize and full-reload the page.
|
|
1843
|
+
...(devtools === undefined && devtoolsReachableFromRoot(root) ? {
|
|
1844
|
+
include: [DEVTOOLS_PACKAGE, '@solidjs/web/server-functions']
|
|
1845
|
+
} : {})
|
|
1768
1846
|
}
|
|
1769
1847
|
})
|
|
1770
1848
|
};
|
|
@@ -1774,7 +1852,7 @@ function startServe(options, internal = {}) {
|
|
|
1774
1852
|
base = config.base;
|
|
1775
1853
|
isBuild = config.command === 'build';
|
|
1776
1854
|
},
|
|
1777
|
-
resolveId(source) {
|
|
1855
|
+
resolveId(source, importer) {
|
|
1778
1856
|
if (source === HANDLER_ID) {
|
|
1779
1857
|
return {
|
|
1780
1858
|
id: HANDLER_ID,
|
|
@@ -1787,33 +1865,94 @@ function startServe(options, internal = {}) {
|
|
|
1787
1865
|
moduleSideEffects: true
|
|
1788
1866
|
};
|
|
1789
1867
|
}
|
|
1790
|
-
if (source === ENTRY_SERVER_ID || source === ENTRY_CLIENT_ID || source === DOCUMENT_ID) {
|
|
1868
|
+
if (source === ENTRY_SERVER_ID || source === ENTRY_CLIENT_ID || source === DOCUMENT_ID || source === ERROR_BOUNDARY_ID) {
|
|
1791
1869
|
return {
|
|
1792
1870
|
id: source,
|
|
1793
1871
|
moduleSideEffects: source === ENTRY_CLIENT_ID
|
|
1794
1872
|
};
|
|
1795
1873
|
}
|
|
1874
|
+
if (devtools && (source === DEVTOOLS_ID || source === DEVTOOLS_MOUNT_ID)) {
|
|
1875
|
+
return {
|
|
1876
|
+
id: source,
|
|
1877
|
+
moduleSideEffects: true
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
// The virtual devtools modules import the package by its bare name,
|
|
1881
|
+
// but a virtual importer gives Vite no directory to walk, so the
|
|
1882
|
+
// specifier would only resolve from the Vite root — which fails in
|
|
1883
|
+
// pnpm-isolated apps where the package is not a root-level install.
|
|
1884
|
+
// Delegate to the resolution captured at detection time instead.
|
|
1885
|
+
if (devtoolsId && source === DEVTOOLS_PACKAGE && (importer === DEVTOOLS_ID || importer === DEVTOOLS_MOUNT_ID)) {
|
|
1886
|
+
return {
|
|
1887
|
+
id: devtoolsId
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1796
1890
|
return null;
|
|
1797
1891
|
},
|
|
1798
1892
|
async load(id, opts) {
|
|
1893
|
+
const consumer = getEnvironmentConsumer(this.environment, opts);
|
|
1799
1894
|
if (id === HANDLER_ID) {
|
|
1800
|
-
if (
|
|
1895
|
+
if (consumer !== 'server') {
|
|
1801
1896
|
this.error(`${HANDLER_ID} is server-only; import it from server code (SSR build).`);
|
|
1802
1897
|
}
|
|
1803
1898
|
const externalDev = !isBuild && this.environment.mode === 'dev' && (externalServer || !isRunnableEnvironment(this.environment));
|
|
1804
1899
|
return handlerModuleCode(externalDev);
|
|
1805
1900
|
}
|
|
1806
1901
|
if (id === RESOLVED_DEV_STYLES_ID) {
|
|
1807
|
-
if (
|
|
1902
|
+
if (consumer !== 'server' || this.environment.mode !== 'dev') {
|
|
1808
1903
|
this.error(`${DEV_STYLES_ID} is only available to the development server handler.`);
|
|
1809
1904
|
}
|
|
1810
1905
|
return devStylesModuleCode(this.environment, file => this.addWatchFile(file));
|
|
1811
1906
|
}
|
|
1812
1907
|
if (id === ENTRY_SERVER_ID) return generatedEntryServerCode();
|
|
1813
|
-
if (id === ENTRY_CLIENT_ID)
|
|
1908
|
+
if (id === ENTRY_CLIENT_ID) {
|
|
1909
|
+
const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1910
|
+
skipSelf: true
|
|
1911
|
+
}), requireEntries().app);
|
|
1912
|
+
return generatedEntryClientCode(toolbar);
|
|
1913
|
+
}
|
|
1814
1914
|
if (id === DOCUMENT_ID) return documentShellCode;
|
|
1915
|
+
if (id === ERROR_BOUNDARY_ID) return errorBoundaryCode;
|
|
1916
|
+
if (id === DEVTOOLS_ID || id === DEVTOOLS_MOUNT_ID) {
|
|
1917
|
+
// A cold direct request (stale tab reload) can reach the virtual
|
|
1918
|
+
// module before the entry has triggered detection — run it here so
|
|
1919
|
+
// first-touch order doesn't matter.
|
|
1920
|
+
if (!isBuild && consumer === 'client' && devtools === undefined) {
|
|
1921
|
+
const {
|
|
1922
|
+
app,
|
|
1923
|
+
entryClient
|
|
1924
|
+
} = requireEntries();
|
|
1925
|
+
await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1926
|
+
skipSelf: true
|
|
1927
|
+
}), app ?? path.resolve(root, entryClient));
|
|
1928
|
+
}
|
|
1929
|
+
if (isBuild || !devtools || consumer !== 'client') {
|
|
1930
|
+
this.error(`${id} is only available to the development client.`);
|
|
1931
|
+
}
|
|
1932
|
+
return id === DEVTOOLS_ID ? devtoolsModuleCode() : devtoolsMountModuleCode();
|
|
1933
|
+
}
|
|
1815
1934
|
return null;
|
|
1816
1935
|
},
|
|
1936
|
+
async transform(code, id, opts) {
|
|
1937
|
+
if (isBuild || devtools === false) return null;
|
|
1938
|
+
const current = requireEntries();
|
|
1939
|
+
if (current.generated || getEnvironmentConsumer(this.environment, opts) !== 'client') {
|
|
1940
|
+
return null;
|
|
1941
|
+
}
|
|
1942
|
+
// Module ids are always forward-slashed; normalize the path.resolve
|
|
1943
|
+
// side too so the comparison holds on Windows.
|
|
1944
|
+
if (normalizePath(id.split('?')[0]) !== normalizePath(path.resolve(root, current.entryClient))) {
|
|
1945
|
+
return null;
|
|
1946
|
+
}
|
|
1947
|
+
const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
|
|
1948
|
+
skipSelf: true
|
|
1949
|
+
}), id);
|
|
1950
|
+
if (!toolbar) return null;
|
|
1951
|
+
return {
|
|
1952
|
+
code: `import ${JSON.stringify(DEVTOOLS_MOUNT_ID)};\n${code}`,
|
|
1953
|
+
map: null
|
|
1954
|
+
};
|
|
1955
|
+
},
|
|
1817
1956
|
configurePreviewServer(server) {
|
|
1818
1957
|
// `vite build && vite preview` runs the production artifact as-is:
|
|
1819
1958
|
// Vite's preview statics serve dist/client (see the config hook) and
|
|
@@ -1889,7 +2028,7 @@ function startServe(options, internal = {}) {
|
|
|
1889
2028
|
// Loaded through the SSR environment so the app, the request
|
|
1890
2029
|
// event storage, and the handler share one module registry.
|
|
1891
2030
|
const handler = await server.ssrLoadModule(HANDLER_ID);
|
|
1892
|
-
const styles = pageRequest ? await collectDevStyles(server, styleRoots()) : [];
|
|
2031
|
+
const styles = pageRequest ? await collectDevStyles(server, styleRoots(), styleFilter) : [];
|
|
1893
2032
|
const devHead = styles.map(renderDevStyleTag).join('');
|
|
1894
2033
|
// Post middlewares run after Vite's base middleware stripped
|
|
1895
2034
|
// the configured `base` from req.url; restore it so the app
|
|
@@ -2578,6 +2717,7 @@ const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
|
|
|
2578
2717
|
const REFRESH_RUNTIME_SOURCE = 'solid-js/refresh';
|
|
2579
2718
|
const viteVersionMajor = +version.split('.')[0];
|
|
2580
2719
|
const isVite8 = viteVersionMajor >= 8;
|
|
2720
|
+
const DEFAULT_STYLE_EXCLUDE = /node_modules/;
|
|
2581
2721
|
const VIRTUAL_MANIFEST_ID = 'virtual:solid-manifest';
|
|
2582
2722
|
const RESOLVED_VIRTUAL_MANIFEST_ID = '\0' + VIRTUAL_MANIFEST_ID;
|
|
2583
2723
|
|
|
@@ -2832,10 +2972,30 @@ function solidPlugin(options = {}) {
|
|
|
2832
2972
|
// `start: true` is sugar for the empty options bag — one start mode,
|
|
2833
2973
|
// two spellings — so normalize here and let everything downstream see a
|
|
2834
2974
|
// single shape (`false` behaves exactly like omission).
|
|
2835
|
-
const
|
|
2975
|
+
const startOptions = options.start === true ? {} : options.start || null;
|
|
2976
|
+
const styleFilterOptions = startOptions?.css?.filter;
|
|
2977
|
+
// The CSS crawl walks the module graph from the app's own entries, so a
|
|
2978
|
+
// plain createFilter allowlist can't express the option's purpose (opting
|
|
2979
|
+
// node_modules graphs in): a bare `include` would reject the app sources
|
|
2980
|
+
// the crawl has to traverse to ever reach the included package. Instead
|
|
2981
|
+
// `include` rescues files on top of the baseline (everything except
|
|
2982
|
+
// `exclude`, which defaults to node_modules), while a file matching both
|
|
2983
|
+
// patterns stays excluded — createFilter's own conflict rule.
|
|
2984
|
+
const createStyleFilter = resolve => {
|
|
2985
|
+
const opts = resolve === undefined ? undefined : {
|
|
2986
|
+
resolve
|
|
2987
|
+
};
|
|
2988
|
+
const base = createFilter(undefined, styleFilterOptions?.exclude ?? DEFAULT_STYLE_EXCLUDE, opts);
|
|
2989
|
+
const include = styleFilterOptions?.include;
|
|
2990
|
+
const hasInclude = include != null && (!Array.isArray(include) || include.length > 0);
|
|
2991
|
+
const included = hasInclude ? createFilter(include, styleFilterOptions?.exclude, opts) : null;
|
|
2992
|
+
return id => base(id) || (included ? included(id) : false);
|
|
2993
|
+
};
|
|
2994
|
+
let styleFilter = createStyleFilter();
|
|
2995
|
+
const filterDevStyles = id => styleFilter(id);
|
|
2836
2996
|
// `start.external` only means something when a server side exists to hand
|
|
2837
2997
|
// over (SSR start mode); in client mode it is a documented no-op.
|
|
2838
|
-
const externalDevServer = !!options.ssr && !!
|
|
2998
|
+
const externalDevServer = !!options.ssr && !!startOptions?.external;
|
|
2839
2999
|
let needHmr = false;
|
|
2840
3000
|
let replaceDev = false;
|
|
2841
3001
|
// The live dev server, kept so the dev manifest module can bake the bridge
|
|
@@ -3113,6 +3273,7 @@ function solidPlugin(options = {}) {
|
|
|
3113
3273
|
filter = createFilter(options.include, options.exclude, {
|
|
3114
3274
|
resolve: projectRoot
|
|
3115
3275
|
});
|
|
3276
|
+
styleFilter = createStyleFilter(projectRoot);
|
|
3116
3277
|
if (serverComponents && !(options.start && options.ssr)) {
|
|
3117
3278
|
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.');
|
|
3118
3279
|
}
|
|
@@ -3126,7 +3287,7 @@ function solidPlugin(options = {}) {
|
|
|
3126
3287
|
// that don't share globals with this process, through the HTTP bridge
|
|
3127
3288
|
// endpoint the middleware serves.
|
|
3128
3289
|
if (options.ssr || options.start) {
|
|
3129
|
-
registerDevAssetResolver(server.config.root, createDevAssetResolver(server));
|
|
3290
|
+
registerDevAssetResolver(server.config.root, createDevAssetResolver(server, filterDevStyles));
|
|
3130
3291
|
installDevManifestBridge(server);
|
|
3131
3292
|
}
|
|
3132
3293
|
if (!needHmr) return;
|
|
@@ -3242,7 +3403,7 @@ function solidPlugin(options = {}) {
|
|
|
3242
3403
|
}
|
|
3243
3404
|
},
|
|
3244
3405
|
async transform(source, id, transformOptions) {
|
|
3245
|
-
const isSsr = transformOptions
|
|
3406
|
+
const isSsr = getEnvironmentConsumer(this.environment, transformOptions) === 'server';
|
|
3246
3407
|
const currentFileExtension = getExtension(id);
|
|
3247
3408
|
const extensionsToWatch = options.extensions || [];
|
|
3248
3409
|
const allExtensions = extensionsToWatch.map(extension =>
|
|
@@ -3382,7 +3543,7 @@ function solidPlugin(options = {}) {
|
|
|
3382
3543
|
// With start mode on (either variant), the dev middleware dispatches
|
|
3383
3544
|
// the endpoint through the SSR handler so user middleware and the
|
|
3384
3545
|
// stub-backed request event front it exactly like page SSR.
|
|
3385
|
-
...(
|
|
3546
|
+
...(startOptions ? {
|
|
3386
3547
|
ssrHandler: SSR_HANDLER_ID
|
|
3387
3548
|
} : {})
|
|
3388
3549
|
}), mainPlugin] : [boundaryModules(), mainPlugin];
|
|
@@ -3390,15 +3551,16 @@ function solidPlugin(options = {}) {
|
|
|
3390
3551
|
// The `start` option opts into start-mode serving on top of the transforms;
|
|
3391
3552
|
// the `ssr` boolean picks the mode (a bare `ssr: true` keeps the
|
|
3392
3553
|
// historical transform-only behavior).
|
|
3393
|
-
if (
|
|
3554
|
+
if (startOptions) {
|
|
3394
3555
|
plugins.push(
|
|
3395
3556
|
// Typed env (`start.env`) rides both start modes: config-time
|
|
3396
3557
|
// validation, the virtual:env/{server,client} modules, generated
|
|
3397
3558
|
// types, and the client-bundle leak scan.
|
|
3398
|
-
...startEnv(
|
|
3559
|
+
...startEnv(startOptions.env), ...startServe(startOptions, {
|
|
3399
3560
|
serverFunctions: !!options.serverFunctions,
|
|
3400
3561
|
serverComponents,
|
|
3401
|
-
ssr: !!options.ssr
|
|
3562
|
+
ssr: !!options.ssr,
|
|
3563
|
+
styleFilter: filterDevStyles
|
|
3402
3564
|
}));
|
|
3403
3565
|
}
|
|
3404
3566
|
|