44 lines
1.9 KiB
TypeScript
44 lines
1.9 KiB
TypeScript
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();
|
|
}
|