UNPKG

monorepo-cache-manager

Version:

Lightweight cache manager for monorepos with cross-workspace cache sharing and optimization

219 lines 8.81 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.MonorepoCacheManager = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const utils_1 = require("./utils"); class MonorepoCacheManager { constructor(config = {}) { this.cacheIndex = new Map(); this.stats = { hits: 0, misses: 0, totalRequests: 0 }; this.config = { rootDir: process.cwd(), cacheDir: '.cache', maxSize: 100 * 1024 * 1024, ttl: 24 * 60 * 60 * 1000, compression: false, workspacePatterns: ['packages/*', 'apps/*', 'libs/*'], ...config }; } async initialize() { await utils_1.CacheUtils.ensureDir(this.config.cacheDir); await this.loadCacheIndex(); await this.cleanup(); } async get(workspace, options = {}) { const cacheKey = utils_1.CacheUtils.createCacheKey(workspace, options.dependencies); const entry = this.cacheIndex.get(cacheKey); if (!entry) { this.stats.misses++; this.stats.totalRequests++; return null; } if (Date.now() - entry.timestamp > this.config.ttl) { await this.remove(cacheKey); this.stats.misses++; this.stats.totalRequests++; return null; } const cachePath = path.join(this.config.cacheDir, cacheKey); if (!(await fs.pathExists(cachePath))) { await this.remove(cacheKey); this.stats.misses++; this.stats.totalRequests++; return null; } this.stats.hits++; this.stats.totalRequests++; return entry; } async set(workspace, buildOutput, options = {}) { const cacheKey = utils_1.CacheUtils.createCacheKey(workspace, options.dependencies); const workspacePath = path.join(this.config.rootDir, workspace); const outputHash = await utils_1.CacheUtils.generateHash(workspacePath); const size = await utils_1.CacheUtils.getDirectorySize(workspacePath); await this.enforceSizeLimit(size); const entry = { key: cacheKey, workspace, timestamp: Date.now(), size, hash: outputHash, dependencies: options.dependencies || [], metadata: options.metadata || {} }; const cachePath = path.join(this.config.cacheDir, cacheKey); await utils_1.CacheUtils.ensureDir(cachePath); for (const output of buildOutput) { const sourcePath = path.join(workspacePath, output); const targetPath = path.join(cachePath, output); if (await fs.pathExists(sourcePath)) { await fs.copy(sourcePath, targetPath); } } const metadataPath = path.join(cachePath, 'metadata.json'); await fs.writeJson(metadataPath, entry); this.cacheIndex.set(cacheKey, entry); await this.saveCacheIndex(); } async remove(cacheKey) { const cachePath = path.join(this.config.cacheDir, cacheKey); await fs.remove(cachePath); this.cacheIndex.delete(cacheKey); await this.saveCacheIndex(); } async clear() { await fs.remove(this.config.cacheDir); await utils_1.CacheUtils.ensureDir(this.config.cacheDir); this.cacheIndex.clear(); await this.saveCacheIndex(); } getStats() { const totalEntries = this.cacheIndex.size; const totalSize = Array.from(this.cacheIndex.values()).reduce((sum, entry) => sum + entry.size, 0); const hitRate = this.stats.totalRequests > 0 ? this.stats.hits / this.stats.totalRequests : 0; const missRate = 1 - hitRate; const timestamps = Array.from(this.cacheIndex.values()).map(entry => entry.timestamp); const oldestEntry = timestamps.length > 0 ? Math.min(...timestamps) : 0; const newestEntry = timestamps.length > 0 ? Math.max(...timestamps) : 0; return { totalEntries, totalSize, hitRate, missRate, oldestEntry, newestEntry }; } async findWorkspaces() { const workspacePaths = await utils_1.CacheUtils.findWorkspaces(this.config.rootDir, this.config.workspacePatterns); const workspaces = []; for (const workspacePath of workspacePaths) { const relativePath = path.relative(this.config.rootDir, workspacePath); const packageJsonPath = path.join(workspacePath, 'package.json'); if (await fs.pathExists(packageJsonPath)) { const packageJson = await fs.readJson(packageJsonPath); const dependencies = [ ...Object.keys(packageJson.dependencies || {}), ...Object.keys(packageJson.devDependencies || {}) ]; workspaces.push({ name: packageJson.name || relativePath, path: relativePath, dependencies, buildOutput: ['dist', 'build', 'lib', 'out'], cacheKey: utils_1.CacheUtils.createCacheKey(relativePath, dependencies) }); } } return workspaces; } async restore(workspace, options = {}) { const entry = await this.get(workspace, options); if (!entry) { return false; } const cacheKey = utils_1.CacheUtils.createCacheKey(workspace, options.dependencies); const cachePath = path.join(this.config.cacheDir, cacheKey); const workspacePath = path.join(this.config.rootDir, workspace); if (await fs.pathExists(cachePath)) { await fs.copy(cachePath, workspacePath, { overwrite: true }); return true; } return false; } async loadCacheIndex() { const indexPath = path.join(this.config.cacheDir, 'index.json'); if (await fs.pathExists(indexPath)) { try { const indexData = await fs.readJson(indexPath); this.cacheIndex = new Map(Object.entries(indexData)); } catch (error) { this.cacheIndex = new Map(); } } } async saveCacheIndex() { const indexPath = path.join(this.config.cacheDir, 'index.json'); const indexData = Object.fromEntries(this.cacheIndex); await fs.writeJson(indexPath, indexData, { spaces: 2 }); } async enforceSizeLimit(newEntrySize) { const stats = this.getStats(); if (stats.totalSize + newEntrySize > this.config.maxSize) { const entries = Array.from(this.cacheIndex.entries()) .sort(([, a], [, b]) => a.timestamp - b.timestamp); for (const [key, entry] of entries) { await this.remove(key); const newStats = this.getStats(); if (newStats.totalSize + newEntrySize <= this.config.maxSize) { break; } } } } async cleanup() { await utils_1.CacheUtils.cleanOldFiles(this.config.cacheDir, this.config.ttl); } } exports.MonorepoCacheManager = MonorepoCacheManager; //# sourceMappingURL=cache-manager.js.map