Skip to content

Commit c63255a

Browse files
cliffhallclaude
andcommitted
chore(everything): keep compiled tests and vitest config out of dist
everything's build tsconfig had no exclude, so tsc emitted __tests__/** and vitest.config.ts into dist/ and the package published them. Exclude them as filesystem does. memory and sequentialthinking excluded only *.test.ts and so packed __tests__/helpers.js; exclude __tests__/** there too. pack:verify now fails an npm tarball that ships a test, test helper or test-runner config, so this cannot come back in any server. Closes #4930 Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: cliffhall <[email protected]>
1 parent b0d865d commit c63255a

10 files changed

Lines changed: 106 additions & 16 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@modelcontextprotocol/server-everything": patch
3+
---
4+
5+
Stop shipping the compiled test suite and Vitest config in the published package: the build now excludes `__tests__/`, `*.test.ts`, `*.spec.ts` and `vitest.config.ts`, as the other TypeScript servers' builds do.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@modelcontextprotocol/server-memory": patch
3+
---
4+
5+
Stop shipping the compiled test helper `dist/__tests__/helpers.js` in the published package: the build now excludes everything under `__tests__/`, not just `*.test.ts`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@modelcontextprotocol/server-sequential-thinking": patch
3+
---
4+
5+
Stop shipping the compiled test helper `dist/__tests__/helpers.js` in the published package: the build now excludes everything under `__tests__/`, not just `*.test.ts`.

‎docs/quality-gate.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ covers: the repo-wide tooling and the guards that keep the gate itself honest.
132132
tarball, the wheel), installs it into an empty directory and boots the
133133
installed server. It catches what the boot smoke cannot, since the smoke
134134
runs the checkout: a file missing from the tarball, a dependency that only
135-
resolves inside the workspace. It needs the network, so it is not a gate
135+
resolves inside the workspace. It also fails an npm tarball that ships a
136+
test, a test helper or a test runner's config. It needs the network, so it is not a gate
136137
stage. `release.yml` runs it before anything publishes, and the `release`
137138
skill runs it by hand for the release ledger.
138139
- **`npm run format`** rewrites files, so it is something you run, not

‎scripts/pack-and-verify.mjs‎

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,10 @@ export function parseArgs(argv) {
101101

102102
/**
103103
* What is wrong with an npm tarball's contents, judged from its manifest and
104-
* file list alone: every `bin` target must be in it. Anything subtler (a
105-
* module the entry point imports, a data file) is found by actually running
106-
* the installed bin, which is the next step.
104+
* file list alone: every `bin` target must be in it, and no test or test
105+
* config file may be (#4930). Anything subtler (a module the entry point
106+
* imports, a data file) is found by actually running the installed bin, which
107+
* is the next step.
107108
*
108109
* @param {{ bin?: string | Record<string, string> }} manifest the package.json
109110
* @param {string[]} files the paths in the tarball, relative to the package
@@ -113,12 +114,34 @@ export function tarballProblems(manifest, files) {
113114
const bins = binEntries(manifest);
114115
if (bins.length === 0) return ["package.json declares no `bin`"];
115116
const present = new Set(files.map((f) => path.posix.normalize(f)));
116-
return bins
117-
.filter(([, target]) => !present.has(path.posix.normalize(target)))
118-
.map(
119-
([name, target]) =>
120-
`bin \`${name}\` points at ${target}, which is not in the tarball`,
121-
);
117+
return [
118+
...bins
119+
.filter(([, target]) => !present.has(path.posix.normalize(target)))
120+
.map(
121+
([name, target]) =>
122+
`bin \`${name}\` points at ${target}, which is not in the tarball`,
123+
),
124+
...files
125+
.filter(isTestFile)
126+
.map((f) => `${f} is a test file, which a package does not ship`),
127+
];
128+
}
129+
130+
/**
131+
* Whether a packed path is a test, a test helper or a test runner's config,
132+
* compiled or not: anything under a `__tests__` directory, a `*.test.*` or
133+
* `*.spec.*` file, or a `vitest.config.*`.
134+
*
135+
* @param {string} file a path in the tarball, relative to the package
136+
*/
137+
export function isTestFile(file) {
138+
const parts = path.posix.normalize(file).split("/");
139+
const base = parts.at(-1);
140+
return (
141+
parts.slice(0, -1).includes("__tests__") ||
142+
/\.(test|spec)\.(d\.)?[cm]?[jt]sx?(\.map)?$/.test(base) ||
143+
/^vitest\.config\./.test(base)
144+
);
122145
}
123146

124147
/** A manifest's `bin` as `[command, target]` pairs, in either of its forms. */

‎scripts/pack-and-verify.test.mjs‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
consumerEnv,
2121
installedLaunch,
2222
isDistribution,
23+
isTestFile,
2324
main,
2425
parseArgs,
2526
tarballProblems,
@@ -71,6 +72,44 @@ test("the string form of bin is read, and a missing bin is a problem", () => {
7172
]);
7273
});
7374

75+
test("a test or test-config file in the tarball is named (#4930)", () => {
76+
const manifest = { name: "@scope/pkg", bin: { cmd: "dist/index.js" } };
77+
assert.deepEqual(
78+
tarballProblems(manifest, [
79+
"package.json",
80+
"dist/index.js",
81+
"dist/__tests__/tools.test.js",
82+
"dist/vitest.config.js",
83+
]),
84+
[
85+
"dist/__tests__/tools.test.js is a test file, which a package does not ship",
86+
"dist/vitest.config.js is a test file, which a package does not ship",
87+
],
88+
);
89+
});
90+
91+
test("isTestFile matches tests, helpers and runner configs, nothing else", () => {
92+
for (const f of [
93+
"dist/__tests__/helpers.js",
94+
"dist/__tests__/server.test.d.ts",
95+
"dist/tools/echo.test.js",
96+
"dist/tools/echo.spec.mjs",
97+
"dist/tools/echo.test.js.map",
98+
"dist/vitest.config.js",
99+
"vitest.config.ts",
100+
])
101+
assert.equal(isTestFile(f), true, f);
102+
for (const f of [
103+
"dist/index.js",
104+
"dist/tools/test-helpers.js",
105+
"dist/tools/get-test.js",
106+
"dist/docs/testing.md",
107+
"dist/__tests__.js",
108+
"README.md",
109+
])
110+
assert.equal(isTestFile(f), false, f);
111+
});
112+
74113
test("installedLaunch runs what each installer put in place", () => {
75114
const c = path.join("tmp", "consumer");
76115
const bin = {

‎src/everything/tsconfig.json‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,14 @@
44
"outDir": "./dist",
55
"rootDir": "."
66
},
7-
"include": ["./**/*.ts"]
7+
"include": ["./**/*.ts"],
8+
"exclude": [
9+
"node_modules",
10+
"dist",
11+
"coverage",
12+
"**/__tests__/**",
13+
"**/*.test.ts",
14+
"**/*.spec.ts",
15+
"vitest.config.ts"
16+
]
817
}

‎src/everything/tsconfig.test.json‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
{
2-
// The no-emit typecheck program for the whole workspace (#4864). Here the
3-
// build `tsconfig.json` already includes the tests; this file exists so every
4-
// workspace has the same `tsconfig.test.json` that `npm run typecheck` runs
5-
// and the root ESLint config uses as its parser project, so what is linted
6-
// and what is typechecked are the same file set in every server.
2+
// The typecheck program for the whole workspace (#4864). `tsconfig.json` is
3+
// the build config and leaves the tests out, so tsc does not emit them into
4+
// `dist/` and the published package does not ship them (#4930); this one
5+
// adds them back without emitting anything. `npm run typecheck` runs it, and
6+
// it is the parser project the root ESLint config uses, so what is linted
7+
// and what is typechecked are the same file set.
78
"extends": "./tsconfig.json",
89
"compilerOptions": {
910
"noEmit": true

‎src/memory/tsconfig.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"./**/*.ts"
99
],
1010
"exclude": [
11+
"**/__tests__/**",
1112
"**/*.test.ts",
1213
"vitest.config.ts"
1314
]

‎src/sequentialthinking/tsconfig.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"./**/*.ts"
99
],
1010
"exclude": [
11+
"**/__tests__/**",
1112
"**/*.test.ts",
1213
"vitest.config.ts"
1314
]

0 commit comments

Comments
 (0)