Skip to content

Commit 5e0249f

Browse files
test(integration): stop the cloudflare workers test from rewriting workspace dist mid-suite (#2307)
1 parent 78fbe27 commit 5e0249f

1 file changed

Lines changed: 60 additions & 8 deletions

File tree

test/integration/test/server/cloudflareWorkers.test.ts

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,63 @@ const WRANGLER_BIN = (() => {
4040
return path.resolve(path.dirname(pkgPath), bin);
4141
})();
4242

43+
/**
44+
* Create an installable tarball of `@modelcontextprotocol/server` without mutating the workspace.
45+
*
46+
* Running `pnpm pack` inside `packages/server` is not an option here: its `prepack` hook rebuilds
47+
* the package in place (tsdown with `clean: true`), deleting and rewriting `packages/server/dist`
48+
* while the rest of the test run is still going. Anything that node-resolves the workspace
49+
* packages at that moment — most notably suites that spawn child processes importing
50+
* `@modelcontextprotocol/server` — sees a half-written dist and fails. Instead, the bundle is
51+
* built into a staging directory under the test's temp dir and the tarball is created from that
52+
* staging copy, so shared, node-resolvable state never changes while tests are running.
53+
*
54+
* Returns the tarball's file name; the tarball itself is written into `tempDir`.
55+
*/
56+
function packServerPackage(tempDir: string): string {
57+
const serverPkgPath = path.resolve(__dirname, '../../../../packages/server');
58+
const stagingDir = path.join(tempDir, 'package-staging');
59+
fs.mkdirSync(stagingDir, { recursive: true });
60+
61+
// Build the publishable bundle with its output redirected away from the workspace's
62+
// packages/server/dist (the CLI flag overrides `outDir` from tsdown.config.ts).
63+
execSync(`pnpm exec tsdown --out-dir "${path.join(stagingDir, 'dist')}"`, {
64+
cwd: serverPkgPath,
65+
stdio: 'pipe',
66+
timeout: 60_000
67+
});
68+
69+
// Write a publish-shaped manifest into the staging dir: drop lifecycle scripts and
70+
// devDependencies, and resolve pnpm-only `catalog:`/`workspace:` specifiers to the versions
71+
// installed in the workspace — the same substitution `pnpm pack` performs when publishing.
72+
const manifest = JSON.parse(fs.readFileSync(path.join(serverPkgPath, 'package.json'), 'utf8')) as {
73+
scripts?: unknown;
74+
devDependencies?: unknown;
75+
dependencies?: Record<string, string>;
76+
};
77+
delete manifest.scripts;
78+
delete manifest.devDependencies;
79+
const dependencies = manifest.dependencies ?? {};
80+
for (const [name, spec] of Object.entries(dependencies)) {
81+
if (spec.startsWith('catalog:') || spec.startsWith('workspace:')) {
82+
const installed = JSON.parse(fs.readFileSync(path.join(serverPkgPath, 'node_modules', name, 'package.json'), 'utf8')) as {
83+
version: string;
84+
};
85+
dependencies[name] = installed.version;
86+
}
87+
}
88+
fs.writeFileSync(path.join(stagingDir, 'package.json'), JSON.stringify(manifest, null, 2));
89+
90+
// Pack the staging copy. The staged manifest carries no scripts, so this is a pure tar step;
91+
// npm is used because the staging dir lives outside the pnpm workspace.
92+
const packOutput = execSync(`npm pack --pack-destination "${tempDir}"`, {
93+
cwd: stagingDir,
94+
encoding: 'utf8',
95+
timeout: 60_000
96+
});
97+
return path.basename(packOutput.trim().split('\n').pop()!);
98+
}
99+
43100
/** Ask the kernel for a currently-free port instead of hardcoding one. */
44101
async function getFreePort(): Promise<number> {
45102
return new Promise((resolve, reject) => {
@@ -212,14 +269,9 @@ describe('Cloudflare Workers compatibility (no nodejs_compat)', () => {
212269
};
213270

214271
try {
215-
// Pack server package
216-
const serverPkgPath = path.resolve(__dirname, '../../../../packages/server');
217-
const packOutput = execSync(`pnpm pack --pack-destination "${tempDir}"`, {
218-
cwd: serverPkgPath,
219-
encoding: 'utf8',
220-
timeout: 60_000
221-
});
222-
const tarballName = path.basename(packOutput.trim().split('\n').pop()!);
272+
// Pack the server package into the temp dir without touching the workspace's own
273+
// dist/ — see packServerPackage for why the plain `pnpm pack` route is unsafe here.
274+
const tarballName = packServerPackage(tempDir);
223275

224276
// Write package.json
225277
const pkgJson = {

0 commit comments

Comments
 (0)