diff --git a/packages/void/src/cli/cf-build-output.ts b/packages/void/src/cli/cf-build-output.ts index 316357f34..594aefaf2 100644 --- a/packages/void/src/cli/cf-build-output.ts +++ b/packages/void/src/cli/cf-build-output.ts @@ -554,6 +554,10 @@ async function prepareBundledBuildOutput(options: { configFile: false, root: options.projectRoot, publicDir: false, + // `.env` is local-only. The framework build drops its client values, but modules it left + // as imports (an installed `void/env` in a SvelteKit D1 app) are compiled here, and Vite + // would fill their `import.meta.env` from the project's `.env`. Shell values still apply. + envDir: false, logLevel: 'error', plugins: [additionalModulesPlugin()], // Like @cloudflare/vite-plugin: Worker conditions plus the build mode. A deploy diff --git a/packages/void/test/unit/cf-build-output.test.ts b/packages/void/test/unit/cf-build-output.test.ts index 0638abad6..c85ef6a01 100644 --- a/packages/void/test/unit/cf-build-output.test.ts +++ b/packages/void/test/unit/cf-build-output.test.ts @@ -800,6 +800,61 @@ it( CF_BUILD_TIMEOUT_MS, ); +it( + 'keeps local .env client values out of framework Worker output', + async () => { + const root = mkdtempSync(join(tmpdir(), 'void-cf-framework-env-test-')); + roots.push(root); + const output = join(root, 'framework'); + mkdirSync(output); + writeFileSync(join(root, 'package.json'), JSON.stringify({ type: 'module' })); + writeFileSync( + join(root, 'wrangler.json'), + JSON.stringify({ + name: 'void-framework-test', + account_id: 'account-id', + main: 'framework/_worker.js', + compatibility_date: '2026-09-29', + assets: { directory: 'framework', binding: 'ASSETS' }, + }), + ); + writeFileSync(join(root, '.env'), 'VITE_ORIGIN=http://localhost:5173\n'); + vi.stubEnv('VITE_SHELL_ORIGIN', 'https://shell.example.com'); + // Reads Vite's env object as a whole, like an installed void/env that the framework build + // left as an import. + const reader = join(root, 'node_modules/env-reader'); + mkdirSync(reader, { recursive: true }); + writeFileSync( + join(reader, 'package.json'), + JSON.stringify({ name: 'env-reader', type: 'module', exports: './index.js' }), + ); + writeFileSync(join(reader, 'index.js'), 'export const values = import.meta.env;\n'); + writeFileSync( + join(output, '_worker.js'), + "import { values } from 'env-reader';\n" + + 'export default { fetch() { return Response.json(values) } };\n', + ); + writeFileSync(join(output, '.assetsignore'), '_worker.js\n'); + + await withPrepared( + prepareCfFrameworkBuildOutput({ + projectRoot: root, + configPath: join(root, 'wrangler.json'), + accountId: 'account-id', + }), + async (prepared) => { + const bundle = readFileSync( + join(getWorkerBundleDir(prepared.directory), 'index.js'), + 'utf8', + ); + expect(bundle).toContain('https://shell.example.com'); + expect(bundle).not.toContain('http://localhost:5173'); + }, + ); + }, + CF_BUILD_TIMEOUT_MS, +); + it( 'accepts a nested hashed Worker entry with a relative module import', async () => {