Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .github/scripts/packed-consumer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ try {
join(fixture, 'routes/api/packed.ts'),
`import { defineHandler } from 'void';\nimport { kv } from 'void/kv';\nexport const GET = defineHandler(async () => { await kv.put('packed', 'ok'); return { value: await kv.get('packed') }; });\n`,
);
// void/email pulls a CommonJS dependency into the Worker; only an installed package shows whether dev serves it.
writeFileSync(
join(fixture, 'routes/api/packed-email.ts'),
`import { defineHandler } from 'void';\nimport { sendEmail } from 'void/email';\nexport const GET = defineHandler(async () => { return sendEmail({ from: 'packed@example.com', to: 'inbox@example.com', subject: 'Packed', text: 'ok' }); });\n`,
);
mkdirSync(join(fixture, 'public'), { recursive: true });
writeFileSync(join(fixture, 'public/packed.txt'), label);
const vite = join(fixture, 'node_modules/vite/bin/vite.js');
Expand Down Expand Up @@ -335,6 +340,16 @@ if (runtime.manifest.platformVersion !== ${JSON.stringify(version)}) throw new E
const apiBody = await apiResponse.text();
assert.equal(apiResponse.status, 200, `${apiBody}\n${task.failure()}`);
assert.deepEqual(JSON.parse(apiBody), { value: 'ok' });
const emailResponse = await fetch(`${base}/api/packed-email`);
const emailBody = await emailResponse.text();
assert.equal(emailResponse.status, 200, `${emailBody}\n${task.failure()}`);
// Preview runs the built Worker: no dev inbox and no send binding.
const email = JSON.parse(emailBody);
if (mode === 'dev') {
assert.equal(email.ok, true, emailBody);
} else {
assert.equal(email.error?.code, 'BINDING_MISSING', emailBody);
}
if (full && framework === 'react') {
const markdown = await fetch(`${base}/markdown`);
assert.equal(markdown.status, 200, task.failure());
Expand Down Expand Up @@ -375,7 +390,7 @@ if (runtime.manifest.platformVersion !== ${JSON.stringify(version)}) throw new E
}
}
console.log(
`Passed packed ${label} (${suite}): init, frozen install, public Vite build/typecheck, assets, KV, runtime integrity${full ? ', dev/preview, hydration/navigation' : ', dev HTTP'}`,
`Passed packed ${label} (${suite}): init, frozen install, public Vite build/typecheck, assets, KV, email, runtime integrity${full ? ', dev/preview, hydration/navigation' : ', dev HTTP'}`,
);
} catch (error) {
failures.push({ fixture: label, error: String(error.stack ?? error) });
Expand Down
4 changes: 3 additions & 1 deletion packages/void/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ export function voidPlugin(options?: {
// graph and therefore another envContext, so request bindings disappear for
// the prebundled copy. Every return path includes this plugin, including
// framework and Node targets that do not all use bindingRuntimePlugin.
const serverRuntimeOptimizerPlugin = createServerRuntimeOptimizerPlugin();
const serverRuntimeOptimizerPlugin = createServerRuntimeOptimizerPlugin(
!isNodeTarget(config.target) && !frameworkOwnsCloudflareRuntime,
);
// Scan queues/websocket routes early so both framework-mode and default-mode
// branches can safely close over these bindings.
const queues = framework ? [] : application.queues;
Expand Down
17 changes: 15 additions & 2 deletions packages/void/src/plugin-worker-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,26 @@ export function createWranglerCompatPersistPlugin(
};
}

export function createServerRuntimeOptimizerPlugin(): Plugin {
export function createServerRuntimeOptimizerPlugin(voidOwnsWorkerRuntime: boolean): Plugin {
const serverRuntimeOptimizerPlugin: Plugin = {
name: 'void:server-runtime-optimizer',
configEnvironment(name) {
if (name !== 'client') {
if (name === 'client') {
return;
}
if (!voidOwnsWorkerRuntime) {
return { optimizeDeps: { exclude: ['void'] } };
}
return {
optimizeDeps: {
exclude: ['void'],
// Excluding void also skips converting its CommonJS dependencies, which workerd cannot
// evaluate: an installed void/email fails with "exports is not defined". Runtimes a
// framework owns stay untouched; Node loads CommonJS itself, and forcing an include
// there turns on SSR prebundling the framework did not ask for.
include: ['void > email-validator'],
},
};
},
};

Expand Down
18 changes: 17 additions & 1 deletion packages/void/test/unit/plugin-dev-asset-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ function createFrameworkProjectRoot(): string {
return root;
}

function createFrameworkOwnedRuntimeProjectRoot(): string {
const root = createProjectRoot();
writeFileSync(
join(root, 'package.json'),
JSON.stringify({ type: 'module', dependencies: { '@analogjs/platform': '^2.0.0' } }),
);
return root;
}

function createNodeProjectRoot(): string {
const root = createProjectRoot();
writeFileSync(
Expand Down Expand Up @@ -220,12 +229,19 @@ describe('voidPlugin dev asset routing', () => {
});

it('keeps one Void runtime module graph in core, framework, and Node servers', async () => {
const workerdOptimizer = {
optimizeDeps: { exclude: ['void'], include: ['void > email-validator'] },
};
await expect(
resolveRuntimeOptimizerConfig(createApiOnlyProjectRoot(), 'void_worker'),
).resolves.toEqual({ optimizeDeps: { exclude: ['void'] } });
).resolves.toEqual(workerdOptimizer);
vi.restoreAllMocks();
await expect(
resolveRuntimeOptimizerConfig(createFrameworkProjectRoot(), 'ssr'),
).resolves.toEqual(workerdOptimizer);
vi.restoreAllMocks();
await expect(
resolveRuntimeOptimizerConfig(createFrameworkOwnedRuntimeProjectRoot(), 'ssr'),
).resolves.toEqual({ optimizeDeps: { exclude: ['void'] } });
vi.restoreAllMocks();
await expect(resolveRuntimeOptimizerConfig(createNodeProjectRoot(), 'ssr')).resolves.toEqual({
Expand Down
Loading