This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { CLEANUP_INTERVAL_MS, DEFAULT_IDLE_HOURS, PROJECTS_ROOT } from "./config";
|
||||
import { isBuildActive } from "./build";
|
||||
import type { ProjectMeta } from "./types";
|
||||
|
||||
let schedulerStarted = false;
|
||||
|
||||
export function isProjectIdle(lastAccessAt: string, idleHours: number, now = Date.now()) {
|
||||
const timestamp = Date.parse(lastAccessAt);
|
||||
return Number.isFinite(timestamp) && timestamp <= now - idleHours * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
export async function cleanupIdleNodeModules(idleHours = DEFAULT_IDLE_HOURS) {
|
||||
await fs.mkdir(PROJECTS_ROOT, { recursive: true });
|
||||
const cutoff = Date.now() - idleHours * 60 * 60 * 1000;
|
||||
const cleaned: string[] = [];
|
||||
for (const entry of await fs.readdir(PROJECTS_ROOT, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory() || isBuildActive(entry.name)) continue;
|
||||
const root = path.join(PROJECTS_ROOT, entry.name);
|
||||
try {
|
||||
const metaPath = path.join(root, "project.json");
|
||||
const meta: ProjectMeta = JSON.parse(await fs.readFile(metaPath, "utf8"));
|
||||
if (!isProjectIdle(meta.lastAccessAt, idleHours, Date.now())) continue;
|
||||
const modules = path.join(root, "workspace", "node_modules");
|
||||
const stat = await fs.lstat(modules).catch(() => null);
|
||||
if (!stat?.isDirectory() || stat.isSymbolicLink()) continue;
|
||||
await fs.rm(modules, { recursive: true, force: true });
|
||||
meta.nodeModulesCleanedAt = new Date().toISOString();
|
||||
await fs.writeFile(metaPath, JSON.stringify(meta, null, 2));
|
||||
cleaned.push(entry.name);
|
||||
} catch { /* one broken project must not stop maintenance */ }
|
||||
}
|
||||
return { cleaned, idleHours, checkedAt: new Date().toISOString() };
|
||||
}
|
||||
|
||||
export function startCleanupScheduler() {
|
||||
if (schedulerStarted || process.env.NODE_ENV === "test") return;
|
||||
schedulerStarted = true;
|
||||
const timer = setInterval(() => { void cleanupIdleNodeModules(); }, CLEANUP_INTERVAL_MS);
|
||||
timer.unref();
|
||||
void cleanupIdleNodeModules();
|
||||
}
|
||||
Reference in New Issue
Block a user