diff --git a/.changeset/honor-host-noexternal-patterns.md b/.changeset/honor-host-noexternal-patterns.md new file mode 100644 index 0000000..2ba693f --- /dev/null +++ b/.changeset/honor-host-noexternal-patterns.md @@ -0,0 +1,5 @@ +--- +'@solidjs/vite-plugin': patch +--- + +Honor the host's `resolve.noExternal` patterns when adding vitefu's externals to the SSR environment. The plugin already refused to re-externalize anything `noExternal` inlines, but it compared literal names only, while Vite treats string entries as picomatch patterns and RegExp entries as tests (`createFilter(undefined, noExternal, { resolve: false })`). Since 3.0.0-next.41 the crawl also reaches the packages that consume the Solid runtime, and their non-Solid dependencies land in `ssr.external` — so a host that inlines its packages by pattern (TanStack Start's `@tanstack/start**`, whose `@tanstack/start-server-core` resolves its `#tanstack-*` imports only when Vite processes it) saw them re-externalized, and `vite dev` failed with `ERR_PACKAGE_IMPORT_NOT_DEFINED: Package import specifier "#tanstack-router-entry" is not defined`. The externals are now filtered with the same matcher Vite uses, and a single string or RegExp `noExternal` value is kept instead of being dropped. diff --git a/src/index.ts b/src/index.ts index 5e8933f..d56fdde 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1270,8 +1270,13 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { // Only set resolve.external if noExternal is not true (to avoid conflicts with plugins like Cloudflare) if (name === 'ssr' && solidPkgsConfig) { if (config.resolve.noExternal !== true) { + const hostNoExternal = config.resolve.noExternal; const noExternal = [ - ...(Array.isArray(config.resolve.noExternal) ? config.resolve.noExternal : []), + ...(Array.isArray(hostNoExternal) + ? hostNoExternal + : hostNoExternal + ? [hostNoExternal] + : []), ...solidPkgsConfig.ssr.noExternal, ]; config.resolve.noExternal = noExternal; @@ -1282,9 +1287,25 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { // @tanstack/solid-router 2.0.0-rc.7 → @solidjs/web) would therefore // re-externalize a core inlined above and split the runtime again. // Nothing inlined may appear in `external`. + // + // "Inlined" means whatever `noExternal` claims, judged the way Vite + // judges it (`createFilter(undefined, noExternal, { resolve: + // false })` in its `createIsConfiguredAsExternal`): string entries + // are picomatch patterns, RegExp entries test the id. A literal + // `includes` check only caught exact names, so a host that inlines + // its packages by pattern — TanStack Start's `@tanstack/start**`, + // whose start-server-core resolves `#tanstack-*` imports only when + // Vite processes it — saw them re-externalized once the + // semi-framework crawl reached them through its Solid adapter + // (their non-Solid dependencies land in vitefu's `ssr.external`), + // and `vite dev` failed with ERR_PACKAGE_IMPORT_NOT_DEFINED. + const keepsExternal = + noExternal.length > 0 + ? createFilter(undefined, noExternal, { resolve: false }) + : () => true; config.resolve.external = [ ...(Array.isArray(config.resolve.external) ? config.resolve.external : []), - ...solidPkgsConfig.ssr.external.filter((dep) => !noExternal.includes(dep)), + ...solidPkgsConfig.ssr.external.filter((dep) => keepsExternal(dep)), ]; } }