diff --git a/.changeset/signals-optimize-include.md b/.changeset/signals-optimize-include.md new file mode 100644 index 0000000..66830a0 --- /dev/null +++ b/.changeset/signals-optimize-include.md @@ -0,0 +1,12 @@ +--- +'@solidjs/vite-plugin': patch +--- + +Pre-bundle `@solidjs/signals` up front under `vite dev` (and Vitest browser +mode) when the app can import it by name, i.e. it resolves from the project +root. The first optimizer pass otherwise inlines signals into `solid-js`, so a +direct `@solidjs/signals` import from a module the scanner never reached (a +browser-mode test file, a non-literal dynamic import) was a late discovery: +re-optimize and a full page reload ("Vite unexpectedly reloaded a test"). +Apps that only get signals transitively (pnpm's isolated layout) are +unchanged. diff --git a/src/index.ts b/src/index.ts index 07b31e0..a16591a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1531,10 +1531,7 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { // `solid-js` fails with ERR_MODULE_NOT_FOUND as soon as anything // externalizes it (vitefu does, once a semi-framework package such as // `@solidjs/diagnostics` lists it under `dependencies`). With no root - // copy there is nothing to dedupe TO, so the gate loses nothing. Not - // added to `optimizeDeps.include`: the optimizer already reaches - // signals through `solid-js`, and an include entry that doesn't - // resolve from the root logs a warning on every start. + // copy there is nothing to dedupe TO, so the gate loses nothing. // // Dedupe applies to builds as well as dev. A dependency that ships its // own nested copy of `solid-js` would otherwise put a second runtime @@ -1545,6 +1542,7 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { const dedupe = ['solid-js', '@solidjs/web', '@solidjs/signals'].filter((name) => findPackageDir(name, dedupeRoot), ); + const rootSignals = dedupe.includes('@solidjs/signals'); const userTest = (userConfig as any).test ?? {}; const test = {} as any; @@ -1709,6 +1707,14 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { ...(command === 'serve' ? ['solid-js/attribution', '@solidjs/web/performance-tracks'] : []), + // An app that depends on `@solidjs/signals` directly can import + // it from a module the scanner never reaches (a test file in + // Vitest browser mode, a non-literal dynamic import). The first + // pass bundled signals inside `solid-js`, so that import is a + // late discovery: re-optimize, full reload. Only when it resolves + // from the root: otherwise the app can't import it by name, and + // an include entry that doesn't resolve warns on every start. + ...(command === 'serve' && rootSignals ? ['@solidjs/signals'] : []), ...solidPkgsConfig.optimizeDeps.include, ], exclude: solidPkgsConfig.optimizeDeps.exclude,