@solidjs/vite-plugin 3.0.0-next.39 → 3.0.0-next.40
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 +67 -2
- package/dist/cjs/index.cjs +341 -37
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +342 -38
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/src/ssr/index.d.ts +37 -0
- package/package.json +3 -3
- package/virtual-solid-manifest.d.ts +10 -0
package/README.md
CHANGED
|
@@ -180,8 +180,8 @@ same server functions.
|
|
|
180
180
|
The object form carries the options (`start: true` is pure sugar for
|
|
181
181
|
`start: {}` — both mean the identical start mode with defaults, and
|
|
182
182
|
`false`/absent means off): `app`, `document`, `entryServer`, `entryClient`,
|
|
183
|
-
`middleware`, `setup`, `env`, `devtools`, `errorBoundary`,
|
|
184
|
-
all documented below.
|
|
183
|
+
`middleware`, `setup`, `renderMode`, `env`, `devtools`, `errorBoundary`,
|
|
184
|
+
`css`, `external`, all documented below.
|
|
185
185
|
|
|
186
186
|
Install `@solidjs/start-devtools` as a development dependency to add the
|
|
187
187
|
development toolbar with runtime errors and server function calls:
|
|
@@ -376,6 +376,71 @@ whatever the hook renders must be matched client-side for hydration —
|
|
|
376
376
|
routers that own both sides (their client entry re-creates the router and
|
|
377
377
|
hydrates the same tree) fit naturally.
|
|
378
378
|
|
|
379
|
+
**`renderMode`** — how a page render becomes a response body: `'stream'`
|
|
380
|
+
(the default) or `'async'`, or a module path deciding per request.
|
|
381
|
+
|
|
382
|
+
Streaming flushes the document shell as soon as it is ready, with every
|
|
383
|
+
`<Loading>` fallback in place, and streams the boundaries' content behind it
|
|
384
|
+
in later chunks; inline scripts swap that content into the page as it
|
|
385
|
+
arrives. That is the best time-to-first-byte a server render can have, but a
|
|
386
|
+
client that never runs JavaScript — a crawler, `curl`, a browser with
|
|
387
|
+
scripts disabled — is left looking at the fallbacks forever
|
|
388
|
+
([solidjs/solid#3280](https://github.com/solidjs/solid/issues/3280)).
|
|
389
|
+
`'async'` is the other end of that trade: the handler awaits the render until
|
|
390
|
+
every boundary has settled and sends one complete document.
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
solid({ start: { renderMode: 'async' }, ssr: true });
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Because nothing has flushed when a boundary resolves, its content is spliced
|
|
397
|
+
in place of its placeholder — the document carries no fallback markup, no
|
|
398
|
+
swap templates, no swap scripts — while hydration data still serializes
|
|
399
|
+
exactly as before, so JavaScript clients hydrate the settled document the
|
|
400
|
+
same way they hydrate a streamed one. The tradeoffs are inherent: the
|
|
401
|
+
response waits for the slowest boundary before its first byte, and the whole
|
|
402
|
+
page buffers in memory before it goes out. Two consequences worth knowing:
|
|
403
|
+
`deferStream` is moot under `'async'` (everything defers), and a `Location`
|
|
404
|
+
header written mid-render — the post-flush script redirect in stream mode —
|
|
405
|
+
becomes a real 3xx with no body, which is exactly what a no-JS client needs.
|
|
406
|
+
|
|
407
|
+
Most apps want streaming for browsers and a complete document for the few
|
|
408
|
+
clients that cannot run the swap. The per-request form is a module path
|
|
409
|
+
(relative to the Vite root, following the `middleware`/`setup` convention
|
|
410
|
+
— a Vite config cannot serialize a closure into the generated handler)
|
|
411
|
+
default-exporting `(event) => 'stream' | 'async' | Promise<'stream' |
|
|
412
|
+
'async'>`. It runs inside the request scope after the middleware chain, so
|
|
413
|
+
`event.locals` is decorated by the time it decides:
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
// vite.config.ts
|
|
417
|
+
solid({ start: { renderMode: './src/render-mode.ts' }, ssr: true });
|
|
418
|
+
|
|
419
|
+
// src/render-mode.ts
|
|
420
|
+
import type { RequestEvent } from '@solidjs/web';
|
|
421
|
+
|
|
422
|
+
const CRAWLER = /Googlebot|bingbot|DuckDuckBot|Slurp|Baiduspider|YandexBot/i;
|
|
423
|
+
|
|
424
|
+
export default function renderMode(event: RequestEvent) {
|
|
425
|
+
const { request } = event;
|
|
426
|
+
if (new URL(request.url).searchParams.has('nojs')) return 'async';
|
|
427
|
+
if (CRAWLER.test(request.headers.get('user-agent') ?? '')) return 'async';
|
|
428
|
+
return 'stream';
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Hosts driving the handler directly can decide per call instead:
|
|
433
|
+
`handleRequest(request, { renderMode: 'async' })`. Precedence is that
|
|
434
|
+
runtime option, then the module function's result, then the static config;
|
|
435
|
+
an unknown value from any of the three is an error naming its source. The
|
|
436
|
+
mode applies to generated and authored entries alike — an authored
|
|
437
|
+
`render()` returning a `renderToStream` result is awaited the same way (and
|
|
438
|
+
in production its client-entry reference is still rewritten). `httpStatus()` /
|
|
439
|
+
`httpHeader()` declarations survive either mode: the runtime freezes the
|
|
440
|
+
response head when the awaited render completes (`@solidjs/web` 2.0.0-rc.7+),
|
|
441
|
+
just as streaming freezes it at shell flush. Server mode only — in client mode the served shell has no boundaries to
|
|
442
|
+
settle, so the option is a documented no-op there.
|
|
443
|
+
|
|
379
444
|
**`env`** — first-party typed environment variables. A schema file at the
|
|
380
445
|
project root — `env.ts` (or `env.js`), probed automatically; point
|
|
381
446
|
elsewhere with `start: { env: './path' }`, disable with `env: false` —
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var mergeAnything = require('merge-anything');
|
|
|
10
10
|
var module$1 = require('module');
|
|
11
11
|
var path = require('path');
|
|
12
12
|
var node_stream = require('node:stream');
|
|
13
|
+
var crypto = require('crypto');
|
|
13
14
|
var vite = require('vite');
|
|
14
15
|
var node_url = require('node:url');
|
|
15
16
|
var vitefu = require('vitefu');
|
|
@@ -757,6 +758,22 @@ function solidDiagnostics(mode = 'auto') {
|
|
|
757
758
|
apply(_config, env) {
|
|
758
759
|
return env.command === 'serve' && !env.isPreview && env.mode !== 'test';
|
|
759
760
|
},
|
|
761
|
+
// The bridge module is virtual and reaches the page behind the
|
|
762
|
+
// scanner's back (a script injected into index.html at transform time,
|
|
763
|
+
// or an import the generated start entry adds), so the optimizer never
|
|
764
|
+
// sees its two package imports up front. Pre-bundle them: otherwise the
|
|
765
|
+
// first page load discovers them, re-optimizes and full-reloads — a
|
|
766
|
+
// flash at best, and a broken page whenever anything else on the page
|
|
767
|
+
// was resolved against the first optimizer pass.
|
|
768
|
+
config(userConfig) {
|
|
769
|
+
const rootDir = path.resolve(userConfig.root || process.cwd());
|
|
770
|
+
if (mode !== true && !detectDiagnosticsPackage(rootDir)) return;
|
|
771
|
+
return {
|
|
772
|
+
optimizeDeps: {
|
|
773
|
+
include: [`${DIAGNOSTICS_PACKAGE}/browser`, `${DIAGNOSTICS_PACKAGE}/protocol`]
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
},
|
|
760
777
|
configResolved(config) {
|
|
761
778
|
root = config.root;
|
|
762
779
|
base = config.base;
|
|
@@ -1253,6 +1270,23 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1253
1270
|
client: undefined
|
|
1254
1271
|
};
|
|
1255
1272
|
let currentServer;
|
|
1273
|
+
|
|
1274
|
+
// THE DEPLOYMENT SECRET (solidjs/solid#3239): the runtime's flash cookie
|
|
1275
|
+
// — the no-JS form outcome — carries the submitted input, so it is
|
|
1276
|
+
// AES-GCM encrypted under a key derived from a deployment-wide secret,
|
|
1277
|
+
// and without one the outcome is withheld entirely. This plugin provides
|
|
1278
|
+
// that secret with zero configuration through the internal
|
|
1279
|
+
// `globalThis.__SOLID_SECRET__ ??=` contract: generated once per plugin
|
|
1280
|
+
// instance, so a production build bakes one value into the emitted server
|
|
1281
|
+
// chunk — every instance of that deployment shares it (a per-process
|
|
1282
|
+
// value would silently lose flashes behind a load balancer) — and a dev
|
|
1283
|
+
// session holds one for its lifetime (a restart invalidates in-flight
|
|
1284
|
+
// flashes, which are 60-second one-shot cookies; the next render just
|
|
1285
|
+
// reads "no flash"). Server output only, never the client graph. The
|
|
1286
|
+
// `??=` keeps an explicit `configureServerFunctionsServer({ secret })` —
|
|
1287
|
+
// or a value injected by an outer harness — authoritative.
|
|
1288
|
+
const deploymentSecret = crypto.randomBytes(32).toString('hex');
|
|
1289
|
+
const deploymentSecretSnippet = `globalThis.__SOLID_SECRET__ ??= ${JSON.stringify(deploymentSecret)};`;
|
|
1256
1290
|
const clientOptions = {
|
|
1257
1291
|
directive,
|
|
1258
1292
|
definitions: {
|
|
@@ -1312,6 +1346,14 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1312
1346
|
// import is only emitted when the option is on, so disabled setups keep
|
|
1313
1347
|
// a server-component-free graph.
|
|
1314
1348
|
return [
|
|
1349
|
+
// The deployment secret. Imports are hoisted above it, but nothing
|
|
1350
|
+
// reads the global at module evaluation — the runtime resolves it
|
|
1351
|
+
// lazily per encode/decode — so leading textually is just the honest
|
|
1352
|
+
// placement. This module is loaded before any dispatch on both
|
|
1353
|
+
// surfaces, and the generated SSR handler imports it at module load,
|
|
1354
|
+
// so the secret is in place for the flash's encode (the form POST)
|
|
1355
|
+
// and its decode (the render that follows the redirect) alike.
|
|
1356
|
+
deploymentSecretSnippet,
|
|
1315
1357
|
// The user's `configure` module comes first: a side-effect import in
|
|
1316
1358
|
// the handler graph, evaluated before any dispatch on both surfaces
|
|
1317
1359
|
// (dev middleware and prod handler) and bundled into the handler
|
|
@@ -1641,6 +1683,11 @@ function devtoolsMountModuleCode() {
|
|
|
1641
1683
|
// it, and page responses go through the runtime's `createSSRResponse`
|
|
1642
1684
|
// head lifecycle (commit at shell flush, real pre-flush redirects, the
|
|
1643
1685
|
// script fallback post-flush).
|
|
1686
|
+
// - `start.renderMode` decides how a page render becomes a body: `'stream'`
|
|
1687
|
+
// (default) flushes the shell with fallbacks and streams boundaries in
|
|
1688
|
+
// behind it; `'async'` awaits the settled document (no fallbacks or swap
|
|
1689
|
+
// scripts — complete for no-JS clients); a module path decides per
|
|
1690
|
+
// request, and `handleRequest(request, { renderMode })` overrides both.
|
|
1644
1691
|
// - `vite preview` serves dist/client statically and dispatches everything
|
|
1645
1692
|
// else through the built handler — the production path, middleware
|
|
1646
1693
|
// included, with no server file needed.
|
|
@@ -1716,6 +1763,37 @@ function normalizeUserPath(root, spec, option) {
|
|
|
1716
1763
|
}
|
|
1717
1764
|
return relative;
|
|
1718
1765
|
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Resolves `start.renderMode` at config time: a literal mode, or the
|
|
1768
|
+
* absolute path of a per-request module (`mode` stays the `'stream'`
|
|
1769
|
+
* default then; the module decides per request). Anything else is a
|
|
1770
|
+
* config error with the fix in the message — an unknown literal is far
|
|
1771
|
+
* more likely a typo than a file, so the message names both readings.
|
|
1772
|
+
*/
|
|
1773
|
+
function resolveRenderMode(root, value) {
|
|
1774
|
+
if (value === undefined) return {
|
|
1775
|
+
mode: 'stream',
|
|
1776
|
+
path: null
|
|
1777
|
+
};
|
|
1778
|
+
// (`string & {}` keeps editor completion for the literals; TS cannot
|
|
1779
|
+
// narrow it away by equality, hence the assertion.)
|
|
1780
|
+
if (value === 'stream' || value === 'async') return {
|
|
1781
|
+
mode: value,
|
|
1782
|
+
path: null
|
|
1783
|
+
};
|
|
1784
|
+
const usage = `start.renderMode must be 'stream' (the default), 'async', or the path of a module ` + `(relative to the Vite root) default-exporting a per-request function ` + `((event) => 'stream' | 'async' | Promise<...>)`;
|
|
1785
|
+
if (typeof value !== 'string') {
|
|
1786
|
+
throw new Error(`[@solidjs/vite-plugin] ${usage}; got ${typeof value}. A Vite config cannot serialize a ` + `closure into the generated handler — put the function in a module (e.g. ` + `./src/render-mode.ts) and pass its path instead.`);
|
|
1787
|
+
}
|
|
1788
|
+
const absolute = path.isAbsolute(value) ? value : path.resolve(root, value);
|
|
1789
|
+
if (!fs.existsSync(absolute)) {
|
|
1790
|
+
throw new Error(`[@solidjs/vite-plugin] ${usage}; got ${JSON.stringify(value)}, which is neither a mode ` + `nor an existing file.`);
|
|
1791
|
+
}
|
|
1792
|
+
return {
|
|
1793
|
+
mode: 'stream',
|
|
1794
|
+
path: path.resolve(root, normalizeUserPath(root, value, 'renderMode'))
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1719
1797
|
function resolveEntries(root, options, clientMode) {
|
|
1720
1798
|
const explicitClient = options.entryClient ? normalizeUserPath(root, options.entryClient, 'entryClient') : null;
|
|
1721
1799
|
if (clientMode) {
|
|
@@ -1808,8 +1886,11 @@ function startServe(options, internal = {}) {
|
|
|
1808
1886
|
// any of the (lazy) uses in entry codegen and the entry transform.
|
|
1809
1887
|
let diagnostics = internal.diagnostics === true;
|
|
1810
1888
|
let devtoolsEnabled = false;
|
|
1811
|
-
|
|
1812
|
-
|
|
1889
|
+
// Toolbar detection, memoized per consumer: whether @solidjs/start-devtools
|
|
1890
|
+
// resolves at all, and the app importer it was probed from. Only the
|
|
1891
|
+
// verdict is kept — the resolved id is deliberately not (see resolveId).
|
|
1892
|
+
let devtoolsDetections = {};
|
|
1893
|
+
let devtoolsImporters = {};
|
|
1813
1894
|
// `external` is server-mode-only (documented no-op in client mode, so a
|
|
1814
1895
|
// host-integrated config survives the `ssr` boolean flip untouched).
|
|
1815
1896
|
const externalServer = !clientMode && !!options.external;
|
|
@@ -1821,31 +1902,45 @@ function startServe(options, internal = {}) {
|
|
|
1821
1902
|
let middlewarePath = null;
|
|
1822
1903
|
/** Absolute path of the per-request setup module, when configured (server mode). */
|
|
1823
1904
|
let setupPath = null;
|
|
1905
|
+
/**
|
|
1906
|
+
* `start.renderMode`, resolved at config time: the static mode baked into
|
|
1907
|
+
* the handler, or the absolute path of the per-request module deciding it
|
|
1908
|
+
* (server mode only — a documented no-op in client mode, whose shell has
|
|
1909
|
+
* no boundaries to settle).
|
|
1910
|
+
*/
|
|
1911
|
+
let renderMode = 'stream';
|
|
1912
|
+
let renderModePath = null;
|
|
1824
1913
|
function requireEntries() {
|
|
1825
1914
|
// config() always runs before resolveId/load/configureServer.
|
|
1826
1915
|
if (!entries) throw new Error('[@solidjs/vite-plugin] SSR entries not resolved yet');
|
|
1827
1916
|
return entries;
|
|
1828
1917
|
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Resolve @solidjs/start-devtools for generated code: from the app graph
|
|
1920
|
+
* first (the documented install location), then from the plugin's own
|
|
1921
|
+
* file — in pnpm-isolated apps a copy that is only a dependency of the
|
|
1922
|
+
* plugin is not reachable from the app's importers. Resolving from the
|
|
1923
|
+
* plugin's own file never yields null when the package is absent: it is
|
|
1924
|
+
* declared an optional peer dependency, so Vite answers with its
|
|
1925
|
+
* `__vite-optional-peer-dep:` stub (an empty module). That stub counts as
|
|
1926
|
+
* "not installed".
|
|
1927
|
+
*/
|
|
1928
|
+
async function resolveDevtoolsId(resolve, importer) {
|
|
1929
|
+
const realId = resolved => resolved && !resolved.id.startsWith('__vite-optional-peer-dep:') ? resolved.id : null;
|
|
1930
|
+
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)))));
|
|
1931
|
+
}
|
|
1829
1932
|
async function resolveDevtools(resolve, importer, consumer) {
|
|
1830
1933
|
if (!devtoolsEnabled) return false;
|
|
1831
|
-
//
|
|
1832
|
-
//
|
|
1833
|
-
//
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
// Vite answers with its `__vite-optional-peer-dep:` stub (an empty
|
|
1839
|
-
// module). Treat that stub as "not installed".
|
|
1840
|
-
const realId = resolved => resolved && !resolved.id.startsWith('__vite-optional-peer-dep:') ? resolved.id : null;
|
|
1841
|
-
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)))));
|
|
1842
|
-
})();
|
|
1843
|
-
const id = await devtoolsResolutions[consumer];
|
|
1844
|
-
devtoolsIds[consumer] = id;
|
|
1845
|
-
if (!id && options.devtools === true) {
|
|
1934
|
+
// An install cannot change under a running server, so the verdict is
|
|
1935
|
+
// memoized per consumer. The id it was reached with is not reused for
|
|
1936
|
+
// generated imports: resolveId resolves afresh from the same importer.
|
|
1937
|
+
devtoolsImporters[consumer] ??= importer;
|
|
1938
|
+
devtoolsDetections[consumer] ??= resolveDevtoolsId(resolve, importer).then(id => id !== null);
|
|
1939
|
+
const detected = await devtoolsDetections[consumer];
|
|
1940
|
+
if (!detected && options.devtools === true) {
|
|
1846
1941
|
throw new Error('[@solidjs/vite-plugin] start.devtools requires @solidjs/start-devtools. ' + 'Install it as a development dependency or set start.devtools to false.');
|
|
1847
1942
|
}
|
|
1848
|
-
return
|
|
1943
|
+
return detected;
|
|
1849
1944
|
}
|
|
1850
1945
|
|
|
1851
1946
|
/**
|
|
@@ -2020,7 +2115,7 @@ function startServe(options, internal = {}) {
|
|
|
2020
2115
|
entryClient
|
|
2021
2116
|
} = requireEntries();
|
|
2022
2117
|
const composeServerFunctions = internal.serverFunctions;
|
|
2023
|
-
const lines = [`import { createRequestEvent, createSSRResponse, commitEventResponse${middlewarePath ? ', composeMiddleware' : ''} } from '@solidjs/web';`, `import { provideRequestEvent } from ${JSON.stringify(STORAGE_SOURCE)};`, `import * as entry from ${JSON.stringify(entryServerSpec())};`, ...(middlewarePath ? [`import middlewareModule from ${JSON.stringify(middlewarePath)};`] : []), ...(externalDev ? [`import DEV_STYLES_HEAD from ${JSON.stringify(DEV_STYLES_ID)};`] : []), ...(composeServerFunctions ? [`import { handleServerFunctionRequest, endpoint } from ${JSON.stringify(SERVER_FUNCTION_HANDLER_ID)};`] : [])];
|
|
2118
|
+
const lines = [`import { createRequestEvent, createSSRResponse, commitEventResponse${middlewarePath ? ', composeMiddleware' : ''} } from '@solidjs/web';`, `import { provideRequestEvent } from ${JSON.stringify(STORAGE_SOURCE)};`, `import * as entry from ${JSON.stringify(entryServerSpec())};`, ...(middlewarePath ? [`import middlewareModule from ${JSON.stringify(middlewarePath)};`] : []), ...(renderModePath ? [`import renderModeModule from ${JSON.stringify(renderModePath)};`] : []), ...(externalDev ? [`import DEV_STYLES_HEAD from ${JSON.stringify(DEV_STYLES_ID)};`] : []), ...(composeServerFunctions ? [`import { handleServerFunctionRequest, endpoint } from ${JSON.stringify(SERVER_FUNCTION_HANDLER_ID)};`] : [])];
|
|
2024
2119
|
if (isBuild) {
|
|
2025
2120
|
lines.push(`import manifest from ${JSON.stringify(MANIFEST_ID)};`);
|
|
2026
2121
|
lines.push(``, `function joinAssetPath(base, file) {`, ` if (typeof base !== 'string' || !base) base = '/';`, ` if (base[base.length - 1] !== '/') base += '/';`, ` return base + (file[0] === '/' ? file.slice(1) : file);`, `}`, ``, `let clientEntryUrl;`, `function resolveClientEntry() {`, ` if (clientEntryUrl !== undefined) return clientEntryUrl;`, ` clientEntryUrl = null;`,
|
|
@@ -2042,6 +2137,21 @@ function startServe(options, internal = {}) {
|
|
|
2042
2137
|
lines.push(`const runMiddleware = (request, next) => next(request);`);
|
|
2043
2138
|
}
|
|
2044
2139
|
|
|
2140
|
+
// Render mode (`start.renderMode`): 'stream' flushes the shell with
|
|
2141
|
+
// fallbacks in place and streams boundary content after it; 'async'
|
|
2142
|
+
// adopts the renderToStream result's thenable — which waits for the
|
|
2143
|
+
// complete render — so one settled document goes out (the fix for
|
|
2144
|
+
// no-JS clients, solidjs/solid#3280). Precedence per request: the
|
|
2145
|
+
// `handleRequest` option (hosts driving the handler directly), then the
|
|
2146
|
+
// configured module's per-request result, then the static config. Every
|
|
2147
|
+
// source is validated against the two literals with the offender named.
|
|
2148
|
+
lines.push(``, `function assertRenderMode(mode, source) {`, ` if (mode !== 'stream' && mode !== 'async') {`, ` throw new Error('[@solidjs/vite-plugin] ' + source + " must be 'stream' or 'async', got " + JSON.stringify(mode));`, ` }`, ` return mode;`, `}`);
|
|
2149
|
+
if (renderModePath) {
|
|
2150
|
+
lines.push(`if (typeof renderModeModule !== 'function') {`, ` throw new Error('[@solidjs/vite-plugin] start.renderMode must default-export a function ' +`, ` "((event) => 'stream' | 'async' | Promise<...>): " + ${JSON.stringify(renderModePath)});`, `}`, `async function resolveRenderMode(event, options) {`, ` if (options.renderMode !== undefined) return assertRenderMode(options.renderMode, 'handleRequest options.renderMode');`, ` return assertRenderMode(await renderModeModule(event), 'the start.renderMode module (' + ${JSON.stringify(renderModePath)} + ') result');`, `}`);
|
|
2151
|
+
} else {
|
|
2152
|
+
lines.push(`function resolveRenderMode(event, options) {`, ` if (options.renderMode !== undefined) return assertRenderMode(options.renderMode, 'handleRequest options.renderMode');`, ` return ${JSON.stringify(renderMode)};`, `}`);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2045
2155
|
// No `_$SC` bootstrap injection: the runtime's serialized
|
|
2046
2156
|
// server-component references self-bootstrap the registry (each
|
|
2047
2157
|
// hydration script's first reference carries it as an idempotent
|
|
@@ -2122,7 +2232,11 @@ function startServe(options, internal = {}) {
|
|
|
2122
2232
|
// flag, so external-host dispatch and preview stay render-always.
|
|
2123
2233
|
lines.push(` if (options.pageRequest === false) {`, ` return new Response(null, { status: 404, headers: { ${JSON.stringify(DEV_FALLTHROUGH_HEADER)}: '1' } });`, ` }`);
|
|
2124
2234
|
}
|
|
2125
|
-
lines.push(isBuild ? ` const clientEntry = options.clientEntry || resolveClientEntry();` : ` const clientEntry = options.clientEntry || ${JSON.stringify(devClientEntryUrl())};`,
|
|
2235
|
+
lines.push(isBuild ? ` const clientEntry = options.clientEntry || resolveClientEntry();` : ` const clientEntry = options.clientEntry || ${JSON.stringify(devClientEntryUrl())};`,
|
|
2236
|
+
// Decided before the render starts (the module form may be async),
|
|
2237
|
+
// inside the request scope and after the middleware chain, so a
|
|
2238
|
+
// per-request policy sees the decorated event.
|
|
2239
|
+
` const renderMode = await resolveRenderMode(event, options);`, ` let result = entry.render(request, { clientEntry, ...options.context });`,
|
|
2126
2240
|
// renderToStream results are thenables whose then() waits for the
|
|
2127
2241
|
// *complete* render — check for pipe first so streaming survives, and
|
|
2128
2242
|
// only await plain promises (async render functions).
|
|
@@ -2131,6 +2245,14 @@ function startServe(options, internal = {}) {
|
|
|
2131
2245
|
// entry): a bare promise resolution would adopt the stream's
|
|
2132
2246
|
// thenable and buffer the whole render.
|
|
2133
2247
|
` if (result && result.${STREAM_BOX}) result = result.${STREAM_BOX};`] : []),
|
|
2248
|
+
// Async mode adopts the thenable deliberately — the very thing the
|
|
2249
|
+
// pipe-first check and the setup box exist to avoid in stream mode.
|
|
2250
|
+
// It resolves with the full HTML once every boundary settled, each
|
|
2251
|
+
// spliced in place pre-flush (no fallbacks, no swap scripts; hydration
|
|
2252
|
+
// data still serialized), and the string then takes
|
|
2253
|
+
// createSSRResponse's string path below: stub commit, transformChunk
|
|
2254
|
+
// on the whole document, and a mid-render Location as a real 3xx.
|
|
2255
|
+
` if (renderMode === 'async' && result && typeof result.then === 'function') {`, ` result = await result;`, ` }`,
|
|
2134
2256
|
// Raw Responses fold at the handler edge (handleRequest), after the
|
|
2135
2257
|
// middleware chain unwinds — not here, where middleware above this
|
|
2136
2258
|
// frame could still legitimately mutate headers.
|
|
@@ -2169,8 +2291,8 @@ function startServe(options, internal = {}) {
|
|
|
2169
2291
|
config(userConfig, env) {
|
|
2170
2292
|
root = path.resolve(userConfig.root || process.cwd());
|
|
2171
2293
|
devtoolsEnabled = env.command === 'serve' && !env.isPreview && options.devtools !== false;
|
|
2172
|
-
|
|
2173
|
-
|
|
2294
|
+
devtoolsDetections = {};
|
|
2295
|
+
devtoolsImporters = {};
|
|
2174
2296
|
entries = resolveEntries(root, options, clientMode);
|
|
2175
2297
|
internal.onDocumentResolved?.(entries.document);
|
|
2176
2298
|
middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
|
|
@@ -2182,6 +2304,17 @@ function startServe(options, internal = {}) {
|
|
|
2182
2304
|
// hook needs does not exist there.
|
|
2183
2305
|
throw new Error('[@solidjs/vite-plugin] start.setup only applies to generated entries: your ' + 'entry-server owns render() already, so call your setup step there instead ' + `(remove start.setup or the authored entry): ${options.setup}`);
|
|
2184
2306
|
}
|
|
2307
|
+
// Server-mode only as well: the client-mode shell renders no app,
|
|
2308
|
+
// so there is nothing to settle. Validated in every mode though —
|
|
2309
|
+
// a typo should not hide behind the `ssr` boolean.
|
|
2310
|
+
({
|
|
2311
|
+
mode: renderMode,
|
|
2312
|
+
path: renderModePath
|
|
2313
|
+
} = resolveRenderMode(root, options.renderMode));
|
|
2314
|
+
if (clientMode) {
|
|
2315
|
+
renderMode = 'stream';
|
|
2316
|
+
renderModePath = null;
|
|
2317
|
+
}
|
|
2185
2318
|
if (env.isPreview) {
|
|
2186
2319
|
if (clientMode) {
|
|
2187
2320
|
// Client-mode builds emit a real dist/client/index.html (the
|
|
@@ -2323,7 +2456,7 @@ function startServe(options, internal = {}) {
|
|
|
2323
2456
|
diagnostics = detectDiagnosticsPackage(root);
|
|
2324
2457
|
}
|
|
2325
2458
|
},
|
|
2326
|
-
resolveId(source, importer, opts) {
|
|
2459
|
+
async resolveId(source, importer, opts) {
|
|
2327
2460
|
if (source === HANDLER_ID) {
|
|
2328
2461
|
return {
|
|
2329
2462
|
id: HANDLER_ID,
|
|
@@ -2348,13 +2481,25 @@ function startServe(options, internal = {}) {
|
|
|
2348
2481
|
moduleSideEffects: true
|
|
2349
2482
|
};
|
|
2350
2483
|
}
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2484
|
+
if (source === DEVTOOLS_PACKAGE && (importer === ENTRY_SERVER_ID || importer === ENTRY_CLIENT_ID || importer === DEVTOOLS_MOUNT_ID)) {
|
|
2485
|
+
// Generated modules have no directory for bare-package resolution:
|
|
2486
|
+
// resolve from the app importer detection probed. Resolve afresh on
|
|
2487
|
+
// every request rather than reusing detection's id — in the client
|
|
2488
|
+
// environment that id is the optimizer's pre-bundled URL, stamped
|
|
2489
|
+
// with the browserHash of the pass that produced it. Any dependency
|
|
2490
|
+
// discovered after the initial scan re-optimizes: the toolbar's
|
|
2491
|
+
// chunks are re-emitted under new names and the hash moves on, and
|
|
2492
|
+
// a frozen id would keep the entry on the previous pass — its lazy
|
|
2493
|
+
// chunks answer 504 (Outdated Optimize Dep) and the stale bundle
|
|
2494
|
+
// brings a second solid-js instance into the page.
|
|
2495
|
+
const from = devtoolsImporters[getEnvironmentConsumer(this.environment, opts)];
|
|
2496
|
+
if (!from) return null;
|
|
2497
|
+
const id = await resolveDevtoolsId((s, i) => this.resolve(s, i, {
|
|
2498
|
+
skipSelf: true
|
|
2499
|
+
}), from);
|
|
2500
|
+
return id ? {
|
|
2501
|
+
id
|
|
2502
|
+
} : null;
|
|
2358
2503
|
}
|
|
2359
2504
|
return null;
|
|
2360
2505
|
},
|
|
@@ -3406,7 +3551,69 @@ function combineSourcemaps(maps) {
|
|
|
3406
3551
|
// remapping expects most-recent-first.
|
|
3407
3552
|
return JSON.parse(remapping(chain.reverse(), () => null).toString());
|
|
3408
3553
|
}
|
|
3554
|
+
function toPosixPath(p) {
|
|
3555
|
+
return p.split(path.sep).join('/');
|
|
3556
|
+
}
|
|
3557
|
+
function tryRealpath(p) {
|
|
3558
|
+
try {
|
|
3559
|
+
return fs.realpathSync.native(p);
|
|
3560
|
+
} catch {
|
|
3561
|
+
return null;
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3565
|
+
/** The `input` a build environment's config resolves to, in any spelling. */
|
|
3566
|
+
function configuredBuildInput(build) {
|
|
3567
|
+
if (!build) return undefined;
|
|
3568
|
+
return build.rolldownOptions?.input ?? build.rollupOptions?.input ?? build.lib?.entry;
|
|
3569
|
+
}
|
|
3409
3570
|
|
|
3571
|
+
/**
|
|
3572
|
+
* The genuine entries of a client build, derived from its configured input
|
|
3573
|
+
* (`build.rollupOptions.input` as a string / array / record, or Vite's
|
|
3574
|
+
* default `index.html`). Rollup and rolldown only ever flag two kinds of
|
|
3575
|
+
* chunk `isEntry`: those facades and chunks plugins emit with
|
|
3576
|
+
* `emitFile({ type: 'chunk' })` — so this is exactly the knowledge that
|
|
3577
|
+
* tells a real application entry apart from an emitted lazy facade.
|
|
3578
|
+
*
|
|
3579
|
+
* `moduleIds` — every spelling the entry's facade module id can take: as
|
|
3580
|
+
* written (virtual ids resolve to themselves), resolved against the root
|
|
3581
|
+
* (Vite resolves relative file inputs there), and the real path of either
|
|
3582
|
+
* (Vite's resolver follows symlinks).
|
|
3583
|
+
* `manifestKeys` — the manifest.json keys Vite derives from those facades
|
|
3584
|
+
* (root-relative, `\0` stripped), matching Vite's own `getChunkName`.
|
|
3585
|
+
*/
|
|
3586
|
+
function resolveConfiguredEntries(input, root) {
|
|
3587
|
+
const raw = input == null ? ['index.html'] : typeof input === 'string' ? [input] : Array.isArray(input) ? input : Object.values(input);
|
|
3588
|
+
const moduleIds = new Set();
|
|
3589
|
+
for (const id of raw) {
|
|
3590
|
+
if (typeof id !== 'string') continue;
|
|
3591
|
+
const clean = id.replace(/\0/g, '');
|
|
3592
|
+
const candidates = [clean, path.resolve(root, clean)];
|
|
3593
|
+
for (const candidate of candidates) {
|
|
3594
|
+
moduleIds.add(candidate);
|
|
3595
|
+
moduleIds.add(toPosixPath(candidate));
|
|
3596
|
+
const real = tryRealpath(candidate);
|
|
3597
|
+
if (real) {
|
|
3598
|
+
moduleIds.add(real);
|
|
3599
|
+
moduleIds.add(toPosixPath(real));
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
const manifestKeys = new Set();
|
|
3604
|
+
for (const id of moduleIds) manifestKeys.add(toPosixPath(path.relative(root, id)));
|
|
3605
|
+
return {
|
|
3606
|
+
moduleIds,
|
|
3607
|
+
manifestKeys,
|
|
3608
|
+
isEntryModule(id) {
|
|
3609
|
+
if (!id) return false;
|
|
3610
|
+
const clean = id.replace(/\0/g, '');
|
|
3611
|
+
if (moduleIds.has(clean) || moduleIds.has(toPosixPath(clean))) return true;
|
|
3612
|
+
const real = tryRealpath(clean);
|
|
3613
|
+
return !!real && (moduleIds.has(real) || moduleIds.has(toPosixPath(real)));
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3410
3617
|
/**
|
|
3411
3618
|
* Chunks emitted for lazy() targets are marked `isEntry` by Rollup even
|
|
3412
3619
|
* though they are semantically dynamic entries. Reclassify any entry that is
|
|
@@ -3415,18 +3622,57 @@ function combineSourcemaps(maps) {
|
|
|
3415
3622
|
* the real client entry. Works on both the Vite manifest.json shape and the
|
|
3416
3623
|
* raw Rollup output bundle — both key entries by name and expose
|
|
3417
3624
|
* `dynamicImports` / `isEntry` with the same meaning.
|
|
3625
|
+
*
|
|
3626
|
+
* Being a dynamic-import target alone does not make a chunk a lazy facade,
|
|
3627
|
+
* though: the real client entry becomes one whenever it absorbs a module
|
|
3628
|
+
* that is also dynamically imported somewhere else. Solid 2 produces that
|
|
3629
|
+
* shape on its own — `@solidjs/web/frames/client` lazily imports the
|
|
3630
|
+
* serialization decoder (`loadCodec()`), so a static import of
|
|
3631
|
+
* `@solidjs/web/serialization/decode` anywhere in the client graph merges
|
|
3632
|
+
* the decoder into the entry chunk, and the entry then lists itself (or is
|
|
3633
|
+
* listed by another lazy chunk) under `dynamicImports`. Stripping `isEntry`
|
|
3634
|
+
* there leaves the bundle with no entry at all ("No entry file found"
|
|
3635
|
+
* downstream, e.g. TanStack Start's manifest capture, #342). Genuine
|
|
3636
|
+
* configured entries are therefore never reclassified, and a chunk's
|
|
3637
|
+
* dynamic import of itself is not an edge worth acting on.
|
|
3638
|
+
*
|
|
3639
|
+
* Rolldown caveat: of the flags written here only `isEntry` is synced back
|
|
3640
|
+
* to the native bundle after the hook (rolldown's `update_output_chunk`
|
|
3641
|
+
* copies `code`, `map`, `imports`, `dynamicImports`, `isEntry` and the file
|
|
3642
|
+
* name; `isDynamicEntry` is kept from the original chunk). Later plugins
|
|
3643
|
+
* and Vite's manifest plugin therefore see reclassified facades as neither
|
|
3644
|
+
* entry nor dynamic entry under rolldown. The manifest `load` path repairs
|
|
3645
|
+
* `isDynamicEntry` on the plugin's own manifest module, the one place it
|
|
3646
|
+
* controls end to end.
|
|
3418
3647
|
*/
|
|
3419
|
-
function normalizeEmittedLazyEntries(manifest
|
|
3420
|
-
|
|
3648
|
+
function normalizeEmittedLazyEntries(manifest, {
|
|
3649
|
+
isConfiguredEntry,
|
|
3650
|
+
knownLazyKeys,
|
|
3651
|
+
warn,
|
|
3652
|
+
repairDynamicEntries
|
|
3653
|
+
}) {
|
|
3654
|
+
const dynamicKeys = new Map();
|
|
3421
3655
|
for (const key in manifest) {
|
|
3422
3656
|
const imports = manifest[key].dynamicImports;
|
|
3423
|
-
if (imports)
|
|
3657
|
+
if (!imports) continue;
|
|
3658
|
+
for (const dep of imports) {
|
|
3659
|
+
// A chunk that absorbed one of its own lazy targets imports itself;
|
|
3660
|
+
// that says nothing about whether it is an entry.
|
|
3661
|
+
if (dep !== key && !dynamicKeys.has(dep)) dynamicKeys.set(dep, key);
|
|
3662
|
+
}
|
|
3424
3663
|
}
|
|
3425
|
-
for (const key of dynamicKeys) {
|
|
3664
|
+
for (const [key, importer] of dynamicKeys) {
|
|
3426
3665
|
const entry = manifest[key];
|
|
3427
|
-
if (entry
|
|
3666
|
+
if (!entry || entry.type === 'asset') continue;
|
|
3667
|
+
if (isConfiguredEntry(key, entry)) continue;
|
|
3668
|
+
if (entry.isEntry) {
|
|
3428
3669
|
entry.isEntry = false;
|
|
3429
3670
|
entry.isDynamicEntry = true;
|
|
3671
|
+
if (warn && !knownLazyKeys?.has(key)) {
|
|
3672
|
+
warn(`[@solidjs/vite-plugin] Reclassified the entry chunk "${key}" as a dynamic entry ` + `because "${importer}" dynamically imports it and it does not match a configured ` + `build input. If "${key}" is the application entry, its chunk absorbed a module ` + 'that is also imported dynamically elsewhere (for example a static import of ' + '"@solidjs/web/serialization/decode" alongside Solid\'s own lazy import of it); ' + 'list the entry in `build.rollupOptions.input` so the plugin can recognize it.');
|
|
3673
|
+
}
|
|
3674
|
+
} else if (repairDynamicEntries && !entry.isDynamicEntry) {
|
|
3675
|
+
entry.isDynamicEntry = true;
|
|
3430
3676
|
}
|
|
3431
3677
|
}
|
|
3432
3678
|
}
|
|
@@ -3488,6 +3734,11 @@ function solidPlugin(options = {}) {
|
|
|
3488
3734
|
let isSsrBuild = false;
|
|
3489
3735
|
let base = '/';
|
|
3490
3736
|
let clientOutDir = null;
|
|
3737
|
+
// The client environment's resolved build options, for the configured
|
|
3738
|
+
// entry input. Read off the resolved config so the SSR half of a
|
|
3739
|
+
// two-invocation build (`vite build --ssr`) still knows the client's
|
|
3740
|
+
// entries when it bakes the client manifest in.
|
|
3741
|
+
let clientBuildConfig = null;
|
|
3491
3742
|
let solidPkgsConfig;
|
|
3492
3743
|
const tsrxCss = new Map();
|
|
3493
3744
|
|
|
@@ -3793,6 +4044,29 @@ function solidPlugin(options = {}) {
|
|
|
3793
4044
|
// So the dev flag has to reach both lists.
|
|
3794
4045
|
if (replaceDev && config.consumer !== 'client' && name !== 'client') {
|
|
3795
4046
|
config.resolve.externalConditions = ['development', ...(config.resolve.externalConditions ?? vite.defaultExternalConditions)];
|
|
4047
|
+
|
|
4048
|
+
// `externalConditions` only reaches the imports the module runner
|
|
4049
|
+
// resolves itself. An externalized package's OWN imports are resolved
|
|
4050
|
+
// by Node, with Node's conditions — never `development`. Since
|
|
4051
|
+
// solid 2.0.0-rc.7 both `solid-js` and `@solidjs/web` ship a
|
|
4052
|
+
// `dist/server.dev.*` behind that condition, so leaving them external
|
|
4053
|
+
// splits the framework in two under `vite dev`: the app's `solid-js`
|
|
4054
|
+
// is the runner's dev copy while `@solidjs/web`'s `import "solid-js"`
|
|
4055
|
+
// lands on Node's prod copy. `renderToStream` then installs the asset
|
|
4056
|
+
// resolver on one `sharedConfig` and `lazy()` reads the other ("no
|
|
4057
|
+
// asset manifest is set"), with every other module-level singleton
|
|
4058
|
+
// (owner tracking, request events, hydration keys) split the same
|
|
4059
|
+
// way. Inlining the two core packages makes every resolution — theirs
|
|
4060
|
+
// included — go through the environment's conditions, so one dev
|
|
4061
|
+
// build is loaded end to end. Framework packages that declare the
|
|
4062
|
+
// `solid` export condition are already inlined via vitefu below and
|
|
4063
|
+
// reach the same copy. Vitest projects manage their own inlining
|
|
4064
|
+
// (`test.server.deps` above) and are left alone, as is a host that
|
|
4065
|
+
// set `noExternal: true` (everything is inlined already).
|
|
4066
|
+
if (!isTestMode && config.resolve.noExternal !== true) {
|
|
4067
|
+
const noExternal = config.resolve.noExternal;
|
|
4068
|
+
config.resolve.noExternal = [...(Array.isArray(noExternal) ? noExternal : noExternal ? [noExternal] : []), 'solid-js', '@solidjs/web'];
|
|
4069
|
+
}
|
|
3796
4070
|
}
|
|
3797
4071
|
|
|
3798
4072
|
// Set resolve.noExternal and resolve.external for the SSR environment.
|
|
@@ -3809,6 +4083,7 @@ function solidPlugin(options = {}) {
|
|
|
3809
4083
|
isSsrBuild = !!config.build.ssr;
|
|
3810
4084
|
base = config.base;
|
|
3811
4085
|
projectRoot = config.root;
|
|
4086
|
+
clientBuildConfig = config.environments?.client?.build ?? config.build;
|
|
3812
4087
|
filter = vite.createFilter(options.include, options.exclude, {
|
|
3813
4088
|
resolve: projectRoot
|
|
3814
4089
|
});
|
|
@@ -3942,7 +4217,23 @@ function solidPlugin(options = {}) {
|
|
|
3942
4217
|
const manifestPath = clientManifestPath();
|
|
3943
4218
|
if (manifestPath) {
|
|
3944
4219
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
3945
|
-
|
|
4220
|
+
// Manifest records are keyed the way Vite keys entry chunks (the
|
|
4221
|
+
// root-relative facade path, also carried as `src`), so the
|
|
4222
|
+
// configured client inputs identify the genuine entries here too —
|
|
4223
|
+
// independent of `isEntry`, which the serialized manifest may have
|
|
4224
|
+
// lost already (older plugin builds stripped it; see #342).
|
|
4225
|
+
const entries = resolveConfiguredEntries(configuredBuildInput(clientBuildConfig), projectRoot);
|
|
4226
|
+
const isConfiguredEntry = (key, record) => entries.manifestKeys.has(key) || typeof record.src === 'string' && entries.manifestKeys.has(record.src);
|
|
4227
|
+
for (const key in manifest) {
|
|
4228
|
+
if (isConfiguredEntry(key, manifest[key]) && manifest[key].file) {
|
|
4229
|
+
manifest[key].isEntry = true;
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
normalizeEmittedLazyEntries(manifest, {
|
|
4233
|
+
isConfiguredEntry,
|
|
4234
|
+
warn: message => this.warn(message),
|
|
4235
|
+
repairDynamicEntries: true
|
|
4236
|
+
});
|
|
3946
4237
|
manifest._base = base;
|
|
3947
4238
|
return `export default ${JSON.stringify(manifest)};`;
|
|
3948
4239
|
}
|
|
@@ -3959,6 +4250,12 @@ function solidPlugin(options = {}) {
|
|
|
3959
4250
|
// the bundle don't mistake them for application entries. Must precede
|
|
3960
4251
|
// the client asset map build, which keys off dynamic entries.
|
|
3961
4252
|
if (options.ssr) {
|
|
4253
|
+
// The genuine entries are the configured inputs of this very
|
|
4254
|
+
// environment — the plugin injects the client entry itself in start
|
|
4255
|
+
// mode, and Vite's default is index.html — so their facade chunks
|
|
4256
|
+
// are recognizable regardless of what dynamically imports them.
|
|
4257
|
+
const entries = resolveConfiguredEntries(configuredBuildInput(this.environment?.config?.build ?? clientBuildConfig), projectRoot);
|
|
4258
|
+
const knownLazyKeys = new Set();
|
|
3962
4259
|
for (const ref of emittedLazyChunkRefs) {
|
|
3963
4260
|
let fileName;
|
|
3964
4261
|
try {
|
|
@@ -3969,10 +4266,17 @@ function solidPlugin(options = {}) {
|
|
|
3969
4266
|
}
|
|
3970
4267
|
const chunk = bundle[fileName];
|
|
3971
4268
|
if (!chunk || chunk.type !== 'chunk') continue;
|
|
4269
|
+
// An entry that is also lazily imported stays an entry.
|
|
4270
|
+
if (entries.isEntryModule(chunk.facadeModuleId)) continue;
|
|
4271
|
+
knownLazyKeys.add(fileName);
|
|
3972
4272
|
chunk.isEntry = false;
|
|
3973
4273
|
chunk.isDynamicEntry = true;
|
|
3974
4274
|
}
|
|
3975
|
-
normalizeEmittedLazyEntries(bundle
|
|
4275
|
+
normalizeEmittedLazyEntries(bundle, {
|
|
4276
|
+
isConfiguredEntry: (_key, chunk) => entries.isEntryModule(chunk.facadeModuleId),
|
|
4277
|
+
knownLazyKeys,
|
|
4278
|
+
warn: message => this.warn(message)
|
|
4279
|
+
});
|
|
3976
4280
|
}
|
|
3977
4281
|
},
|
|
3978
4282
|
async transform(source, id, transformOptions) {
|