diff --git a/.changeset/semi-framework-deps-stay-out-of-optimize.md b/.changeset/semi-framework-deps-stay-out-of-optimize.md new file mode 100644 index 0000000..0592eb1 --- /dev/null +++ b/.changeset/semi-framework-deps-stay-out-of-optimize.md @@ -0,0 +1,5 @@ +--- +'@solidjs/vite-plugin': patch +--- + +Fix `vite dev` failing in dependency optimization for apps using a Solid library that ships build-time code in the same package (#375). Since the semi-framework classification landed (any package with `solid-js` / `@solidjs/web` in `dependencies` or `peerDependencies` is `ssr.noExternal` so it shares the app's runtime copy), vitefu's crawl also deep-included every CJS dependency of such a package into the browser's `optimizeDeps.include`, exactly as it does for framework packages. For `@yak/solid` that is `@swc/core`, and rolldown failed on its native `.node` binding (`UNLOADABLE_DEPENDENCY … stream did not contain valid UTF-8`) before the dev server served a page. Sharing the runtime needs nothing pre-bundled, so include chains that pass through a semi-framework package are dropped; a browser-side CJS dependency of one is discovered and optimized on first use, as before the package was classified. Framework packages (a `solid` export condition) keep vitefu's full treatment. diff --git a/src/index.ts b/src/index.ts index bc111bf..ef599d3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1084,6 +1084,9 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { // `buildInputs`, which lists every route module). Null outside start mode. let startClientEntryId: string | null = null; let solidPkgsConfig: Awaited>; + // Names of the packages `isSemiFrameworkPkgByJson` classified, so their + // dependencies can be taken back out of `optimizeDeps.include` (see below). + const semiFrameworkPkgs = new Set(); const tsrxCss = new Map(); // The client build's manifest, read back by SSR builds. In builder-mode @@ -1274,12 +1277,35 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { // would split it just the same), never vitest (it manages inlining // via test.server.deps). if (!(replaceDev || observe) || isTestMode) return false; - return SOLID_RUNTIME_PKGS.some( + const semi = SOLID_RUNTIME_PKGS.some( (name) => pkgJson.dependencies?.[name] || pkgJson.peerDependencies?.[name], ); + if (semi && typeof pkgJson.name === 'string') semiFrameworkPkgs.add(pkgJson.name); + return semi; }, }); + // A semi-framework package is inlined for ONE reason: so it resolves the + // runtime through Vite and shares the app's copy. vitefu's crawl treats + // it like a framework package for its dependencies too — every CJS + // dependency is deep-included (`"pkg > dep"`) so the browser optimizer + // pre-bundles it. For a library that ships its build-time half in the + // same package (a Vite plugin, a compiler binding, a CLI) that pushes + // node-only modules into the client's pre-bundle: `@yak/solid` lists + // `@swc/core`, and rolldown then fails on its native `.node` binding + // before `vite dev` serves a page (#375). Nothing about sharing the + // runtime needs the package's dependencies pre-bundled, so any chain + // that runs through a semi-framework package is dropped; a browser-side + // CJS dependency of such a package is discovered and optimized on + // first use instead, as it was before the package was classified at + // all. Framework packages (a `solid` export condition) keep vitefu's + // full treatment — that is the long-standing contract for them. + if (semiFrameworkPkgs.size > 0) { + solidPkgsConfig.optimizeDeps.include = solidPkgsConfig.optimizeDeps.include.filter( + (entry) => !entry.split(' > ').some((name) => semiFrameworkPkgs.has(name)), + ); + } + // fix for bundling dev in production const nestedDeps = replaceDev ? ['solid-js', '@solidjs/web'] : [];