Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions scripts/check-turbo-typecheck-inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ export function collectCrossBoundaryReach(root: string): CrossBoundaryReach[] {
const reach = new Map<string, string>();
for (const file of files) {
const source = readFileSync(file, "utf8");
// Both `from "..."` and bare `import "..."`, since a side-effect import is type-checked too.
for (const match of source.matchAll(/(?:from|import)\s+"((?:\.\.\/)+[^"]+)"/g)) {
// Static `from "..."`, bare `import "..."`, and dynamic `import("...")` (incl. multi-line) — side-effect
// and JSON imports are type-checked too. Mirror check-dead-source-files.ts's optional-paren form (#10046).
for (const match of source.matchAll(/(?:from|import)\s*\(?\s*"((?:\.\.\/)+[^"]+)"/g)) {
const specifier = match[1];
if (!specifier) continue;
const segments = /(?:^|\/)(packages|apps)\/([^/]+)\/([^/"]+)/.exec(specifier);
Expand Down
40 changes: 40 additions & 0 deletions test/unit/check-turbo-typecheck-inputs.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { describe, expect, it } from "vitest";
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { collectCrossBoundaryReach, coveredWorkspacesFromDependsOn, findUnhashedReach, parseJsonc } from "../../scripts/check-turbo-typecheck-inputs";

// turbo.json's //#typecheck inputs are a hand-maintained approximation of tsc's real transitive surface. Its
Expand Down Expand Up @@ -52,6 +55,43 @@ describe("collectCrossBoundaryReach", () => {
expect(paths).toContain("packages/loopover-mcp/lib");
});

it("REGRESSION (#10046): dynamic import() reaches package.json the same as static from", () => {
// The old regex required whitespace after `import` before `"`, so `import("...")` never matched.
// miner-cli.test.ts uses a multi-line dynamic JSON import — pin both forms against a synthetic tree.
const root = mkdtempSync(join(tmpdir(), "turbo-inputs-10046-"));
try {
mkdirSync(join(root, "test", "unit"), { recursive: true });
mkdirSync(join(root, "packages", "loopover-miner", "lib"), { recursive: true });
writeFileSync(join(root, "packages", "loopover-miner", "package.json"), '{"name":"@loopover/miner"}\n');
writeFileSync(join(root, "packages", "loopover-miner", "lib", "version.ts"), "export const v = 1;\n");
writeFileSync(
join(root, "test", "unit", "dyn-import.test.ts"),
[
'it("dyn", async () => {',
" const packageJson = await import(",
' "../../packages/loopover-miner/package.json",',
' { with: { type: "json" } }',
");",
"});",
"",
].join("\n"),
);
writeFileSync(
join(root, "test", "unit", "static-from.test.ts"),
'import { v } from "../../packages/loopover-miner/lib/version";\nvoid v;\n',
);

const reached = collectCrossBoundaryReach(root);
const paths = reached.map((entry) => entry.path);
expect(paths).toContain("packages/loopover-miner/package.json");
expect(paths).toContain("packages/loopover-miner/lib");
const dyn = reached.find((entry) => entry.path === "packages/loopover-miner/package.json");
expect(dyn?.importedBy.replace(/\\/g, "/")).toMatch(/test\/unit\/dyn-import\.test\.ts$/);
} finally {
rmSync(root, { recursive: true, force: true });
}
});

it("INVARIANT: this repo's own turbo.json covers its real reach", async () => {
// The check the CI script performs, asserted here too so a `turbo.json` edit that drops a path fails in
// the test suite as well as the standalone checker.
Expand Down
1 change: 1 addition & 0 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"package-lock.json",
"packages/loopover-miner/lib/**",
"packages/loopover-miner/bin/**",
"packages/loopover-miner/package.json",
"packages/loopover-mcp/package.json",
"packages/discovery-index/src/**",
"packages/discovery-index/scripts/**",
Expand Down