UNPKG

@orcdkestrator/orcdk-plugin-localstack

Version:
178 lines 7.22 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.HotReloadWatcher = exports.DEFAULT_WATCH_INTERVAL_MS = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const core_1 = require("@orcdkestrator/core"); const utils_1 = require("./utils"); const runtime_config_1 = require("./runtime-config"); // Default debounce interval for file change detection (milliseconds) exports.DEFAULT_WATCH_INTERVAL_MS = 700; /** * Hot reload file watcher for LocalStack Lambda functions * Monitors file changes and triggers LocalStack hot reload via events */ class HotReloadWatcher { constructor(config) { this.watchers = new Map(); this.lastModified = new Map(); this.isWatching = false; this.config = config; this.eventBus = core_1.EventBus.getInstance(); } /** * Start watching configured Lambda paths for changes */ async startWatching() { if (!this.config.hotReloading?.enabled || this.isWatching) { return; } const lambdaPaths = this.config.hotReloading.lambdaPaths || []; if (lambdaPaths.length === 0) { this.debug('No Lambda paths configured for hot reloading'); return; } this.debug(`Starting hot reload watching for ${lambdaPaths.length} Lambda functions`); // Get project root for path resolution const projectRoot = process.cwd(); for (const lambdaPath of lambdaPaths) { // Expand environment variables first const expandedPath = (0, utils_1.expandEnvironmentVariables)(lambdaPath.localPath); // Get file extensions for this runtime const fileExtensions = lambdaPath.fileExtensions || (0, runtime_config_1.getFileExtensionsForRuntime)(lambdaPath.runtime, this.config.hotReloading?.runtimeFileExtensions); // Resolve relative paths before watching const resolvedConfig = { ...lambdaPath, localPath: path.isAbsolute(expandedPath) ? expandedPath : path.resolve(projectRoot, expandedPath), functionName: (0, utils_1.expandEnvironmentVariables)(lambdaPath.functionName), handler: (0, utils_1.expandEnvironmentVariables)(lambdaPath.handler), fileExtensions }; await this.watchLambdaPath(resolvedConfig); } this.isWatching = true; this.debug('Hot reload watching started'); } /** * Stop watching all Lambda paths */ stopWatching() { if (!this.isWatching) { return; } this.debug('Stopping hot reload watching'); for (const [path, watcher] of this.watchers) { watcher.close(); this.debug(`Stopped watching ${path}`); } this.watchers.clear(); this.lastModified.clear(); this.isWatching = false; this.debug('Hot reload watching stopped'); } /** * Watch a specific Lambda path for changes */ async watchLambdaPath(lambdaConfig) { const { functionName, localPath, handler, runtime, fileExtensions } = lambdaConfig; if (!fs.existsSync(localPath)) { this.debug(`Lambda path does not exist: ${localPath}`); return; } const stat = fs.statSync(localPath); if (!stat.isDirectory()) { this.debug(`Lambda path is not a directory: ${localPath}`); return; } this.debug(`Setting up hot reload for ${functionName} at ${localPath}`); this.debug(`Watching file extensions: ${fileExtensions.join(', ')}`); // Initialize last modified time this.lastModified.set(localPath, Date.now()); // Create file watcher const watcher = fs.watch(localPath, { recursive: true }, (eventType, filename) => { if (!filename) return; const fullPath = path.join(localPath, filename); const ext = path.extname(filename).toLowerCase(); // Filter for relevant file types based on configured extensions if (!fileExtensions.includes(ext)) { return; } this.handleFileChange(functionName, localPath, fullPath, handler, runtime); }); this.watchers.set(localPath, watcher); this.debug(`Started watching ${localPath} for ${functionName}`); } /** * Handle file change event with debouncing */ handleFileChange(functionName, localPath, changedFile, handler, runtime) { const now = Date.now(); const lastMod = this.lastModified.get(localPath) || 0; const interval = this.config.hotReloading?.watchInterval || exports.DEFAULT_WATCH_INTERVAL_MS; // Debounce rapid file changes if (now - lastMod < interval) { return; } this.lastModified.set(localPath, now); this.debug(`File change detected: ${changedFile}`); // Emit event for hot reload this.eventBus.emitEvent('localstack:hot-reload:code-updated', { functionName, localPath, changedFile, handler, runtime, timestamp: new Date(), }, 'LocalStackHotReload'); // eslint-disable-next-line no-console console.log(`[localstack:hot-reload] Code updated for ${functionName}: ${path.basename(changedFile)}`); } /** * Debug logging helper */ debug(message) { if (this.config.debug) { // eslint-disable-next-line no-console console.log(`[localstack:hot-reload:debug] ${message}`); } } } exports.HotReloadWatcher = HotReloadWatcher; //# sourceMappingURL=hot-reload.js.map