Files
hyperframes/scripts/rewrite-relative-import-extensions.test.mjs
James RussoandClaude Opus 5.5 f55b366175 fix(build): ship NodeNext-resolvable declarations from every package (#5532)
* fix(build): ship NodeNext-resolvable declarations from every package

tsc copies relative specifiers verbatim, so packages whose sources use
bundler-style extensionless imports (core, inspector) published .d.ts files
that a module/moduleResolution nodenext consumer cannot resolve. Core already
rewrote its emitted JavaScript for this, but not its declarations.

Move that rewrite to scripts/rewrite-relative-import-extensions.mjs, make it
cover .d.ts too (adding .js or /index.js, and failing the build when a
declaration import has no declaration), and run it after every tsc emit:
core, engine, sdk, producer, aws-lambda, gcp-cloud-run and inspector.

verify:packed-manifests now typechecks the clean packed consumer with
skipLibCheck false and type-only imports of every typed subpath, so an
unresolvable import inside a published declaration fails the gate and is
attributed to its package.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(ci): allow the moved core rewrite-esm-extensions script deletion

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ci: rebuild the edit-accuracy bench when the import-extension rewriter changes

The package builds now run scripts/rewrite-relative-import-extensions.mjs, so main's
edit_accuracy paths filter has to include it (check-edit-accuracy-filter test).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 05:59:41 +00:00

89 lines
3.1 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { describe, it } from "node:test";
import { rewriteRelativeImportExtensions } from "./rewrite-relative-import-extensions.mjs";
function withEmittedFiles(files, callback) {
const dir = mkdtempSync(join(tmpdir(), "hyperframes-rewrite-test-"));
try {
for (const [file, source] of Object.entries(files)) {
mkdirSync(dirname(join(dir, file)), { recursive: true });
writeFileSync(join(dir, file), source);
}
callback(dir, (file) => readFileSync(join(dir, file), "utf8"));
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
describe("relative import extension rewrite", () => {
it("points declaration imports at the JavaScript they describe", () => {
withEmittedFiles(
{
"index.d.ts": [
'import type { A } from "./types";',
'export * from "./adapters";',
'import "./side";',
'export declare const c: import("./types").A;',
'export type { D } from "./explicit.js";',
"",
].join("\n"),
"types.d.ts": "export type A = string;\n",
"adapters/index.d.ts": "export {};\n",
"b.d.ts": "export declare const b: number;\n",
"nested/c.d.ts": 'export { b } from "../b";\n',
"side.d.ts": "export {};\n",
},
(dir, read) => {
assert.equal(rewriteRelativeImportExtensions(dir), 2);
assert.equal(
read("index.d.ts"),
[
'import type { A } from "./types.js";',
'export * from "./adapters/index.js";',
'import "./side.js";',
'export declare const c: import("./types.js").A;',
'export type { D } from "./explicit.js";',
"",
].join("\n"),
);
assert.equal(read("nested/c.d.ts"), 'export { b } from "../b.js";\n');
},
);
});
it("fails the build when a declaration import has no declaration behind it", () => {
withEmittedFiles({ "index.d.ts": 'export * from "./missing";\n' }, (dir) => {
assert.throws(
() => rewriteRelativeImportExtensions(dir),
/index\.d\.ts imports \.\/missing, which has no declaration/,
);
});
});
it("resolves JavaScript against emitted JavaScript and leaves unmatched text alone", () => {
withEmittedFiles(
{
"index.js": 'export * from "./util";\nconst snippet = \'import x from "./not-emitted"\';\n',
"util.js": "export {};\n",
},
(dir, read) => {
rewriteRelativeImportExtensions(dir);
assert.equal(
read("index.js"),
'export * from "./util.js";\nconst snippet = \'import x from "./not-emitted"\';\n',
);
},
);
});
it("leaves already-correct output untouched", () => {
withEmittedFiles(
{ "index.d.ts": 'export * from "./a.js";\n', "a.d.ts": "export {};\n" },
(dir) => assert.equal(rewriteRelativeImportExtensions(dir), 0),
);
});
});