commit e2de3bafc1eb2b004ecd81e55687e2b5a33fbc94
parent 8c8f80fa87cec78e941aa7dec3fd9dcbc30b245f
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 29 Sep 2026 22:52:15 -0400
scripts: review L1 + L2 + L3 — only the build's own cache/ and dev/ are left unread (a test pins it); the post-build check's comment says that with the excludes in place it sees such a pattern only through a name they miss; "left 31 of the 68 routes clean"
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
1 file changed, 35 insertions(+), 10 deletions(-)
diff --git a/scripts/next-build-trace.test.mjs b/scripts/next-build-trace.test.mjs
@@ -26,8 +26,8 @@
// ignored either: it is a dynamic part, and a path or fs call on it becomes a
// PATTERN over the app's own directory. Measured in umtool (release 15, slice
// UT): the path ops on env and home-directory values in its path modules took
-// in the app's whole tree outside dot-directories (opting them out cleaned 31
-// of 68 routes), and the clip-audio route's join with a dynamic extension took
+// in the app's whole tree outside dot-directories (opting them out left 31 of
+// the 68 routes clean), and the clip-audio route's join with a dynamic extension took
// in the dot-directories too, the e2e fixture and `.env.local` among them.
// Neither walk entered a symlinked directory. No static check here can tell
// such a pattern from a harmless one, so the last test reads a build's traces
@@ -35,7 +35,8 @@
//
// Run with: pnpm test:scripts
import assert from "node:assert/strict";
-import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
+import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
+import { tmpdir } from "node:os";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
@@ -382,12 +383,16 @@ test("the scan set follows relative imports out of the listed folders", () => {
assert.ok(!apps.has("common/bin/compose-site.ts"), "a common CLI no app imports is scanned");
});
-/** Every `.nft.json` a build wrote under `dir`, its cache and dev-server output aside. */
-function traceFilesUnder(dir, out = []) {
+/**
+ * Every `.nft.json` a build wrote under its directory `dist`, its cache and
+ * dev-server output (`dist/cache`, `dist/dev`) aside. Only those two: a route
+ * directory named `cache` or `dev` deeper down is read like any other.
+ */
+function traceFilesUnder(dist, dir = dist, out = []) {
for (const e of readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, e.name);
if (e.isDirectory()) {
- if (e.name !== "cache" && e.name !== "dev") traceFilesUnder(p, out);
+ if (dir !== dist || (e.name !== "cache" && e.name !== "dev")) traceFilesUnder(dist, p, out);
} else if (e.name.endsWith(".nft.json")) out.push(p);
}
return out;
@@ -418,6 +423,20 @@ export function forbiddenTrace(abs, dist) {
return null;
}
+test("traceFilesUnder: only the build's own cache/ and dev/ are left out", () => {
+ const dist = mkdtempSync(path.join(tmpdir(), "next-build-trace-"));
+ try {
+ for (const f of ["cache/a.nft.json", "dev/b.nft.json", "server/app/api/cache/route.js.nft.json", "server/app/dev/page.js.nft.json"]) {
+ mkdirSync(path.dirname(path.join(dist, f)), { recursive: true });
+ writeFileSync(path.join(dist, f), '{"files":[]}');
+ }
+ const found = traceFilesUnder(dist).map((f) => path.relative(dist, f)).sort();
+ assert.deepEqual(found, ["server/app/api/cache/route.js.nft.json", "server/app/dev/page.js.nft.json"]);
+ } finally {
+ rmSync(dist, { recursive: true, force: true });
+ }
+});
+
test("forbiddenTrace: a fixture, another build, a secret, the corpus, outside the repo", () => {
const dist = path.join(UMTOOL, ".next");
const at = (p) => forbiddenTrace(path.join(REPO, p), dist);
@@ -434,10 +453,16 @@ test("forbiddenTrace: a fixture, another build, a secret, the corpus, outside th
// The static checks above cannot see a value Turbopack reads through an
// import: `path.join(CACHE_DIR, `${stamp}.${asMp3 ? "mp3" : "wav"}`)` in
-// umtool's clip-audio route made every file under umtool/ with a dot in its
-// name part of that route's trace, the fixture and the e2e build's directory
-// included (plans/release-15.md, slice UT). So the last build's traces are read
-// back, when there is one.
+// umtool's clip-audio route took umtool's dot-directories into that route's
+// trace, the fixture and the e2e build's directory included (plans/release-15.md,
+// slice UT). So the last build's traces are read back, when there is one.
+//
+// What this can see: umtool/next.config.ts now excludes `.e2e-song`,
+// `.next-e2e` and `.env*` from every trace, so a pattern like that one shows
+// here only through a name the excludes miss -- `test-results/.last-run.json`
+// after an e2e run, `.next-shots`, the corpus, a path outside the repo. A
+// checkout with no e2e run behind it is blind to it; the fix at the call is
+// what keeps the route clean.
test("umtool's last build traced no dot-directory, no corpus file and nothing outside the repo", (t) => {
const dist = path.join(UMTOOL, ".next");
const id = path.join(dist, "BUILD_ID");