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
5 changes: 5 additions & 0 deletions .changeset/server-functions-host-environments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@solidjs/vite-plugin': patch
---

In dev, the server function manifest is now invalidated in every Vite environment, not just `client` and `ssr`. A host that imports the generated handler from a server environment of its own, such as Void's `void_worker` or a Cloudflare worker, kept a stale manifest before. A server function that only the client referenced then failed with "not part of the deployment" until the dev server restarted.
63 changes: 63 additions & 0 deletions src/server-functions/environments.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { mkdtemp, mkdir, realpath, rm, symlink, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { createServer, type ViteDevServer } from 'vite';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { serverFunctions } from './index.js';

const MANIFEST_ID = 'virtual:solid-server-function-manifest';

// A host can own the server side under its own environment name (Void's
// `void_worker`, a Cloudflare worker) and import the manifest there. A server
// function the client reaches first has to show up in that environment's
// manifest too.
describe('server function manifest in a host-named server environment', () => {
let root: string;
let server: ViteDevServer;

async function loadManifest() {
const result = await server.environments.worker!.transformRequest(MANIFEST_ID);
return result?.code ?? '';
}

beforeAll(async () => {
root = await realpath(await mkdtemp(path.join(tmpdir(), 'solid-vite-plugin-env-')));
await symlink(
fileURLToPath(new URL('../../node_modules', import.meta.url)),
path.join(root, 'node_modules'),
'dir',
);
await mkdir(path.join(root, 'src'));
await writeFile(
path.join(root, 'src/greet.ts'),
'"use server";\nexport async function greet(name: string) {\n return `hello ${name}`;\n}\n',
);
server = await createServer({
root,
configFile: false,
appType: 'custom',
logLevel: 'silent',
server: { middlewareMode: true, hmr: false, watch: null },
optimizeDeps: { noDiscovery: true, include: [] },
environments: {
client: {},
worker: { consumer: 'server' },
},
plugins: [serverFunctions()],
});
});

afterAll(async () => {
await server?.close();
await rm(root, { recursive: true, force: true });
});

it('invalidates the manifest in that environment when the client finds a new module', async () => {
expect(await loadManifest()).not.toContain('/src/greet.ts');

await server.environments.client.transformRequest('/src/greet.ts');

expect(await loadManifest()).toContain('"/src/greet.ts"');
});
});
9 changes: 7 additions & 2 deletions src/server-functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,13 @@ function invalidateModules(
manifest: string,
): void {
if (server?.environments && result.invalidPreload) {
invalidateModule(server.environments.client.moduleGraph, manifest);
invalidateModule(server.environments.ssr.moduleGraph, manifest);
// Every environment, not just `client` and `ssr`: a host can load the
// manifest from a server environment of its own (`void_worker`, a
// Cloudflare worker), and one with no `ssr` environment at all would
// otherwise throw here.
for (const environment of Object.values(server.environments)) {
invalidateModule(environment.moduleGraph, manifest);
}
}
}

Expand Down
Loading