From 56e2b85af027d511b2cc95b59263a634c95eaffc Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Thu, 15 Oct 2026 22:27:21 -0500 Subject: [PATCH] fix(ssr): drop $$moduleUrl from server entry chunk exports --- .changeset/strip-entry-module-url.md | 5 ++++ src/index.ts | 35 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 .changeset/strip-entry-module-url.md diff --git a/.changeset/strip-entry-module-url.md b/.changeset/strip-entry-module-url.md new file mode 100644 index 0000000..95dc7d0 --- /dev/null +++ b/.changeset/strip-entry-module-url.md @@ -0,0 +1,5 @@ +--- +'@solidjs/vite-plugin': patch +--- + +SSR builds no longer export `$$moduleUrl` from entry chunks, so a Cloudflare Worker whose `main` is a project module starts again in workerd. diff --git a/src/index.ts b/src/index.ts index ced3064..a4f8f25 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1334,6 +1334,23 @@ export default function solidPlugin(options: Partial = {}): Plugin[] { return code + `\nexport const $$moduleUrl = ${JSON.stringify(relativeId)};\n`; } + // Pads removed specifiers with spaces so the chunk's source map stays valid. + function stripModuleUrlExport(code: string): string | null { + const clause = /export\s*\{([^}]*)\}\s*;?(?=(?:\s|\/\/[^\n]*|\/\*[\s\S]*?\*\/)*$)/.exec(code); + if (!clause) return null; + const specifiers = clause[1].split(','); + const kept = specifiers.filter( + (spec) => !/^\s*(?:[\w$]+\s+as\s+)?(?:\$\$moduleUrl|(["'])\$\$moduleUrl\1)\s*$/.test(spec), + ); + if (kept.length === specifiers.length) return null; + const start = clause.index + clause[0].indexOf('{') + 1; + return ( + code.slice(0, start) + + kept.join(',').padEnd(clause[1].length) + + code.slice(start + clause[1].length) + ); + } + function nativeTsrxCss(result: unknown): string { const css = (result as { css?: unknown }).css; return typeof css === 'string' ? css : ''; @@ -2070,6 +2087,24 @@ export default manifest;`; }, }, + renderChunk(code, chunk, outputOptions) { + // Hosts re-export server entries whole, and workerd rejects non-handler exports. + if (!isBuild || isClientBuild(this) || outputOptions.format !== 'es') return null; + if (!chunk.isEntry || !chunk.exports.includes('$$moduleUrl')) return null; + const build = this.environment?.config?.build; + const input = build?.lib + ? build.lib.entry + : typeof build?.ssr === 'string' + ? build.ssr + : configuredBuildInput(build); + if (!resolveConfiguredEntries(input, projectRoot).isEntryModule(chunk.facadeModuleId)) + return null; + const facade = this.getModuleInfo(chunk.facadeModuleId!); + if (facade?.importers.length || facade?.dynamicImporters.length) return null; + const stripped = stripModuleUrlExport(code); + return stripped === null ? null : { code: stripped, map: null }; + }, + generateBundle(outputOptions, bundle) { if (!isBuild || !isClientBuild(this)) return; clientOutDir = outputOptions.dir ?? null;