From 94f18780510ddc25035440e2d835093d1f8e1380 Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Sun, 27 Sep 2026 02:32:10 -0700 Subject: [PATCH] fix: keep a semi-framework package's dependencies out of optimizeDeps.include (#375) Since the semi-framework classification (any package with solid-js or @solidjs/web in dependencies/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, as it does for framework packages. For @yak/solid that is @swc/core, and rolldown failed on its native .node binding before vite dev served a page. Sharing the runtime needs nothing pre-bundled. Record the names isSemiFrameworkPkgByJson classifies and drop every include chain that passes through one; a browser-side CJS dependency of such a package is discovered and optimized on first use, as before the package was classified. Framework packages (a solid export condition) keep vitefu's full treatment. Verified against next-yak's vite-solid e2e (client + SSR dev server, 36 cases + 7 HMR cases, both fold modes) on solid-js 2.0.0-rc.10, which fails in dependency optimization on next.44/next.45 and passes with this. Co-authored-by: Claude --- ...emi-framework-deps-stay-out-of-optimize.md | 5 ++++ src/index.ts | 28 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .changeset/semi-framework-deps-stay-out-of-optimize.md 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'] : [];