Skip to content

Commit f651d8a

Browse files
committed
test(cli): the subpath pin asserts the ts-morph borrow before making it
The pin symlinks `ts-morph` in from the workspace so the packed extractor can be EXECUTED and not merely resolved. That borrow was unconditional: had the entry left `dependencies` (it is `^28.0.0` there today), an installed copy of the tarball would fail the free-identifiers path with ERR_MODULE_NOT_FOUND while this pin — handing itself a copy no consumer receives — stayed green. A pin asserting a public surface works when it does not. `beforeAll` now asserts `MANIFEST.dependencies['ts-morph']` before symlinking, with a failure message naming what the borrow hides and what a real consumer would hit instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
1 parent 90e72f9 commit f651d8a

1 file changed

Lines changed: 21 additions & 0 deletions

File tree

packages/cli/test/published-subpath-hook-body.pin.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
* card measured. The one thing borrowed from the workspace is the tarball's own
4242
* runtime dependency `ts-morph`, symlinked in so the extractor can be EXECUTED
4343
* from the packed copy and not merely resolved; resolution never consults it.
44+
* That borrow is guarded rather than assumed: `beforeAll` asserts the manifest
45+
* still declares `ts-morph` under `dependencies` before symlinking, because the
46+
* copy this file hands over is a copy a real consumer would never receive.
4447
*
4548
* ## What this file deliberately does NOT do
4649
*
@@ -86,6 +89,12 @@ interface Manifest {
8689
name: string;
8790
version: string;
8891
exports: ExportsMap;
92+
/**
93+
* Optional on purpose: a manifest that no longer declares `ts-morph` has to be
94+
* REPRESENTABLE here, so the borrow guard in `beforeAll` is what fails — not
95+
* a type assertion quietly promising a key the file on disk may not carry.
96+
*/
97+
dependencies?: Record<string, string>;
8998
}
9099

91100
const MANIFEST = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8')) as Manifest;
@@ -281,6 +290,18 @@ beforeAll(() => {
281290
// The extractor's one runtime dependency, so `import()` can EXECUTE it from
282291
// the packed copy. A real-path symlink: pnpm's store keeps ts-morph's own
283292
// dependencies beside the real directory, and Node resolves from there.
293+
//
294+
// ⛔ Never borrow it unconditionally. A consumer receives `ts-morph` only
295+
// because the PUBLISHED manifest declares it a runtime dependency; this file
296+
// hands itself a copy the consumer would not have, so the premise is asserted
297+
// BEFORE the symlink can paper over its absence.
298+
expect(
299+
MANIFEST.dependencies?.['ts-morph'],
300+
`${PACKAGE_NAME} must declare ts-morph in "dependencies" — the symlink below borrows it from this workspace, ` +
301+
'but an installed copy of the tarball receives it only from that manifest entry. Moved to devDependencies or ' +
302+
'dropped, the free-identifiers path would fail with ERR_MODULE_NOT_FOUND for every real consumer while this ' +
303+
'pin, supplying its own copy, stayed green.',
304+
).toBeTypeOf('string');
284305
symlinkSync(realpathSync(join(PACKAGE_ROOT, 'node_modules', 'ts-morph')), join(consumer, 'node_modules', 'ts-morph'), 'dir');
285306

286307
const probePath = join(consumer, 'probe.mjs');

0 commit comments

Comments
 (0)