Skip to content

Commit 35b3755

Browse files
test: make barrelClean chunk scan transitive (BFS) and CI-safe; map /stdio in quickstart tsconfig paths
- chunkImportsOf now BFS-walks the chunk import graph so the test name ('transitively imported') matches the implementation - beforeAll builds the package if dist/ is missing (CI test job runs pnpm test:all without a build step) - examples/{client,server}-quickstart/tsconfig.json: add /stdio to paths so tsc --noEmit resolves the new subpath without a prior build
1 parent 46fe6d2 commit 35b3755

4 files changed

Lines changed: 50 additions & 10 deletions

File tree

examples/client-quickstart/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"forceConsistentCasingInFileNames": true,
1313
"paths": {
1414
"@modelcontextprotocol/client": ["./node_modules/@modelcontextprotocol/client/src/index.ts"],
15+
"@modelcontextprotocol/client/stdio": ["./node_modules/@modelcontextprotocol/client/src/stdio.ts"],
1516
"@modelcontextprotocol/client/_shims": ["./node_modules/@modelcontextprotocol/client/src/shimsNode.ts"],
1617
"@modelcontextprotocol/core": [
1718
"./node_modules/@modelcontextprotocol/client/node_modules/@modelcontextprotocol/core/src/index.ts"

examples/server-quickstart/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"forceConsistentCasingInFileNames": true,
1212
"paths": {
1313
"@modelcontextprotocol/server": ["./node_modules/@modelcontextprotocol/server/src/index.ts"],
14+
"@modelcontextprotocol/server/stdio": ["./node_modules/@modelcontextprotocol/server/src/stdio.ts"],
1415
"@modelcontextprotocol/server/_shims": ["./node_modules/@modelcontextprotocol/server/src/shimsNode.ts"],
1516
"@modelcontextprotocol/core": [
1617
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/src/index.ts"

packages/client/test/client/barrelClean.test.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
1-
import { readFileSync } from 'node:fs';
1+
import { execFileSync } from 'node:child_process';
2+
import { existsSync, readFileSync } from 'node:fs';
23
import { dirname, join } from 'node:path';
34
import { fileURLToPath } from 'node:url';
45

5-
import { describe, expect, test } from 'vitest';
6+
import { beforeAll, describe, expect, test } from 'vitest';
67

7-
const distDir = join(dirname(fileURLToPath(import.meta.url)), '../../dist');
8+
const pkgDir = join(dirname(fileURLToPath(import.meta.url)), '../..');
9+
const distDir = join(pkgDir, 'dist');
810
const NODE_ONLY = /\b(child_process|cross-spawn|node:stream|node:child_process)\b/;
911

1012
function chunkImportsOf(entryPath: string): string[] {
11-
const src = readFileSync(entryPath, 'utf8');
12-
return [...src.matchAll(/from\s+["']\.\/(.+?\.mjs)["']/g)].map(m => join(distDir, m[1]!));
13+
const visited = new Set<string>();
14+
const queue = [entryPath];
15+
while (queue.length > 0) {
16+
const file = queue.shift()!;
17+
if (visited.has(file)) continue;
18+
visited.add(file);
19+
const src = readFileSync(file, 'utf8');
20+
for (const m of src.matchAll(/from\s+["']\.\/(.+?\.mjs)["']/g)) {
21+
queue.push(join(dirname(file), m[1]!));
22+
}
23+
}
24+
visited.delete(entryPath);
25+
return [...visited];
1326
}
1427

1528
describe('@modelcontextprotocol/client root entry is browser-safe', () => {
29+
beforeAll(() => {
30+
if (!existsSync(join(distDir, 'index.mjs')) || !existsSync(join(distDir, 'stdio.mjs'))) {
31+
execFileSync('pnpm', ['build'], { cwd: pkgDir, stdio: 'inherit' });
32+
}
33+
}, 60_000);
34+
1635
test('dist/index.mjs contains no process-spawning runtime imports', () => {
1736
const entry = join(distDir, 'index.mjs');
1837
expect(readFileSync(entry, 'utf8')).not.toMatch(NODE_ONLY);

packages/server/test/server/barrelClean.test.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
1-
import { readFileSync } from 'node:fs';
1+
import { execFileSync } from 'node:child_process';
2+
import { existsSync, readFileSync } from 'node:fs';
23
import { dirname, join } from 'node:path';
34
import { fileURLToPath } from 'node:url';
45

5-
import { describe, expect, test } from 'vitest';
6+
import { beforeAll, describe, expect, test } from 'vitest';
67

7-
const distDir = join(dirname(fileURLToPath(import.meta.url)), '../../dist');
8+
const pkgDir = join(dirname(fileURLToPath(import.meta.url)), '../..');
9+
const distDir = join(pkgDir, 'dist');
810
const NODE_ONLY = /\b(node:stream|node:child_process)\b/;
911

1012
function chunkImportsOf(entryPath: string): string[] {
11-
const src = readFileSync(entryPath, 'utf8');
12-
return [...src.matchAll(/from\s+["']\.\/(.+?\.mjs)["']/g)].map(m => join(distDir, m[1]!));
13+
const visited = new Set<string>();
14+
const queue = [entryPath];
15+
while (queue.length > 0) {
16+
const file = queue.shift()!;
17+
if (visited.has(file)) continue;
18+
visited.add(file);
19+
const src = readFileSync(file, 'utf8');
20+
for (const m of src.matchAll(/from\s+["']\.\/(.+?\.mjs)["']/g)) {
21+
queue.push(join(dirname(file), m[1]!));
22+
}
23+
}
24+
visited.delete(entryPath);
25+
return [...visited];
1326
}
1427

1528
describe('@modelcontextprotocol/server root entry is browser-safe', () => {
29+
beforeAll(() => {
30+
if (!existsSync(join(distDir, 'index.mjs')) || !existsSync(join(distDir, 'stdio.mjs'))) {
31+
execFileSync('pnpm', ['build'], { cwd: pkgDir, stdio: 'inherit' });
32+
}
33+
}, 60_000);
34+
1635
test('dist/index.mjs contains no process-stdio runtime imports', () => {
1736
const entry = join(distDir, 'index.mjs');
1837
expect(readFileSync(entry, 'utf8')).not.toMatch(NODE_ONLY);

0 commit comments

Comments
 (0)