UNPKG

@patchworkdev/pdk

Version:

Patchwork Development Kit

231 lines (230 loc) 8.89 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const crypto_1 = __importDefault(require("crypto")); const fs_1 = __importDefault(require("fs")); const glob_1 = require("glob"); const path_1 = __importDefault(require("path")); class LockFileManager { lockFilePath; rootDir; lockData; excludePatterns; constructor(configPath, excludePatterns = []) { this.excludePatterns = excludePatterns; this.rootDir = path_1.default.dirname(configPath); this.lockFilePath = path_1.default.join(this.rootDir, 'patchwork.lock'); this.lockData = this.readLockFile(); } readLockFile() { try { const data = fs_1.default.readFileSync(this.lockFilePath, 'utf8'); return JSON.parse(data); } catch { return { currentNetwork: 'local', lastDeployment: null, fileHashes: {}, directoryHashes: {}, deploymentHistory: [], projectHash: '', context: {}, }; } } saveLockFile() { fs_1.default.writeFileSync(this.lockFilePath, JSON.stringify(this.lockData, null, 2)); } shouldExclude(itemPath) { const relativePath = path_1.default.relative(this.rootDir, itemPath); return this.excludePatterns.some((pattern) => { if (pattern.startsWith('*')) { return relativePath.endsWith(pattern.slice(1)); } return relativePath.includes(pattern); }); } getRelativePath(absolutePath) { return path_1.default.relative(this.rootDir, absolutePath); } getAbsolutePath(relativePath) { return path_1.default.join(this.rootDir, relativePath); } updateNetwork(network) { this.lockData.currentNetwork = network; this.saveLockFile(); } logDeployment(contract, hash, address, network, timestamp, block) { const deploymentInfo = { contract, hash, address, network, timestamp, block, }; this.lockData.lastDeployment = deploymentInfo; this.lockData.deploymentHistory.push(deploymentInfo); this.saveLockFile(); } getLatestDeploymentForContract(contract, network) { const networkDeployments = this.lockData.deploymentHistory .filter((deployment) => deployment.network === network && deployment.contract === contract) .sort((a, b) => b.block - a.block); return networkDeployments[0] || null; } calculateFileHash(filepath) { const absolutePath = path_1.default.isAbsolute(filepath) ? filepath : this.getAbsolutePath(filepath); // Handle virtual files (like generator states) that don't exist on disk if (!fs_1.default.existsSync(absolutePath)) { return this.lockData.fileHashes[this.getRelativePath(filepath)] || ''; } const content = fs_1.default.readFileSync(absolutePath); const hash = crypto_1.default.createHash('sha256'); hash.update(content); return hash.digest('hex'); } updateFileHash(filepath, hash) { const relativePath = this.getRelativePath(filepath); const finalHash = hash ?? this.calculateFileHash(filepath); this.lockData.fileHashes[relativePath] = finalHash; this.saveLockFile(); } hasFileChanged(filepath) { const relativePath = this.getRelativePath(filepath); const currentHash = this.calculateFileHash(filepath); return this.lockData.fileHashes[relativePath] !== currentHash; } async getMatchingFiles(pattern) { const absolutePattern = path_1.default.isAbsolute(pattern) ? pattern : path_1.default.join(this.rootDir, pattern); try { const files = await (0, glob_1.glob)(absolutePattern, { nodir: true, ignore: this.excludePatterns, }); return files.filter((file) => !this.shouldExclude(file)); } catch (error) { console.error(`Error matching files for pattern ${pattern}:`, error); return []; } } calculateDirectoryHash(dirpath) { const absolutePath = path_1.default.isAbsolute(dirpath) ? dirpath : this.getAbsolutePath(dirpath); const hash = crypto_1.default.createHash('sha256'); hash.update(path_1.default.basename(absolutePath)); try { const items = fs_1.default.readdirSync(absolutePath); const sortedItems = items.sort(); for (const item of sortedItems) { const fullPath = path_1.default.join(absolutePath, item); if (this.shouldExclude(fullPath)) { continue; } const stats = fs_1.default.statSync(fullPath); const relativePath = this.getRelativePath(fullPath); if (stats.isFile()) { const fileHash = this.calculateFileHash(fullPath); hash.update(fileHash); this.lockData.fileHashes[relativePath] = fileHash; } else if (stats.isDirectory()) { const dirHash = this.calculateDirectoryHash(fullPath); hash.update(dirHash); this.lockData.directoryHashes[relativePath] = dirHash; } } return hash.digest('hex'); } catch (error) { console.error(`Error processing directory ${dirpath}:`, error); return ''; } } getChangedFiles() { return Object.keys(this.lockData.fileHashes).filter((filepath) => { const absolutePath = this.getAbsolutePath(filepath); return !this.shouldExclude(absolutePath) && this.hasFileChanged(absolutePath); }); } watchDirectory(callback) { const watcher = fs_1.default.watch(this.rootDir, { recursive: true }, async (eventType, filename) => { if (!filename) return; const absolutePath = this.getAbsolutePath(filename); if (this.shouldExclude(absolutePath)) { return; } // Give the filesystem a moment to settle await new Promise((resolve) => setTimeout(resolve, 100)); const changes = this.getAllChangedItems(); callback(changes); }); return watcher; } updateProjectHash() { const projectHash = this.calculateDirectoryHash(this.rootDir); this.lockData.projectHash = projectHash; this.saveLockFile(); } hasProjectChanged() { const currentHash = this.calculateDirectoryHash(this.rootDir); return this.lockData.projectHash !== currentHash; } getAllChangedItems() { const changedFiles = Object.keys(this.lockData.fileHashes).filter((filepath) => { // Skip virtual files (like generator states) if (filepath.startsWith('generator:')) return false; const absolutePath = this.getAbsolutePath(filepath); return !this.shouldExclude(absolutePath) && this.hasFileChanged(absolutePath); }); const changedDirs = Object.keys(this.lockData.directoryHashes).filter((dirpath) => { const absolutePath = this.getAbsolutePath(dirpath); return !this.shouldExclude(absolutePath) && this.lockData.directoryHashes[dirpath] !== this.calculateDirectoryHash(absolutePath); }); return { files: changedFiles, directories: changedDirs, }; } getDeploymentHistory() { return this.lockData.deploymentHistory; } getCurrentNetwork() { return this.lockData.currentNetwork; } getLastDeployment() { return this.lockData.lastDeployment; } getFileHash(filepath) { const relativePath = this.getRelativePath(filepath); return this.lockData.fileHashes[relativePath]; } getDirectoryHash(dirpath) { const relativePath = this.getRelativePath(dirpath); return this.lockData.directoryHashes[relativePath]; } getProjectHash() { return this.lockData.projectHash; } getRootDir() { return this.rootDir; } // Only update, do not write lockfile updateCtx(ctx) { this.lockData.context = ctx; } //ctx related functions updateAndSaveCtx(ctx) { this.updateCtx(ctx); this.saveLockFile(); } getCtx() { return this.lockData.context; } } exports.default = LockFileManager;