diff --git a/scripts/check-dynamic-import-warts.mjs b/scripts/check-dynamic-import-warts.mjs index 9991cfeb232b..7a19b700afc8 100644 --- a/scripts/check-dynamic-import-warts.mjs +++ b/scripts/check-dynamic-import-warts.mjs @@ -41,7 +41,16 @@ function isTypeOnlyImportDeclaration(node) { } function isTypeOnlyExportDeclaration(node) { - return node.isTypeOnly === true; + if (node.isTypeOnly === true) { + return true; + } + const clause = node.exportClause; + return ( + Boolean(clause) && + ts.isNamedExports(clause) && + clause.elements.length > 0 && + clause.elements.every((element) => element.isTypeOnly) + ); } function readDeclarationName(node) { diff --git a/test/scripts/check-dynamic-import-warts.test.ts b/test/scripts/check-dynamic-import-warts.test.ts index bc9a4d078729..e21f07f432b9 100644 --- a/test/scripts/check-dynamic-import-warts.test.ts +++ b/test/scripts/check-dynamic-import-warts.test.ts @@ -54,6 +54,33 @@ describe("check-dynamic-import-warts", () => { expect(findDynamicImportAdvisories(source)).toStrictEqual([]); }); + it("ignores inline type-only static re-exports", () => { + const source = ` + export { type Runtime, type RuntimeOptions } from "./runtime.js"; + export async function start() { + return (await import("./runtime.js")).createRuntime(); + } + `; + expect(findDynamicImportAdvisories(source)).toStrictEqual([]); + }); + + it("flags mixed runtime and inline type-only static re-exports", () => { + const source = ` + let runtimePromise: Promise | undefined; + function loadRuntime() { + runtimePromise ??= import("./runtime.js"); + return runtimePromise; + } + export { type Runtime, createRuntime } from "./runtime.js"; + `; + expect(findDynamicImportAdvisories(source)).toEqual([ + { + line: 4, + reason: 'runtime static + dynamic import of "./runtime.js" (static line 7)', + }, + ]); + }); + it("ignores local export declarations without module specifiers", () => { const source = ` const run = true;