Describe the bug
When fsCache: true (default) and the jiti instance id is a file path (the documented usage createJiti(import.meta.url)), the default cache dir almost never resolves to node_modules/.cache/jiti — it silently falls back to ${TMP_DIR}/jiti.
To reproduce
import { createJiti } from "jiti";
// id = a file nested below the package root, e.g. <pkg>/src/loader.ts
const jiti = createJiti("file:///home/user/my-pkg/src/loader.ts", { fsCache: true });
console.log(jiti.options.fsCache); // → /tmp/jiti (not <pkg>/node_modules/.cache/jiti)
Even with <pkg>/node_modules present and writable.
Cause
getCacheDir() in src/cache.ts computes:
const nmDir = ctx.filename && resolve(ctx.filename, "../node_modules");
pathe.resolve treats the id (a FILE path, e.g. <pkg>/src/loader.ts) as a directory path, so "../node_modules" resolves relative to the file's parent directory: <pkg>/src/loader.ts/../node_modules → <pkg>/src/node_modules — which never exists. Same for ids inside node_modules (node_modules/foo/index.js → node_modules/foo/node_modules). The documented node_modules/.cache/jiti only materializes when the id sits directly at the project root (<proj>/index.js → <proj>/node_modules).
Impact
- Transform cache lands in
${TMP_DIR}/jiti: volatile (wiped on reboot), shared machine-wide, unbounded growth (stale content-hash variants accumulate forever).
- Every consumer whose entry module isn't at the project root silently re-pays cold babel transpiles after every reboot (measured: ~1.2s cold vs ~4ms warm for a small config graph).
- On some systems
TMPDIR handling has extra workarounds (pnpm #6140) that a proper node_modules cache dir avoids entirely.
Suggested fix
Use the file's directory:
const nmDir = ctx.filename && resolve(dirname(ctx.filename), "../node_modules");
(dirname is already imported in src/cache.ts.) This makes node_modules/.cache/jiti work for file ids at any depth; the tmpdir fallback remains for the no-node_modules case. Note: our consumer (pi-steering, cad0p/pi-steering#39) worked around it by passing an explicit fsCache string — an upstream fix would make the default correct for everyone.
Environment
- jiti 2.7.0 (verified against current main,
src/cache.ts getCacheDir), Node 22/24, Linux.
Describe the bug
When
fsCache: true(default) and the jiti instance id is a file path (the documented usagecreateJiti(import.meta.url)), the default cache dir almost never resolves tonode_modules/.cache/jiti— it silently falls back to${TMP_DIR}/jiti.To reproduce
Even with
<pkg>/node_modulespresent and writable.Cause
getCacheDir()insrc/cache.tscomputes:pathe.resolvetreats the id (a FILE path, e.g.<pkg>/src/loader.ts) as a directory path, so"../node_modules"resolves relative to the file's parent directory:<pkg>/src/loader.ts/../node_modules→<pkg>/src/node_modules— which never exists. Same for ids insidenode_modules(node_modules/foo/index.js→node_modules/foo/node_modules). The documentednode_modules/.cache/jitionly materializes when the id sits directly at the project root (<proj>/index.js→<proj>/node_modules).Impact
${TMP_DIR}/jiti: volatile (wiped on reboot), shared machine-wide, unbounded growth (stale content-hash variants accumulate forever).TMPDIRhandling has extra workarounds (pnpm #6140) that a propernode_modulescache dir avoids entirely.Suggested fix
Use the file's directory:
(
dirnameis already imported insrc/cache.ts.) This makesnode_modules/.cache/jitiwork for file ids at any depth; the tmpdir fallback remains for the no-node_modules case. Note: our consumer (pi-steering, cad0p/pi-steering#39) worked around it by passing an explicitfsCachestring — an upstream fix would make the default correct for everyone.Environment
src/cache.tsgetCacheDir), Node 22/24, Linux.