commit f991e35b2a78ad01740bfb7b4cc8e5225ef9d070
parent 2e6144647711fae5b94c9d2343c795fcc24113f9
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 19:59:31 -0400
umtool: cwd-derived paths opt out of Turbopack's asset tracing
Slice Q's `path.join(REPO_ROOT, "transcripts", "channels")`, with
REPO_ROOT = findRepoRoot(process.cwd()), was evaluated by Turbopack as a
directory asset reference to the corpus: `next build` in the primary
checkout walked hundreds of GB until the kernel killed it, or died on
the first symlink out of the root. A worktree has no transcripts/ and
built in 30 s, which is how it shipped.
Every path op on a value derived from process.cwd() or import.meta.url
in a module the app imports now opens with the documented per-expression
opt-out, `/* turbopackIgnore: true */`: lib/paths.mjs (findRepoRoot,
CHANNELS_DIR), lib/paths.ts (SONG_CODE, stateFile), lib/tools.mjs,
lib/trim.ts, lib/report/driver.mjs, report-to-video/brand.mjs and
cues.mjs. The values at run time are unchanged. With the corpus linked,
the build under a 5 GB cap succeeds in 24 s at 0.80 GB max RSS, the
same 1,009 files and 28 MB as without it; nothing under transcripts/
is traced. os.homedir() joins are left as they are: the tracer does not
follow them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
7 files changed, 33 insertions(+), 23 deletions(-)
diff --git a/umtool/lib/paths.mjs b/umtool/lib/paths.mjs
@@ -98,13 +98,22 @@ const dedupe = (list) => [...new Set(list.map((p) => path.resolve(p)))];
* all run with cwd inside the checkout (the app at umtool/, which is what the
* fallback assumes). report-to-video/cues.mjs keeps its import.meta.url walk:
* only its CLI entry points (build-video, resolve-windows) read that default.
+ *
+ * EVERY PATH OP ON A cwd-DERIVED VALUE CARRIES `turbopackIgnore`. Turbopack
+ * evaluates `process.cwd()` statically as the project, so an un-annotated
+ * `path.join(REPO_ROOT, "transcripts", "channels")` became a DIRECTORY ASSET
+ * REFERENCE: `next build` walked the whole corpus (hundreds of GB, `data/`
+ * symlinked to another drive) and was OOM-killed, or died on the first symlink
+ * out of the root. A worktree with no transcripts/ builds fine, which is how it
+ * shipped. The comment is the documented per-expression opt-out; the values at
+ * run time are unchanged. scripts/umtool-build-trace.test.mjs holds the line.
*/
export function findRepoRoot(start) {
- let dir = path.resolve(start);
+ let dir = path.resolve(/* turbopackIgnore: true */ start);
for (;;) {
- if (existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
- const up = path.dirname(dir);
- if (up === dir) return path.resolve(start, "..");
+ if (existsSync(path.join(/* turbopackIgnore: true */ dir, "pnpm-workspace.yaml"))) return dir;
+ const up = path.dirname(/* turbopackIgnore: true */ dir);
+ if (up === dir) return path.resolve(/* turbopackIgnore: true */ start, "..");
dir = up;
}
}
@@ -112,10 +121,11 @@ export function findRepoRoot(start) {
export const REPO_ROOT = findRepoRoot(process.cwd());
export const CHANNELS_DIR = path.resolve(
+ /* turbopackIgnore: true */
process.env.CHANNELS_DIR ??
(process.env.TRANSCRIPTS_DIR
- ? path.join(process.env.TRANSCRIPTS_DIR, "channels")
- : path.join(REPO_ROOT, "transcripts", "channels")),
+ ? path.join(/* turbopackIgnore: true */ process.env.TRANSCRIPTS_DIR, "channels")
+ : path.join(/* turbopackIgnore: true */ REPO_ROOT, "transcripts", "channels")),
);
export const READ_ROOTS = dedupe(
diff --git a/umtool/lib/paths.ts b/umtool/lib/paths.ts
@@ -39,9 +39,9 @@ export {
// cwd at the package root.
export const SONG_CODE = process.env.SONG_CODE_DIR
? path.resolve(process.env.SONG_CODE_DIR)
- : path.join(process.cwd(), "song");
+ : path.join(/* turbopackIgnore: true */ process.cwd(), "song");
-export const stateFile = (name: string) => path.join(SONG_CODE, name);
+export const stateFile = (name: string) => path.join(/* turbopackIgnore: true */ SONG_CODE, name);
// dataFile needs SONG_DATA at module scope, which the re-export above does not
// bind locally -- so it is imported again rather than duplicated.
diff --git a/umtool/lib/report/driver.mjs b/umtool/lib/report/driver.mjs
@@ -11,9 +11,9 @@
import path from "node:path";
/** Where the pipeline lives. One place, so a move is one edit. */
-export const PIPELINE_DIR = path.resolve(process.cwd(), "report-to-video");
+export const PIPELINE_DIR = path.resolve(/* turbopackIgnore: true */ process.cwd(), "report-to-video");
-const script = (name) => path.join(PIPELINE_DIR, name);
+const script = (name) => path.join(/* turbopackIgnore: true */ PIPELINE_DIR, name);
/**
* Presets, in the order somebody actually works.
@@ -322,9 +322,9 @@ export function checkSourcesSteps(projects, env = {}) {
// ---------------------------------------------------------------------------
/** This package's own root. bin/ lives here, and so does report-to-video/. */
-export const UMTOOL_DIR = path.resolve(process.cwd());
+export const UMTOOL_DIR = path.resolve(/* turbopackIgnore: true */ process.cwd());
-const tool = (name) => path.join(UMTOOL_DIR, "bin", name);
+const tool = (name) => path.join(/* turbopackIgnore: true */ UMTOOL_DIR, "bin", name);
/** The interpreter a project's own scripts are run with. */
export const PYTHON = process.env.PYTHON_BIN ?? "python3";
diff --git a/umtool/lib/tools.mjs b/umtool/lib/tools.mjs
@@ -19,8 +19,8 @@ import { SONG_SCRATCH } from "./paths.mjs";
export const facedetPython = () =>
process.env.FACEDET_PYTHON ?? path.join(SONG_SCRATCH, "facedet", "bin", "python");
export const songCode = () =>
- process.env.SONG_CODE_DIR ? path.resolve(process.env.SONG_CODE_DIR) : path.join(process.cwd(), "song");
-export const facecropPy = () => path.join(songCode(), "facecrop.py");
+ process.env.SONG_CODE_DIR ? path.resolve(process.env.SONG_CODE_DIR) : path.join(/* turbopackIgnore: true */ process.cwd(), "song");
+export const facecropPy = () => path.join(/* turbopackIgnore: true */ songCode(), "facecrop.py");
/**
* The tools, with the env override each pipeline script honours. `required`
diff --git a/umtool/lib/trim.ts b/umtool/lib/trim.ts
@@ -222,7 +222,7 @@ export function hookRecipe(
const trimDir = `mkvocals/hooks-trim-${stamp}`;
const takeDir = `mkvocals/hooks-best-${stamp}`;
const outOverlays = dataFile(pair.to.replace(/\.json$/, `-${stamp}.json`));
- const cwd = path.join(process.cwd(), "song");
+ const cwd = path.join(/* turbopackIgnore: true */ process.cwd(), "song");
const base = { SONG_DIR: SONG_DATA };
return [
diff --git a/umtool/report-to-video/brand.mjs b/umtool/report-to-video/brand.mjs
@@ -37,17 +37,17 @@ import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
-const HERE = path.dirname(fileURLToPath(import.meta.url));
+const HERE = path.dirname(/* turbopackIgnore: true */ fileURLToPath(import.meta.url));
import { BRAND_CHOICES, BRAND_IDS } from "./brand-ids.mjs";
import { IBM_PLEX_SANS } from "./svg-faces.mjs";
export { BRAND_CHOICES, BRAND_IDS };
-export const FONTS_DIR = path.join(HERE, "fonts");
-export const FONTCONFIG_FILE = path.join(FONTS_DIR, "fonts.conf");
-export const MONO_FONT_FILE = path.join(FONTS_DIR, "IBMPlexMono-Regular.ttf");
+export const FONTS_DIR = path.join(/* turbopackIgnore: true */ HERE, "fonts");
+export const FONTCONFIG_FILE = path.join(/* turbopackIgnore: true */ FONTS_DIR, "fonts.conf");
+export const MONO_FONT_FILE = path.join(/* turbopackIgnore: true */ FONTS_DIR, "IBMPlexMono-Regular.ttf");
/** Plex Mono's static Bold: `render.fontBold`, which compose-chrome's HyperFrames band sets its bold in. */
-export const MONO_BOLD_FONT_FILE = path.join(FONTS_DIR, "IBMPlexMono-Bold.ttf");
+export const MONO_BOLD_FONT_FILE = path.join(/* turbopackIgnore: true */ FONTS_DIR, "IBMPlexMono-Bold.ttf");
/** The end card's default length: YouTube's end screen runs in the last 5–20 s. */
export const END_CARD_DEFAULT_SECONDS = 20;
@@ -79,7 +79,7 @@ export function brandKit(id) {
throw new Error(`render.brand "${id}" is not a preset — one of ${BRAND_IDS.join(", ")}`);
}
if (!kits.has(id)) {
- kits.set(id, JSON.parse(readFileSync(path.join(HERE, "brands", `${id}.json`), "utf8")));
+ kits.set(id, JSON.parse(readFileSync(path.join(/* turbopackIgnore: true */ HERE, "brands", `${id}.json`), "utf8")));
}
return kits.get(id);
}
diff --git a/umtool/report-to-video/cues.mjs b/umtool/report-to-video/cues.mjs
@@ -58,10 +58,10 @@ import { fileURLToPath } from "node:url";
// checkout would have is two levels up. Previously this defaulted to an absolute
// path inside the original author's home directory, which meant every other
// clone silently looked in a directory that does not exist.
-const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
+const REPO_ROOT = path.resolve(/* turbopackIgnore: true */ path.dirname(/* turbopackIgnore: true */ fileURLToPath(import.meta.url)), "..", "..");
export const DEFAULT_CHANNELS_DIR =
- process.env.CHANNELS_DIR ?? path.join(REPO_ROOT, "transcripts", "channels");
+ process.env.CHANNELS_DIR ?? path.join(/* turbopackIgnore: true */ REPO_ROOT, "transcripts", "channels");
const DEFAULT_CACHE_DIR =
process.env.REPORT_CACHE_DIR ??