@laphilosophia/steady-watch 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -2
- package/bin/steady-watch +1 -1
- package/dist/index.d.mts +108 -8
- package/dist/index.d.ts +108 -8
- package/dist/index.js +600 -120
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +596 -120
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -9
- package/scripts/smoke-tests.mjs +277 -0
- package/steady-watch.config.json +4 -0
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/theme.ts","../src/watcher.ts","../src/cli.ts","../src/index.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { ThemeName } from './types.js';\n\nexport type ChalkStyle = (s: string) => string;\n\nexport interface Theme {\n blue: ChalkStyle;\n green: ChalkStyle;\n yellow: ChalkStyle;\n red: ChalkStyle;\n cyan: ChalkStyle;\n gray: ChalkStyle;\n dim: ChalkStyle;\n bold: ChalkStyle;\n}\n\nexport const themes: Record<ThemeName, Theme> = {\n default: {\n blue: chalk.blue,\n green: chalk.green,\n yellow: chalk.yellow,\n red: chalk.red,\n cyan: chalk.cyan,\n gray: chalk.gray,\n dim: chalk.dim,\n bold: chalk.bold\n },\n minimal: {\n blue: (s: string) => s,\n green: (s: string) => s,\n yellow: (s: string) => s,\n red: (s: string) => s,\n cyan: (s: string) => s,\n gray: (s: string) => s,\n dim: (s: string) => s,\n bold: (s: string) => s\n },\n none: {\n blue: () => '',\n green: () => '',\n yellow: () => '',\n red: () => '',\n cyan: () => '',\n gray: () => '',\n dim: () => '',\n bold: (s: string) => s\n }\n};\n\nexport function getTheme(name: ThemeName): Theme {\n return themes[name] || themes.default;\n}\n","import { ChildProcess, spawn } from 'child_process';\nimport chokidar, { FSWatcher } from 'chokidar';\nimport crypto from 'crypto';\nimport fs from 'fs';\nimport path from 'path';\nimport { EventEmitter } from 'events';\nimport { \n SteadyWatchOptions, \n NormalizedOptions, \n ValidationResult,\n HashAlgorithm,\n ThemeName,\n SteadyWatchEvents\n} from './types.js';\nimport { getTheme, Theme } from './theme.js';\n\nexport class SteadyWatcher extends EventEmitter {\n private options: NormalizedOptions;\n private watcher: FSWatcher | null = null;\n private fileHashes = new Map<string, string>();\n private timeout: NodeJS.Timeout | null = null;\n private isRunning = false;\n private activeProcess: ChildProcess | null = null;\n private killTimer: NodeJS.Timeout | null = null;\n private retryCount = 0;\n private disposed = false;\n private t: Theme;\n\n private static readonly DEFAULT_IGNORE = [/node_modules/, /\\.git/, /dist/, /build/];\n private static readonly VALID_HASH_ALGORITHMS: HashAlgorithm[] = ['md5', 'sha1', 'sha256'];\n private static readonly VALID_THEMES: ThemeName[] = ['default', 'minimal', 'none'];\n\n constructor(options: SteadyWatchOptions) {\n super();\n this.options = this.normalizeOptions(options);\n this.t = getTheme(this.options.theme);\n }\n\n private normalizeOptions(options: SteadyWatchOptions): NormalizedOptions {\n const hash = SteadyWatcher.VALID_HASH_ALGORITHMS.includes(options.hash as HashAlgorithm)\n ? options.hash as HashAlgorithm\n : 'md5';\n \n const theme = SteadyWatcher.VALID_THEMES.includes(options.theme as ThemeName)\n ? options.theme as ThemeName\n : 'default';\n\n const mergedIgnore = [\n ...SteadyWatcher.DEFAULT_IGNORE,\n ...this.normalizeIgnorePatterns(options.ignore || [])\n ];\n\n return {\n pattern: options.pattern,\n cmd: options.cmd,\n delay: Math.max(0, options.delay ?? 300),\n verbose: options.verbose ?? false,\n quiet: options.quiet ?? false,\n ignore: mergedIgnore,\n ext: options.ext || [],\n killTimeout: Math.max(0, options.killTimeout ?? 0),\n retry: Math.max(0, options.retry ?? 0),\n hash,\n mtimeOnly: options.mtimeOnly ?? false,\n clearScreen: options.clearScreen ?? false,\n json: options.json ?? false,\n theme\n };\n }\n\n private normalizeIgnorePatterns(patterns: (string | RegExp)[]): RegExp[] {\n return patterns.map(p => {\n if (p instanceof RegExp) return p;\n try {\n return new RegExp(p);\n } catch {\n return new RegExp(`^${p.replace(/\\*/g, '.*')}$`);\n }\n });\n }\n\n public validate(): ValidationResult {\n const errors: string[] = [];\n\n if (!this.options.pattern) {\n errors.push('Pattern is required');\n }\n\n if (!this.options.cmd) {\n errors.push('Command is required');\n }\n\n if (this.options.delay < 0) {\n errors.push('Delay must be a non-negative number');\n }\n\n if (this.options.killTimeout < 0) {\n errors.push('Kill timeout must be a non-negative number');\n }\n\n if (this.options.retry < 0) {\n errors.push('Retry must be a non-negative number');\n }\n\n return { valid: errors.length === 0, errors };\n }\n\n private getEffectivePattern(): string {\n if (this.options.ext.length === 0) {\n return this.options.pattern;\n }\n\n const extGlob = this.options.ext\n .map(e => e.startsWith('.') ? `*${e}` : `*.${e}`)\n .join(',');\n \n const pattern = this.options.pattern;\n return pattern.includes('{')\n ? pattern.replace(/\\}$/, `,${extGlob}}`)\n : `${pattern.replace(/\\/$/, '')}/{${extGlob}}`;\n }\n\n private getHash(filePath: string): string | null {\n try {\n if (!fs.existsSync(filePath)) return null;\n \n if (this.options.mtimeOnly) {\n const stats = fs.statSync(filePath);\n return `mtime:${stats.mtimeMs}`;\n }\n \n const content = fs.readFileSync(filePath);\n return crypto.createHash(this.options.hash).update(content).digest('hex');\n } catch {\n return null;\n }\n }\n\n private log(...args: unknown[]): void {\n if (!this.options.quiet && !this.disposed) console.log(...args);\n }\n\n private logVerbose(...args: unknown[]): void {\n if (this.options.verbose && !this.options.quiet && !this.disposed) {\n console.log(...args);\n }\n }\n\n private logJson(type: string, data: Record<string, unknown>): void {\n if (this.options.json && !this.disposed) {\n console.log(JSON.stringify({ timestamp: new Date().toISOString(), type, ...data }));\n }\n }\n\n private timestamp(): string {\n return this.t.gray(`[${new Date().toLocaleTimeString()}]`);\n }\n\n private parseCommand(cmdString: string): { cmd: string; args: string[] } {\n const tokens: string[] = [];\n let current = '';\n let inQuote = false;\n let quoteChar = '';\n let escaped = false;\n\n for (let i = 0; i < cmdString.length; i++) {\n const char = cmdString[i];\n const prevChar = i > 0 ? cmdString[i - 1] : '';\n\n if (escaped) {\n current += char;\n escaped = false;\n continue;\n }\n\n if (char === '\\\\' && !inQuote) {\n escaped = true;\n continue;\n }\n\n if ((char === '\"' || char === \"'\") && !inQuote) {\n inQuote = true;\n quoteChar = char;\n } else if (char === quoteChar && inQuote) {\n inQuote = false;\n quoteChar = '';\n } else if (char === ' ' && !inQuote) {\n if (current) {\n tokens.push(current);\n current = '';\n }\n } else {\n current += char;\n }\n }\n\n if (current) {\n tokens.push(current);\n }\n\n return { cmd: tokens[0] || '', args: tokens.slice(1) };\n }\n\n private runCommand(): void {\n if (this.disposed) return;\n\n if (this.isRunning) {\n this.logVerbose(this.t.yellow('⚠️ Previous command still running, skipping...'));\n this.emit('skip', 'previous command still running');\n return;\n }\n\n if (this.options.clearScreen) {\n console.clear();\n }\n\n this.isRunning = true;\n const retryInfo = this.retryCount > 0 ? ` (Retry ${this.retryCount}/${this.options.retry})` : '';\n this.log(`${this.timestamp()} ${this.t.cyan('🚀 Triggering:')} ${this.t.bold(this.options.cmd)}${retryInfo}`);\n this.logJson('trigger', { command: this.options.cmd, retry: this.retryCount });\n this.emit('trigger', this.options.cmd);\n\n const { cmd, args } = this.parseCommand(this.options.cmd);\n const startTime = Date.now();\n\n this.activeProcess = spawn(cmd, args, {\n stdio: 'inherit',\n shell: true,\n env: { ...process.env }\n });\n\n if (this.options.killTimeout > 0) {\n this.killTimer = setTimeout(() => {\n if (this.activeProcess && !this.disposed) {\n this.log(this.t.yellow(`⚠️ Process timeout (${this.options.killTimeout}ms), force killing...`));\n this.activeProcess.kill('SIGKILL');\n }\n }, this.options.killTimeout);\n }\n\n this.activeProcess.on('error', (err) => {\n this.log(this.t.red(`Process error: ${err.message}`));\n this.emit('error', err);\n });\n\n this.activeProcess.on('close', (code, signal) => {\n if (this.disposed) return;\n\n this.isRunning = false;\n if (this.killTimer) {\n clearTimeout(this.killTimer);\n this.killTimer = null;\n }\n this.activeProcess = null;\n const duration = ((Date.now() - startTime) / 1000).toFixed(2);\n\n if (code === 0) {\n this.log(`${this.timestamp()} ${this.t.green('✔ Done')} in ${duration}s`);\n this.logJson('done', { duration: parseFloat(duration) });\n this.emit('done', parseFloat(duration));\n this.retryCount = 0;\n } else {\n const exitMessage = signal ? ` (Signal: ${signal})` : ` (Exit code: ${code})`;\n this.log(`${this.timestamp()} ${this.t.red('✘ Failed')}${exitMessage}`);\n this.logJson('failed', { exitCode: code, signal });\n this.emit('fail', code, signal ?? undefined);\n \n if (this.options.retry > 0 && this.retryCount < this.options.retry) {\n this.retryCount++;\n this.log(this.t.yellow(`🔄 Retrying in 1s... (${this.retryCount}/${this.options.retry})`));\n setTimeout(() => this.runCommand(), 1000);\n return;\n }\n this.retryCount = 0;\n }\n this.log(this.t.dim('─'.repeat(40)));\n });\n }\n\n private handleFileChange(filePath: string): void {\n if (this.disposed) return;\n\n const currentHash = this.getHash(filePath);\n const lastHash = this.fileHashes.get(filePath);\n\n if (currentHash === lastHash) {\n this.logVerbose(this.t.gray(`Skipping ghost change: ${path.basename(filePath)}`));\n this.emit('skip', 'content unchanged', filePath);\n return;\n }\n\n if (currentHash) {\n this.fileHashes.set(filePath, currentHash);\n }\n\n this.emit('change', filePath);\n\n if (this.timeout) clearTimeout(this.timeout);\n this.timeout = setTimeout(() => {\n if (!this.disposed) {\n this.log(`${this.timestamp()} ${this.t.yellow('⚡ Change detected:')} ${path.basename(filePath)}`);\n this.logJson('change', { file: path.basename(filePath) });\n this.runCommand();\n }\n }, this.options.delay);\n }\n\n public async start(): Promise<void> {\n const validation = this.validate();\n if (!validation.valid) {\n const errorMsg = validation.errors.join(', ');\n this.log(this.t.red(`Validation error: ${errorMsg}`));\n this.emit('error', new Error(errorMsg));\n throw new Error(errorMsg);\n }\n\n const effectivePattern = this.getEffectivePattern();\n\n this.log(this.t.bold(`\\n🔭 Steady Watch Initialized`));\n this.log(` ${this.t.dim('Pattern:')} ${effectivePattern}`);\n this.log(` ${this.t.dim('Command:')} ${this.options.cmd}`);\n this.log(` ${this.t.dim('Delay:')} ${this.options.delay}ms`);\n if (this.options.quiet) this.log(` ${this.t.dim('Mode:')} quiet`);\n if (this.options.killTimeout > 0) this.log(` ${this.t.dim('Kill:')} ${this.options.killTimeout}ms`);\n if (this.options.retry > 0) this.log(` ${this.t.dim('Retry:')} ${this.options.retry}x`);\n if (this.options.mtimeOnly) this.log(` ${this.t.dim('Hash:')} mtime-only (fastest)`);\n else this.log(` ${this.t.dim('Hash:')} ${this.options.hash}`);\n this.log('');\n\n this.watcher = chokidar.watch(effectivePattern, {\n ignored: this.options.ignore,\n ignoreInitial: false,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 100\n }\n });\n\n this.watcher.on('ready', () => {\n this.log(this.t.green('👁️ Watcher ready. Monitoring for changes...'));\n this.logVerbose(this.t.dim(` Tracking ${this.fileHashes.size} file(s)`));\n this.emit('ready');\n });\n\n this.watcher.on('error', (error) => {\n this.log(this.t.red(`Watcher error: ${error.message}`));\n this.emit('error', error);\n });\n\n this.watcher.on('add', (filePath) => {\n if (this.disposed) return;\n const hash = this.getHash(filePath);\n if (hash) this.fileHashes.set(filePath, hash);\n this.logVerbose(this.t.dim(`Indexed: ${path.basename(filePath)}`));\n });\n\n this.watcher.on('change', (filePath) => this.handleFileChange(filePath));\n\n this.watcher.on('unlink', (filePath) => {\n if (this.disposed) return;\n this.fileHashes.delete(filePath);\n this.logVerbose(this.t.dim(`Removed: ${path.basename(filePath)}`));\n });\n }\n\n public async close(): Promise<void> {\n if (this.disposed) return;\n this.disposed = true;\n\n this.log(this.t.yellow('\\n🛑 Shutting down...'));\n this.logJson('shutdown', {});\n\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = null;\n }\n\n if (this.killTimer) {\n clearTimeout(this.killTimer);\n this.killTimer = null;\n }\n\n if (this.activeProcess) {\n this.activeProcess.kill('SIGTERM');\n this.activeProcess = null;\n }\n\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = null;\n }\n\n this.removeAllListeners();\n }\n\n public getTrackedFiles(): string[] {\n return Array.from(this.fileHashes.keys());\n }\n\n public isCurrentlyRunning(): boolean {\n return this.isRunning;\n }\n\n public isDisposed(): boolean {\n return this.disposed;\n }\n}\n\nexport function steadyWatch(options: SteadyWatchOptions): SteadyWatcher {\n return new SteadyWatcher(options);\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { program } from 'commander';\nimport { CliOptions, CliArgs, SteadyWatchOptions, HashAlgorithm, ThemeName } from './types.js';\n\nexport function loadConfig(configPath?: string): Record<string, unknown> {\n const searchPaths = [\n configPath,\n '.steady-watchrc',\n '.steady-watchrc.json',\n 'steady-watch.config.json'\n ].filter(Boolean) as string[];\n\n for (const cfgPath of searchPaths) {\n try {\n const fullPath = path.resolve(cfgPath);\n if (fs.existsSync(fullPath)) {\n return JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n }\n } catch {\n continue;\n }\n }\n\n return {};\n}\n\nexport function parseIgnorePatterns(patternString?: string): (string | RegExp)[] {\n if (!patternString) return [];\n return patternString.split(',').map(p => p.trim()).filter(Boolean);\n}\n\nexport function parseExtFilter(extString?: string): string[] {\n if (!extString) return [];\n return extString.split(',').map(e => e.trim()).filter(Boolean);\n}\n\nexport function parseBoolean(value: unknown): boolean {\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') return value.toLowerCase() === 'true';\n return false;\n}\n\nexport function parseNumber(value: unknown, defaultValue: number): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = parseInt(value, 10);\n return isNaN(parsed) ? defaultValue : parsed;\n }\n return defaultValue;\n}\n\nexport function mergeOptions(\n cliArgs: CliArgs,\n cliOpts: CliOptions,\n config: Record<string, unknown>\n): SteadyWatchOptions {\n return {\n pattern: cliArgs.pattern || (config.pattern as string) || '',\n cmd: cliOpts.cmd || (config.cmd as string) || '', \n delay: parseNumber(cliOpts.delay ?? config.delay, 300),\n verbose: parseBoolean(cliOpts.verbose ?? config.verbose),\n quiet: parseBoolean(cliOpts.quiet ?? config.quiet),\n ignore: [...parseIgnorePatterns(cliOpts.ignore), ...(config.ignore as string[] || [])],\n ext: cliOpts.ext ? parseExtFilter(cliOpts.ext) : (config.ext as string[] || []),\n killTimeout: parseNumber(cliOpts.killTimeout ?? config.killTimeout, 0),\n retry: parseNumber(cliOpts.retry ?? config.retry, 0),\n hash: (cliOpts.hash as HashAlgorithm) || (config.hash as HashAlgorithm) || 'md5',\n mtimeOnly: cliOpts.noHash ?? (config.mtimeOnly as boolean) ?? false,\n clearScreen: parseBoolean(cliOpts.clear ?? config.clearScreen),\n json: parseBoolean(cliOpts.json ?? config.json),\n theme: (cliOpts.theme as ThemeName) || (config.theme as ThemeName) || 'default'\n };\n}\n\nexport function parseCliArgs(): { args: CliArgs; opts: CliOptions } {\n program\n .name('steady-watch')\n .description('Intelligent file watcher with debouncing and content hashing.')\n .argument('<files>', 'Glob pattern to watch (e.g., \"src/**/*.ts\")')\n .requiredOption('-c, --cmd <command>', 'Command(s) to execute on change (supports quotes)')\n .option('-d, --delay <ms>', 'Debounce delay in milliseconds', '300')\n .option('-v, --verbose', 'Show hash calculations', false)\n .option('-q, --quiet', 'Minimize output', false)\n .option('--ignore <patterns>', 'Additional ignore patterns (comma-separated)')\n .option('--ext <extensions>', 'Filter by file extensions (e.g., .ts,.tsx)')\n .option('--config <path>', 'Path to config file')\n .option('--kill-timeout <ms>', 'Force kill process after timeout', '0')\n .option('--retry <count>', 'Retry failed command (0 = disabled)', '0')\n .option('--hash <algorithm>', 'Hash algorithm (md5, sha1, sha256)', 'md5')\n .option('--no-hash', 'Use mtime only instead of content hash (fastest)')\n .option('--clear', 'Clear screen on each trigger')\n .option('--json', 'Output in JSON format')\n .option('--theme <theme>', 'Color theme (default, minimal, none)', 'default')\n .version('2.0.0')\n .parse();\n\n const cliOpts = program.opts() as CliOptions;\n const cliArgs: CliArgs = { pattern: program.args[0] || '' };\n\n return { args: cliArgs, opts: cliOpts };\n}\n","export * from './types.js';\nexport * from './theme.js';\nexport { SteadyWatcher, steadyWatch } from './watcher.js';\n\nimport { SteadyWatcher, steadyWatch } from './watcher.js';\nimport { parseCliArgs, loadConfig, mergeOptions } from './cli.js';\n\nexport function runCli(): void {\n const { args, opts } = parseCliArgs();\n const config = loadConfig(opts.config);\n \n if (opts.config && config.cmd) {\n opts.cmd = config.cmd as string;\n }\n\n const options = mergeOptions(args, opts, config);\n \n if (!options.cmd) {\n console.error('Error: Command is required. Use -c option or config file.');\n process.exit(1);\n }\n\n const watcher = new SteadyWatcher(options);\n\n watcher.on('error', (err) => {\n console.error(`Error: ${err.message}`);\n process.exit(1);\n });\n\n watcher.start().catch((err) => {\n console.error(`Failed to start: ${err.message}`);\n process.exit(1);\n });\n\n const shutdown = async (signal: string) => {\n console.log(`\\nReceived ${signal}, shutting down...`);\n await watcher.close();\n process.exit(0);\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n process.on('uncaughtException', (err) => {\n console.error('Uncaught exception:', err);\n process.exit(1);\n });\n}\n\nconst isMain = require.main === module || process.argv[1]?.includes('index.js') || process.argv[1]?.includes('steady-watch');\n\nif (isMain) {\n runCli();\n}\n"],"mappings":"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","names":["fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/theme.ts","../src/watcher.ts","../src/cli.ts","../src/index.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { ThemeName } from './types.js';\n\nexport type ChalkStyle = (s: string) => string;\n\nexport interface Theme {\n blue: ChalkStyle;\n green: ChalkStyle;\n yellow: ChalkStyle;\n red: ChalkStyle;\n cyan: ChalkStyle;\n gray: ChalkStyle;\n dim: ChalkStyle;\n bold: ChalkStyle;\n}\n\nexport const themes: Record<ThemeName, Theme> = {\n default: {\n blue: chalk.blue,\n green: chalk.green,\n yellow: chalk.yellow,\n red: chalk.red,\n cyan: chalk.cyan,\n gray: chalk.gray,\n dim: chalk.dim,\n bold: chalk.bold\n },\n minimal: {\n blue: (s: string) => s,\n green: (s: string) => s,\n yellow: (s: string) => s,\n red: (s: string) => s,\n cyan: (s: string) => s,\n gray: (s: string) => s,\n dim: (s: string) => s,\n bold: (s: string) => s\n },\n none: {\n blue: () => '',\n green: () => '',\n yellow: () => '',\n red: () => '',\n cyan: () => '',\n gray: () => '',\n dim: () => '',\n bold: (s: string) => s\n }\n};\n\nexport function getTheme(name: ThemeName): Theme {\n return themes[name] || themes.default;\n}\n","import { ChildProcess, execFile, spawn } from 'child_process';\nimport chokidar, { FSWatcher } from 'chokidar';\nimport crypto from 'crypto';\nimport fs from 'fs';\nimport path from 'path';\nimport { EventEmitter } from 'events';\nimport {\n SteadyWatchOptions,\n NormalizedOptions,\n ValidationResult,\n HashAlgorithm,\n ThemeName,\n SteadyWatchHookContext,\n SteadyWatchHooks\n} from './types.js';\nimport { getTheme, Theme } from './theme.js';\n\ninterface CommandResult {\n code: number | null;\n signal: NodeJS.Signals | null;\n duration: number;\n}\n\ntype ManagedChildProcess = ChildProcess & { steadyWatchUsesShell?: boolean };\n\nexport class SteadyWatcher extends EventEmitter {\n private options: NormalizedOptions;\n private watcher: FSWatcher | null = null;\n private watcherReady = false;\n private fileHashes = new Map<string, string>();\n private gitChangedFiles = new Set<string>();\n private timeout: NodeJS.Timeout | null = null;\n private cycleRunning = false;\n private pendingCycle = false;\n private lifecycleProcess: ManagedChildProcess | null = null;\n private lifecycleKillTimer: NodeJS.Timeout | null = null;\n private startedProcess: ManagedChildProcess | null = null;\n private stoppingStartedProcess: ManagedChildProcess | null = null;\n private retryCount = 0;\n private disposed = false;\n private closePromise: Promise<void> | null = null;\n private t: Theme;\n\n private static readonly DEFAULT_IGNORE = [/node_modules/, /\\.git/, /dist/, /build/];\n private static readonly VALID_HASH_ALGORITHMS: HashAlgorithm[] = ['md5', 'sha1', 'sha256'];\n private static readonly VALID_THEMES: ThemeName[] = ['default', 'minimal', 'none'];\n\n constructor(options: SteadyWatchOptions) {\n super();\n this.options = this.normalizeOptions(options);\n this.t = getTheme(this.options.theme);\n }\n\n private normalizeOptions(options: SteadyWatchOptions): NormalizedOptions {\n const hash = SteadyWatcher.VALID_HASH_ALGORITHMS.includes(options.hash as HashAlgorithm)\n ? options.hash as HashAlgorithm\n : 'md5';\n\n const theme = SteadyWatcher.VALID_THEMES.includes(options.theme as ThemeName)\n ? options.theme as ThemeName\n : 'default';\n\n const mergedIgnore = [\n ...SteadyWatcher.DEFAULT_IGNORE,\n ...this.normalizeIgnorePatterns(options.ignore || [])\n ];\n\n const start = options.start || '';\n const hooks: SteadyWatchHooks = {\n ...(options.hooks || {}),\n beforeCommand: options.beforeCommand || options.hooks?.beforeCommand,\n afterCommand: options.afterCommand || options.hooks?.afterCommand,\n beforeStop: options.beforeStop || options.hooks?.beforeStop,\n afterStop: options.afterStop || options.hooks?.afterStop,\n beforeStart: options.beforeStart || options.hooks?.beforeStart,\n afterStart: options.afterStart || options.hooks?.afterStart,\n onSkip: options.onSkip || options.hooks?.onSkip\n };\n\n return {\n pattern: options.pattern,\n cmd: options.cmd || '',\n start,\n restartOnSuccess: options.restartOnSuccess ?? false,\n restartOnChange: options.restartOnChange ?? false,\n initialRun: options.initialRun ?? Boolean(start),\n hooks,\n delay: Math.max(0, options.delay ?? 300),\n verbose: options.verbose ?? false,\n quiet: options.quiet ?? false,\n ignore: mergedIgnore,\n ext: options.ext || [],\n gitChanged: options.gitChanged ?? false,\n gitBase: options.gitBase || 'HEAD',\n killTimeout: Math.max(0, options.killTimeout ?? (start ? 5000 : 0)),\n restartDelay: Math.max(0, options.restartDelay ?? 0),\n retry: Math.max(0, options.retry ?? 0),\n hash,\n mtimeOnly: options.mtimeOnly ?? false,\n clearScreen: options.clearScreen ?? false,\n json: options.json ?? false,\n theme\n };\n }\n\n private normalizeIgnorePatterns(patterns: (string | RegExp)[]): RegExp[] {\n return patterns.map(p => {\n if (p instanceof RegExp) return p;\n try {\n return new RegExp(p);\n } catch {\n return new RegExp(`^${p.replace(/\\*/g, '.*')}$`);\n }\n });\n }\n\n public validate(): ValidationResult {\n const errors: string[] = [];\n\n if (!this.options.pattern) {\n errors.push('Pattern is required');\n }\n\n if (!this.options.cmd && !this.options.start) {\n errors.push('Command is required. Use --cmd, or use --start with --restart-on-change');\n }\n\n if (this.options.start && !this.options.restartOnSuccess && !this.options.restartOnChange) {\n errors.push('A start command requires --restart-on-success or --restart-on-change');\n }\n\n if (this.options.restartOnSuccess && (!this.options.cmd || !this.options.start)) {\n errors.push('--restart-on-success requires both --cmd and --start');\n }\n\n if (this.options.restartOnChange && !this.options.start) {\n errors.push('--restart-on-change requires --start');\n }\n\n if (this.options.restartOnChange && this.options.cmd) {\n errors.push('--restart-on-change cannot be combined with --cmd; use --restart-on-success');\n }\n\n if (!this.options.cmd && this.options.start && !this.options.restartOnChange) {\n errors.push('--restart-on-change is required when --start is used without --cmd');\n }\n\n if (this.options.delay < 0) {\n errors.push('Delay must be a non-negative number');\n }\n\n if (this.options.killTimeout < 0) {\n errors.push('Kill timeout must be a non-negative number');\n }\n\n if (this.options.restartDelay < 0) {\n errors.push('Restart delay must be a non-negative number');\n }\n\n if (this.options.gitChanged && !this.options.gitBase) {\n errors.push('Git base must be set when git-changed mode is enabled');\n }\n\n if (this.options.retry < 0) {\n errors.push('Retry must be a non-negative number');\n }\n\n return { valid: errors.length === 0, errors };\n }\n\n private getEffectivePattern(): string {\n if (this.options.ext.length === 0) {\n return this.options.pattern;\n }\n\n const extGlob = this.options.ext\n .map(e => e.startsWith('.') ? `*${e}` : `*.${e}`)\n .join(',');\n\n const pattern = this.options.pattern;\n return pattern.includes('{')\n ? pattern.replace(/\\}$/, `,${extGlob}}`)\n : `${pattern.replace(/\\/$/, '')}/{${extGlob}}`;\n }\n\n private getHash(filePath: string): string | null {\n try {\n if (!fs.existsSync(filePath)) return null;\n\n if (this.options.mtimeOnly) {\n const stats = fs.statSync(filePath);\n return `mtime:${stats.mtimeMs}`;\n }\n\n const content = fs.readFileSync(filePath);\n return crypto.createHash(this.options.hash).update(content).digest('hex');\n } catch {\n return null;\n }\n }\n\n private normalizeGitPath(filePath: string): string {\n return path.relative(process.cwd(), path.resolve(filePath)).replace(/\\\\/g, '/');\n }\n\n private runGit(args: string[]): Promise<string[]> {\n return new Promise((resolve, reject) => {\n execFile('git', args, { cwd: process.cwd(), maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {\n if (error) {\n const message = stderr.trim() || error.message;\n reject(new Error(`git ${args.join(' ')} failed: ${message}`));\n return;\n }\n\n resolve(stdout.split(/\\r?\\n/).map(line => line.trim()).filter(Boolean));\n });\n });\n }\n\n private async refreshGitChangedFiles(): Promise<void> {\n if (!this.options.gitChanged) return;\n\n const [unstaged, staged, untracked] = await Promise.all([\n this.runGit(['diff', '--name-only', '--relative', this.options.gitBase, '--']),\n this.runGit(['diff', '--name-only', '--cached', '--relative', this.options.gitBase, '--']),\n this.runGit(['ls-files', '--others', '--exclude-standard'])\n ]);\n\n this.gitChangedFiles = new Set([...unstaged, ...staged, ...untracked].map(file => file.replace(/\\\\/g, '/')));\n }\n\n private async isGitChangedFile(filePath: string): Promise<boolean> {\n if (!this.options.gitChanged) return true;\n\n await this.refreshGitChangedFiles();\n const relativePath = this.normalizeGitPath(filePath);\n return this.gitChangedFiles.has(relativePath);\n }\n\n private log(...args: unknown[]): void {\n if (!this.options.quiet && !this.disposed) console.log(...args);\n }\n\n private logVerbose(...args: unknown[]): void {\n if (this.options.verbose && !this.options.quiet && !this.disposed) {\n console.log(...args);\n }\n }\n\n private logJson(type: string, data: Record<string, unknown>): void {\n if (this.options.json && !this.disposed) {\n console.log(JSON.stringify({ timestamp: new Date().toISOString(), type, ...data }));\n }\n }\n\n private timestamp(): string {\n return this.t.gray(`[${new Date().toLocaleTimeString()}]`);\n }\n\n private async runHook(\n hook: keyof SteadyWatchHooks,\n context: Omit<SteadyWatchHookContext, 'phase' | 'timestamp'>,\n mode: 'gate' | 'notify'\n ): Promise<boolean> {\n const callback = this.options.hooks[hook];\n if (!callback) return true;\n\n const hookContext: SteadyWatchHookContext = {\n phase: hook,\n timestamp: new Date(),\n ...context\n };\n\n try {\n await callback(Object.freeze(hookContext));\n return true;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(this.t.red(`Hook ${hook} failed: ${err.message}`));\n this.logJson('hook_error', { hook, error: err.message });\n this.emit('hookError', hook, err);\n if (mode === 'notify') return true;\n this.emit('fail', null);\n return false;\n }\n }\n\n private notifyHook(\n hook: keyof SteadyWatchHooks,\n context: Omit<SteadyWatchHookContext, 'phase' | 'timestamp'>\n ): void {\n void this.runHook(hook, context, 'notify');\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private parseCommand(cmdString: string): { cmd: string; args: string[] } {\n const tokens: string[] = [];\n let current = '';\n let inQuote = false;\n let quoteChar = '';\n let escaped = false;\n\n for (let i = 0; i < cmdString.length; i++) {\n const char = cmdString[i];\n\n if (escaped) {\n current += char;\n escaped = false;\n continue;\n }\n\n if (char === '\\\\' && !inQuote) {\n escaped = true;\n continue;\n }\n\n if ((char === '\"' || char === \"'\") && !inQuote) {\n inQuote = true;\n quoteChar = char;\n } else if (char === quoteChar && inQuote) {\n inQuote = false;\n quoteChar = '';\n } else if (char === ' ' && !inQuote) {\n if (current) {\n tokens.push(current);\n current = '';\n }\n } else {\n current += char;\n }\n }\n\n if (current) {\n tokens.push(current);\n }\n\n return { cmd: tokens[0] || '', args: tokens.slice(1) };\n }\n\n private commandNeedsShell(command: string): boolean {\n return /(?:&&|\\|\\||[|<>])/.test(command);\n }\n\n private resolveWindowsCommand(command: string): string | null {\n if (process.platform !== 'win32') return null;\n if (path.isAbsolute(command) || command.includes('\\\\') || command.includes('/')) {\n return fs.existsSync(command) ? command : null;\n }\n\n const pathEntries = (process.env.PATH || '').split(path.delimiter).filter(Boolean);\n const pathExts = (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD')\n .split(';')\n .filter(Boolean);\n const hasExt = Boolean(path.extname(command));\n const candidates = hasExt ? [command] : pathExts.map(ext => `${command}${ext.toLowerCase()}`);\n\n for (const entry of pathEntries) {\n for (const candidate of candidates) {\n const fullPath = path.join(entry, candidate);\n if (fs.existsSync(fullPath)) return fullPath;\n }\n }\n\n return null;\n }\n\n private shouldUseShell(command: string, preferShell: boolean): boolean {\n if (preferShell || this.commandNeedsShell(command)) return true;\n\n const parsed = this.parseCommand(command);\n if (!parsed.cmd) return true;\n\n if (process.platform !== 'win32') return false;\n\n const resolved = this.resolveWindowsCommand(parsed.cmd);\n if (!resolved) return true;\n\n return /\\.(cmd|bat)$/i.test(resolved);\n }\n\n private spawnCommand(command: string, preferShell: boolean): ManagedChildProcess {\n const useShell = this.shouldUseShell(command, preferShell);\n const parsed = this.parseCommand(command);\n const child = useShell\n ? spawn(command, {\n stdio: 'inherit',\n shell: true,\n env: { ...process.env },\n detached: process.platform !== 'win32',\n windowsHide: false\n })\n : spawn(parsed.cmd, parsed.args, {\n stdio: 'inherit',\n env: { ...process.env },\n detached: process.platform !== 'win32',\n windowsHide: false\n });\n\n return Object.assign(child, { steadyWatchUsesShell: useShell });\n }\n\n private runTaskkill(pid: number, force: boolean): Promise<boolean> {\n return new Promise(resolve => {\n const args = ['/pid', String(pid), '/T'];\n if (force) args.push('/F');\n\n const killer = spawn('taskkill', args, {\n stdio: 'ignore',\n windowsHide: true\n });\n\n let settled = false;\n const finish = (ok: boolean) => {\n if (settled) return;\n settled = true;\n resolve(ok);\n };\n\n killer.on('close', code => finish(code === 0));\n killer.on('error', () => finish(false));\n });\n }\n\n private async forceKillProcess(child: ChildProcess, label: string): Promise<void> {\n if (process.platform === 'win32' && child.pid) {\n const killedTree = await this.runTaskkill(child.pid, true);\n if (killedTree) return;\n }\n\n try {\n if (process.platform !== 'win32' && child.pid) {\n process.kill(-child.pid, 'SIGKILL');\n } else {\n child.kill('SIGKILL');\n }\n } catch {\n this.logVerbose(this.t.gray(`Process already exited before force kill: ${label}`));\n }\n }\n\n private terminateProcess(child: ManagedChildProcess, command: string, label: string): Promise<void> {\n return new Promise(resolve => {\n let settled = false;\n const timeoutMs = this.options.killTimeout > 0 ? this.options.killTimeout : 5000;\n let forceTimer: NodeJS.Timeout | null = null;\n\n const finish = () => {\n if (settled) return;\n settled = true;\n if (forceTimer) clearTimeout(forceTimer);\n resolve();\n };\n\n if (child.exitCode !== null || child.signalCode !== null) {\n finish();\n return;\n }\n\n child.once('close', finish);\n\n this.emit('stop', command, 'SIGTERM');\n\n const requestGracefulStop = async () => {\n try {\n if (process.platform === 'win32' && child.pid) {\n const killedTree = await this.runTaskkill(child.pid, false);\n if (killedTree) {\n finish();\n return;\n }\n }\n\n if (process.platform !== 'win32' && child.pid) {\n process.kill(-child.pid, 'SIGTERM');\n } else {\n child.kill('SIGTERM');\n }\n } catch {\n finish();\n return;\n }\n };\n\n void requestGracefulStop();\n\n forceTimer = setTimeout(() => {\n if (!settled) {\n this.log(this.t.yellow(`Process did not exit after ${timeoutMs}ms, force killing ${label}...`));\n void (async () => {\n await this.forceKillProcess(child, label);\n finish();\n })();\n }\n }, timeoutMs);\n });\n }\n\n private runSingleCommand(attempt: number): Promise<CommandResult> {\n if (this.disposed) {\n return Promise.resolve({ code: null, signal: null, duration: 0 });\n }\n\n this.retryCount = attempt;\n const retryInfo = attempt > 0 ? ` (Retry ${attempt}/${this.options.retry})` : '';\n this.log(`${this.timestamp()} ${this.t.cyan('Triggering:')} ${this.t.bold(this.options.cmd)}${retryInfo}`);\n this.logJson('trigger', { command: this.options.cmd, retry: attempt });\n this.emit('trigger', this.options.cmd);\n\n const startTime = Date.now();\n const child = this.spawnCommand(this.options.cmd, false);\n this.lifecycleProcess = child;\n\n const commandTimeout = this.options.killTimeout;\n if (commandTimeout > 0) {\n this.lifecycleKillTimer = setTimeout(() => {\n if (this.lifecycleProcess === child && !this.disposed) {\n this.log(this.t.yellow(`Process timeout (${commandTimeout}ms), force killing...`));\n void this.forceKillProcess(child, this.options.cmd);\n }\n }, commandTimeout);\n }\n\n return new Promise(resolve => {\n let settled = false;\n\n const finish = (code: number | null, signal: NodeJS.Signals | null) => {\n if (settled) return;\n settled = true;\n\n if (this.lifecycleKillTimer) {\n clearTimeout(this.lifecycleKillTimer);\n this.lifecycleKillTimer = null;\n }\n\n if (this.lifecycleProcess === child) {\n this.lifecycleProcess = null;\n }\n\n const duration = (Date.now() - startTime) / 1000;\n\n if (!this.disposed) {\n if (code === 0) {\n this.log(`${this.timestamp()} ${this.t.green('Done')} in ${duration.toFixed(2)}s`);\n this.logJson('done', { duration: parseFloat(duration.toFixed(2)) });\n this.emit('done', parseFloat(duration.toFixed(2)));\n } else {\n const exitMessage = signal ? ` (Signal: ${signal})` : ` (Exit code: ${code})`;\n this.log(`${this.timestamp()} ${this.t.red('Failed')}${exitMessage}`);\n this.logJson('failed', { exitCode: code, signal });\n this.emit('fail', code, signal ?? undefined);\n }\n }\n\n resolve({ code, signal, duration });\n };\n\n child.on('error', (err) => {\n if (!this.disposed) {\n this.log(this.t.red(`Process error: ${err.message}`));\n }\n finish(null, null);\n });\n\n child.on('close', finish);\n });\n }\n\n private async runCommandWithRetry(): Promise<CommandResult> {\n let attempt = 0;\n\n while (!this.disposed) {\n const result = await this.runSingleCommand(attempt);\n\n if (result.code === 0) {\n this.retryCount = 0;\n return result;\n }\n\n if (this.options.retry > 0 && attempt < this.options.retry) {\n attempt++;\n this.log(this.t.yellow(`Retrying in 1s... (${attempt}/${this.options.retry})`));\n await this.sleep(1000);\n continue;\n }\n\n this.retryCount = 0;\n return result;\n }\n\n return { code: null, signal: null, duration: 0 };\n }\n\n private async stopStartedProcess(): Promise<void> {\n if (!this.startedProcess) return;\n\n const child = this.startedProcess;\n const pid = child.pid;\n this.stoppingStartedProcess = child;\n await this.terminateProcess(child, this.options.start, this.options.start);\n\n if (this.startedProcess === child) {\n this.startedProcess = null;\n }\n if (this.stoppingStartedProcess === child) {\n this.stoppingStartedProcess = null;\n }\n\n this.notifyHook('afterStop', { command: this.options.start, startCommand: this.options.start, pid });\n }\n\n private startLongRunningProcess(): void {\n if (this.disposed || !this.options.start) return;\n\n this.log(`${this.timestamp()} ${this.t.cyan('Starting:')} ${this.t.bold(this.options.start)}`);\n this.logJson('start', { command: this.options.start });\n\n const child = this.spawnCommand(this.options.start, false);\n this.startedProcess = child;\n this.emit('start', this.options.start, child.pid);\n setImmediate(() => {\n if (this.startedProcess === child && !this.disposed && child.exitCode === null && child.signalCode === null) {\n this.notifyHook('afterStart', { command: this.options.start, startCommand: this.options.start, pid: child.pid });\n }\n });\n\n child.on('error', (err) => {\n if (!this.disposed) {\n this.log(this.t.red(`Start process error: ${err.message}`));\n this.logJson('start_error', { command: this.options.start, error: err.message });\n this.emit('fail', null);\n }\n if (this.startedProcess === child) {\n this.startedProcess = null;\n }\n });\n\n child.on('close', (code, signal) => {\n const expectedStop = this.disposed || this.stoppingStartedProcess === child;\n\n if (this.startedProcess === child) {\n this.startedProcess = null;\n }\n if (this.stoppingStartedProcess === child) {\n this.stoppingStartedProcess = null;\n }\n\n if (!expectedStop && !this.disposed) {\n const exitMessage = signal ? ` (Signal: ${signal})` : ` (Exit code: ${code})`;\n this.log(`${this.timestamp()} ${this.t.yellow('Start process exited')}${exitMessage}`);\n this.logJson('start_exit', { exitCode: code, signal, expected: false });\n }\n\n if (!this.disposed) {\n this.emit('startExit', code, signal ?? undefined, expectedStop);\n }\n });\n }\n\n private async restartStartedProcess(): Promise<void> {\n if (!this.options.start) return;\n\n this.emit('restart', this.options.start);\n const canStart = await this.runHook('beforeStart', {\n command: this.options.start,\n startCommand: this.options.start\n }, 'gate');\n if (!canStart) return;\n\n if (this.startedProcess) {\n const canStop = await this.runHook('beforeStop', {\n command: this.options.start,\n startCommand: this.options.start,\n pid: this.startedProcess.pid\n }, 'gate');\n if (!canStop) return;\n await this.stopStartedProcess();\n }\n\n if (this.options.restartDelay > 0 && !this.disposed) {\n await this.sleep(this.options.restartDelay);\n }\n\n this.startLongRunningProcess();\n }\n\n private async runCycle(reason: string): Promise<void> {\n if (this.disposed) return;\n\n if (this.cycleRunning) {\n this.pendingCycle = true;\n this.logVerbose(this.t.yellow(`Lifecycle already running, coalescing ${reason} change...`));\n this.skip('lifecycle already running');\n return;\n }\n\n this.cycleRunning = true;\n\n try {\n if (this.options.clearScreen) {\n console.clear();\n }\n\n if (this.options.cmd) {\n const canRunCommand = await this.runHook('beforeCommand', {\n reason,\n command: this.options.cmd,\n startCommand: this.options.start || undefined\n }, 'gate');\n if (!canRunCommand) return;\n\n const result = await this.runCommandWithRetry();\n const canContinue = await this.runHook('afterCommand', {\n reason,\n command: this.options.cmd,\n startCommand: this.options.start || undefined,\n exitCode: result.code,\n signal: result.signal,\n duration: result.duration\n }, 'gate');\n if (!canContinue) return;\n\n if (result.code === 0 && this.options.start && this.options.restartOnSuccess) {\n await this.restartStartedProcess();\n }\n } else if (this.options.start && this.options.restartOnChange) {\n await this.restartStartedProcess();\n }\n } finally {\n this.cycleRunning = false;\n if (!this.disposed) {\n this.log(this.t.dim('-'.repeat(40)));\n }\n\n if (this.pendingCycle && !this.disposed) {\n this.pendingCycle = false;\n setImmediate(() => {\n void this.runCycle('coalesced');\n });\n }\n }\n }\n\n private requestCycle(reason: string): void {\n void this.runCycle(reason);\n }\n\n private skip(reason: string, filePath?: string): void {\n this.emit('skip', reason, filePath);\n this.notifyHook('onSkip', { skipReason: reason, file: filePath });\n }\n\n private scheduleFileCycle(filePath: string, reason: string): void {\n this.emit('change', filePath);\n\n if (this.timeout) clearTimeout(this.timeout);\n this.timeout = setTimeout(() => {\n if (!this.disposed) {\n this.log(`${this.timestamp()} ${this.t.yellow('Change detected:')} ${path.basename(filePath)}`);\n this.logJson('change', { file: path.basename(filePath), reason });\n this.requestCycle(reason);\n }\n }, this.options.delay);\n }\n\n private async handleFileChange(filePath: string): Promise<void> {\n if (this.disposed) return;\n\n const currentHash = this.getHash(filePath);\n const lastHash = this.fileHashes.get(filePath);\n\n if (currentHash === lastHash) {\n this.logVerbose(this.t.gray(`Skipping ghost change: ${path.basename(filePath)}`));\n this.skip('content unchanged', filePath);\n return;\n }\n\n if (currentHash) {\n this.fileHashes.set(filePath, currentHash);\n }\n\n let isGitChanged = true;\n try {\n isGitChanged = await this.isGitChangedFile(filePath);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(this.t.red(`Git changed-file check failed: ${err.message}`));\n this.emit('error', err);\n return;\n }\n\n if (!isGitChanged) {\n this.logVerbose(this.t.gray(`Skipping unchanged git file: ${path.basename(filePath)}`));\n this.skip('not changed from git base', filePath);\n return;\n }\n\n this.scheduleFileCycle(filePath, 'file');\n }\n\n public async start(): Promise<void> {\n const validation = this.validate();\n if (!validation.valid) {\n const errorMsg = validation.errors.join(', ');\n this.log(this.t.red(`Validation error: ${errorMsg}`));\n this.emit('error', new Error(errorMsg));\n throw new Error(errorMsg);\n }\n\n const effectivePattern = this.getEffectivePattern();\n\n this.log(this.t.bold('\\nSteady Watch Initialized'));\n this.log(` ${this.t.dim('Pattern:')} ${effectivePattern}`);\n if (this.options.cmd) this.log(` ${this.t.dim('Command:')} ${this.options.cmd}`);\n if (this.options.start) this.log(` ${this.t.dim('Start:')} ${this.options.start}`);\n this.log(` ${this.t.dim('Delay:')} ${this.options.delay}ms`);\n if (this.options.quiet) this.log(` ${this.t.dim('Mode:')} quiet`);\n if (this.options.killTimeout > 0) this.log(` ${this.t.dim('Kill:')} ${this.options.killTimeout}ms`);\n if (this.options.restartDelay > 0) this.log(` ${this.t.dim('Restart delay:')} ${this.options.restartDelay}ms`);\n if (this.options.retry > 0) this.log(` ${this.t.dim('Retry:')} ${this.options.retry}x`);\n if (this.options.gitChanged) this.log(` ${this.t.dim('Git:')} changed from ${this.options.gitBase}`);\n if (this.options.initialRun) this.log(` ${this.t.dim('Initial:')} enabled`);\n if (this.options.mtimeOnly) this.log(` ${this.t.dim('Hash:')} mtime-only (fastest)`);\n else this.log(` ${this.t.dim('Hash:')} ${this.options.hash}`);\n this.log('');\n\n if (this.options.gitChanged) {\n await this.refreshGitChangedFiles();\n }\n\n this.watcher = chokidar.watch(effectivePattern, {\n ignored: this.options.ignore,\n ignoreInitial: false,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 100\n }\n });\n\n this.watcher.on('ready', () => {\n this.watcherReady = true;\n this.log(this.t.green('Watcher ready. Monitoring for changes...'));\n this.logVerbose(this.t.dim(` Tracking ${this.fileHashes.size} file(s)`));\n this.emit('ready');\n if (this.options.initialRun) {\n this.requestCycle('initial');\n }\n });\n\n this.watcher.on('error', (error) => {\n this.log(this.t.red(`Watcher error: ${error.message}`));\n this.emit('error', error);\n });\n\n this.watcher.on('add', (filePath) => {\n void this.handleFileAdd(filePath);\n });\n\n this.watcher.on('change', (filePath) => {\n void this.handleFileChange(filePath);\n });\n\n this.watcher.on('unlink', (filePath) => {\n void this.handleFileUnlink(filePath);\n });\n }\n\n private async handleFileAdd(filePath: string): Promise<void> {\n if (this.disposed) return;\n const hash = this.getHash(filePath);\n if (hash) this.fileHashes.set(filePath, hash);\n this.logVerbose(this.t.dim(`Indexed: ${path.basename(filePath)}`));\n\n if (!this.watcherReady || !this.options.gitChanged) return;\n\n try {\n if (await this.isGitChangedFile(filePath)) {\n this.scheduleFileCycle(filePath, 'file added');\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(this.t.red(`Git changed-file check failed: ${err.message}`));\n this.emit('error', err);\n }\n }\n\n private async handleFileUnlink(filePath: string): Promise<void> {\n if (this.disposed) return;\n this.fileHashes.delete(filePath);\n this.logVerbose(this.t.dim(`Removed: ${path.basename(filePath)}`));\n\n if (!this.watcherReady || !this.options.gitChanged) return;\n\n try {\n if (await this.isGitChangedFile(filePath)) {\n this.scheduleFileCycle(filePath, 'file removed');\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(this.t.red(`Git changed-file check failed: ${err.message}`));\n this.emit('error', err);\n }\n }\n\n public async close(): Promise<void> {\n if (this.closePromise) {\n return this.closePromise;\n }\n\n this.closePromise = this.closeInternal();\n return this.closePromise;\n }\n\n private async closeInternal(): Promise<void> {\n if (this.disposed) return;\n this.log(this.t.yellow('\\nShutting down...'));\n this.logJson('shutdown', {});\n\n this.disposed = true;\n\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = null;\n }\n\n if (this.lifecycleKillTimer) {\n clearTimeout(this.lifecycleKillTimer);\n this.lifecycleKillTimer = null;\n }\n\n const lifecycleProcess = this.lifecycleProcess;\n const startedProcess = this.startedProcess;\n\n await Promise.all([\n lifecycleProcess ? this.terminateProcess(lifecycleProcess, this.options.cmd, this.options.cmd) : Promise.resolve(),\n startedProcess ? this.terminateProcess(startedProcess, this.options.start, this.options.start) : Promise.resolve()\n ]);\n\n this.lifecycleProcess = null;\n this.startedProcess = null;\n this.stoppingStartedProcess = null;\n\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = null;\n }\n this.watcherReady = false;\n\n this.removeAllListeners();\n }\n\n public getTrackedFiles(): string[] {\n return Array.from(this.fileHashes.keys());\n }\n\n public isCurrentlyRunning(): boolean {\n return this.cycleRunning || Boolean(this.lifecycleProcess);\n }\n\n public isStartedProcessRunning(): boolean {\n return Boolean(this.startedProcess);\n }\n\n public isDisposed(): boolean {\n return this.disposed;\n }\n}\n\nexport function steadyWatch(options: SteadyWatchOptions): SteadyWatcher {\n return new SteadyWatcher(options);\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { Command } from 'commander';\nimport { CliOptions, CliArgs, SteadyWatchOptions, HashAlgorithm, ThemeName } from './types.js';\n\nexport const CLI_VERSION = '2.1.0';\n\nexport function loadConfig(configPath?: string): Record<string, unknown> {\n const searchPaths = [\n configPath,\n '.steady-watchrc',\n '.steady-watchrc.json',\n 'steady-watch.config.json'\n ];\n\n for (const cfgPath of searchPaths) {\n if (!cfgPath) continue;\n try {\n const fullPath = path.resolve(cfgPath);\n if (fs.existsSync(fullPath)) {\n return JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n }\n } catch {\n continue;\n }\n }\n\n return {};\n}\n\nexport function parseIgnorePatterns(patternString?: string): (string | RegExp)[] {\n if (!patternString) return [];\n return patternString.split(',').map(p => p.trim()).filter(Boolean);\n}\n\nexport function parseExtFilter(extString?: string): string[] {\n if (!extString) return [];\n return extString.split(',').map(e => e.trim()).filter(Boolean);\n}\n\nexport function parseBoolean(value: unknown): boolean {\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') return value.toLowerCase() === 'true';\n return false;\n}\n\nexport function parseNumber(value: unknown, defaultValue: number): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = parseInt(value, 10);\n return isNaN(parsed) ? defaultValue : parsed;\n }\n return defaultValue;\n}\n\nfunction unsetDefaultedCliOptions(program: Command, opts: CliOptions): void {\n const getSource = program.getOptionValueSource.bind(program);\n const optionKeys: Array<keyof CliOptions> = [\n 'delay',\n 'verbose',\n 'quiet',\n 'gitBase',\n 'retry',\n 'hash',\n 'theme'\n ];\n\n for (const key of optionKeys) {\n if (getSource(key) === 'default') {\n delete opts[key];\n }\n }\n}\n\nexport function mergeOptions(\n cliArgs: CliArgs,\n cliOpts: CliOptions,\n config: Record<string, unknown>\n): SteadyWatchOptions {\n return {\n pattern: cliArgs.pattern || (config.pattern as string) || '',\n cmd: cliOpts.cmd || (config.cmd as string) || '',\n start: cliOpts.start || (config.start as string) || '',\n restartOnSuccess: parseBoolean(cliOpts.restartOnSuccess ?? config.restartOnSuccess),\n restartOnChange: parseBoolean(cliOpts.restartOnChange ?? config.restartOnChange),\n initialRun: cliOpts.initialRun ?? (config.initialRun as boolean | undefined),\n delay: parseNumber(cliOpts.delay ?? config.delay, 300),\n verbose: parseBoolean(cliOpts.verbose ?? config.verbose),\n quiet: parseBoolean(cliOpts.quiet ?? config.quiet),\n ignore: [...parseIgnorePatterns(cliOpts.ignore), ...(config.ignore as string[] || [])],\n ext: cliOpts.ext ? parseExtFilter(cliOpts.ext) : (config.ext as string[] || []),\n gitChanged: parseBoolean(cliOpts.gitChanged ?? config.gitChanged),\n gitBase: cliOpts.gitBase || (config.gitBase as string) || 'HEAD',\n killTimeout: cliOpts.killTimeout !== undefined || config.killTimeout !== undefined\n ? parseNumber(cliOpts.killTimeout ?? config.killTimeout, 0)\n : undefined,\n restartDelay: parseNumber(cliOpts.restartDelay ?? config.restartDelay, 0),\n retry: parseNumber(cliOpts.retry ?? config.retry, 0),\n hash: (typeof cliOpts.hash === 'string' ? cliOpts.hash as HashAlgorithm : undefined)\n || (config.hash as HashAlgorithm)\n || 'md5',\n mtimeOnly: (cliOpts.hash === false) || (config.mtimeOnly as boolean) || false,\n clearScreen: parseBoolean(cliOpts.clear ?? config.clearScreen),\n json: parseBoolean(cliOpts.json ?? config.json),\n theme: (cliOpts.theme as ThemeName) || (config.theme as ThemeName) || 'default'\n };\n}\n\nexport function parseCliArgs(argv = process.argv): { args: CliArgs; opts: CliOptions } {\n const program = new Command();\n\n program\n .name('steady-watch')\n .description('Intelligent file watcher with debouncing and content hashing.')\n .argument('[files]', 'Glob pattern to watch (e.g., \"src/**/*.ts\")')\n .option('-c, --cmd <command>', 'Command(s) to execute on change (supports quotes)')\n .option('--start <command>', 'Long-running command to start or restart after a successful lifecycle command')\n .option('--restart-on-success', 'Restart --start only when --cmd exits with code 0')\n .option('--restart-on-change', 'Restart --start directly on change when no --cmd is configured')\n .option('--initial-run', 'Run the initial lifecycle when the watcher starts')\n .option('--no-initial-run', 'Wait for the first file change before running the lifecycle')\n .option('-d, --delay <ms>', 'Debounce delay in milliseconds', '300')\n .option('-v, --verbose', 'Show hash calculations', false)\n .option('-q, --quiet', 'Minimize output', false)\n .option('--ignore <patterns>', 'Additional ignore patterns (comma-separated)')\n .option('--ext <extensions>', 'Filter by file extensions (e.g., .ts,.tsx)')\n .option('--git-changed', 'Only trigger for files changed from the git base ref')\n .option('--git-base <ref>', 'Git base ref for --git-changed', 'HEAD')\n .option('--config <path>', 'Path to config file')\n .option('--kill-timeout <ms>', 'Force kill lifecycle commands and process shutdown after timeout')\n .option('--restart-delay <ms>', 'Delay between stopping and starting --start')\n .option('--retry <count>', 'Retry failed command (0 = disabled)', '0')\n .option('--hash <algorithm>', 'Hash algorithm (md5, sha1, sha256)', 'md5')\n .option('--no-hash', 'Use mtime only instead of content hash (fastest)')\n .option('--clear', 'Clear screen on each trigger')\n .option('--json', 'Output in JSON format')\n .option('--theme <theme>', 'Color theme (default, minimal, none)', 'default')\n .version(CLI_VERSION)\n .parse(argv);\n\n const cliOpts = program.opts() as CliOptions;\n unsetDefaultedCliOptions(program, cliOpts);\n const cliArgs: CliArgs = { pattern: program.args[0] || '' };\n\n return { args: cliArgs, opts: cliOpts };\n}\n","export * from './types.js';\nexport * from './theme.js';\nexport { SteadyWatcher, steadyWatch } from './watcher.js';\nexport { CLI_VERSION, loadConfig, mergeOptions, parseCliArgs } from './cli.js';\n\nimport { SteadyWatcher, steadyWatch } from './watcher.js';\nimport { parseCliArgs, loadConfig, mergeOptions } from './cli.js';\n\nexport function runCli(): void {\n const { args, opts } = parseCliArgs();\n const config = loadConfig(opts.config);\n\n const options = mergeOptions(args, opts, config);\n\n const watcher = new SteadyWatcher(options);\n let exiting = false;\n\n const shutdown = async (exitCode: number, message?: string) => {\n if (exiting) return;\n exiting = true;\n if (message) console.error(message);\n try {\n await watcher.close();\n } finally {\n process.exit(exitCode);\n }\n };\n\n watcher.on('error', (err) => {\n void shutdown(1, `Error: ${err.message}`);\n });\n\n watcher.start().catch((err) => {\n void shutdown(1, `Failed to start: ${err.message}`);\n });\n\n const shutdownSignal = async (signal: string) => {\n if (exiting) return;\n console.log(`\\nReceived ${signal}, shutting down...`);\n await shutdown(0);\n };\n\n process.on('SIGINT', () => void shutdownSignal('SIGINT'));\n process.on('SIGTERM', () => void shutdownSignal('SIGTERM'));\n process.on('uncaughtException', (err) => {\n void shutdown(1, `Uncaught exception: ${err instanceof Error ? err.stack || err.message : String(err)}`);\n });\n}\n\nif (require.main === module) {\n runCli();\n}\n"],"mappings":"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","names":["fs","path"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laphilosophia/steady-watch",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Intelligent file watcher with content hashing and debouncing. No more ghost rebuilds.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -12,18 +12,21 @@
|
|
|
12
12
|
"require": "./dist/index.js"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsup",
|
|
17
|
-
"
|
|
18
|
-
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"test": "npm run build && node scripts/smoke-tests.mjs",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
19
20
|
"bin": {
|
|
20
21
|
"steady-watch": "./bin/steady-watch",
|
|
21
22
|
"sw": "./bin/steady-watch"
|
|
22
23
|
},
|
|
23
|
-
"files": [
|
|
24
|
-
"dist",
|
|
25
|
-
"bin"
|
|
26
|
-
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"bin",
|
|
27
|
+
"scripts",
|
|
28
|
+
"steady-watch.config.json"
|
|
29
|
+
],
|
|
27
30
|
"keywords": [
|
|
28
31
|
"watch",
|
|
29
32
|
"watcher",
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const repoRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
9
|
+
const { SteadyWatcher, parseCliArgs, mergeOptions } = await import(pathToFileURL(path.join(repoRoot, 'dist', 'index.js')).href);
|
|
10
|
+
|
|
11
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
12
|
+
|
|
13
|
+
async function waitFor(predicate, timeoutMs, label) {
|
|
14
|
+
const deadline = Date.now() + timeoutMs;
|
|
15
|
+
while (Date.now() < deadline) {
|
|
16
|
+
if (predicate()) return;
|
|
17
|
+
await sleep(25);
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`Timed out waiting for ${label}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function tempDir(name) {
|
|
23
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), `steady-watch-${name}-`));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function nodeCommand(source) {
|
|
27
|
+
return `"${process.execPath}" -e "${source.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isPidAlive(pid) {
|
|
31
|
+
try {
|
|
32
|
+
process.kill(pid, 0);
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function testBuildFailurePreservesRunningProcess() {
|
|
40
|
+
const dir = tempDir('preserve');
|
|
41
|
+
const watchedFile = path.join(dir, 'source.txt');
|
|
42
|
+
const serverFile = path.join(dir, 'server.pid');
|
|
43
|
+
const buildFlag = path.join(dir, 'build-ok');
|
|
44
|
+
fs.writeFileSync(watchedFile, 'initial');
|
|
45
|
+
fs.writeFileSync(buildFlag, 'ok');
|
|
46
|
+
|
|
47
|
+
const watcher = new SteadyWatcher({
|
|
48
|
+
pattern: path.join(dir, '*.txt').replace(/\\/g, '/'),
|
|
49
|
+
cmd: nodeCommand(`if (!require('fs').existsSync('${buildFlag.replace(/\\/g, '\\\\')}')) process.exit(2)`),
|
|
50
|
+
start: nodeCommand(`const fs=require('fs'); fs.writeFileSync('${serverFile.replace(/\\/g, '\\\\')}', String(process.pid)); setInterval(function(){},1000)`),
|
|
51
|
+
restartOnSuccess: true,
|
|
52
|
+
delay: 20,
|
|
53
|
+
killTimeout: 1000,
|
|
54
|
+
quiet: true,
|
|
55
|
+
theme: 'none'
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await watcher.start();
|
|
60
|
+
await waitFor(() => fs.existsSync(serverFile), 4000, 'initial start');
|
|
61
|
+
const firstPid = fs.readFileSync(serverFile, 'utf8');
|
|
62
|
+
|
|
63
|
+
fs.rmSync(buildFlag);
|
|
64
|
+
fs.writeFileSync(watchedFile, 'fail');
|
|
65
|
+
await sleep(700);
|
|
66
|
+
assert.equal(fs.readFileSync(serverFile, 'utf8'), firstPid);
|
|
67
|
+
assert.equal(watcher.isStartedProcessRunning(), true);
|
|
68
|
+
|
|
69
|
+
fs.writeFileSync(buildFlag, 'ok');
|
|
70
|
+
fs.writeFileSync(watchedFile, 'success');
|
|
71
|
+
await waitFor(() => fs.readFileSync(serverFile, 'utf8') !== firstPid, 5000, 'restart after successful build');
|
|
72
|
+
} finally {
|
|
73
|
+
await watcher.close();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function testCoalescesChangesDuringLifecycle() {
|
|
78
|
+
const dir = tempDir('coalesce');
|
|
79
|
+
const watchedFile = path.join(dir, 'source.txt');
|
|
80
|
+
const counterFile = path.join(dir, 'counter.state');
|
|
81
|
+
fs.writeFileSync(watchedFile, 'initial');
|
|
82
|
+
fs.writeFileSync(counterFile, '0');
|
|
83
|
+
|
|
84
|
+
const command = nodeCommand([
|
|
85
|
+
`const fs=require('fs')`,
|
|
86
|
+
`const p='${counterFile.replace(/\\/g, '\\\\')}'`,
|
|
87
|
+
`fs.writeFileSync(p, String(Number(fs.readFileSync(p,'utf8'))+1))`,
|
|
88
|
+
`setTimeout(function(){process.exit(0)},250)`
|
|
89
|
+
].join(';'));
|
|
90
|
+
|
|
91
|
+
const watcher = new SteadyWatcher({
|
|
92
|
+
pattern: path.join(dir, '*.txt').replace(/\\/g, '/'),
|
|
93
|
+
cmd: command,
|
|
94
|
+
delay: 20,
|
|
95
|
+
quiet: true,
|
|
96
|
+
theme: 'none'
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await watcher.start();
|
|
101
|
+
await new Promise(resolve => watcher.once('ready', resolve));
|
|
102
|
+
fs.writeFileSync(watchedFile, 'a');
|
|
103
|
+
await waitFor(() => fs.readFileSync(counterFile, 'utf8') === '1', 5000, 'first running cycle');
|
|
104
|
+
await sleep(50);
|
|
105
|
+
fs.writeFileSync(watchedFile, 'b');
|
|
106
|
+
await sleep(50);
|
|
107
|
+
fs.writeFileSync(watchedFile, 'c');
|
|
108
|
+
await waitFor(() => fs.readFileSync(counterFile, 'utf8') === '2', 5000, 'coalesced second cycle');
|
|
109
|
+
await sleep(300);
|
|
110
|
+
assert.equal(fs.readFileSync(counterFile, 'utf8'), '2');
|
|
111
|
+
} finally {
|
|
112
|
+
await watcher.close();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function testGitChangedUntrackedAddTriggers() {
|
|
117
|
+
const dir = tempDir('git-add');
|
|
118
|
+
spawnSync('git', ['init'], { cwd: dir, stdio: 'ignore' });
|
|
119
|
+
fs.writeFileSync(path.join(dir, 'tracked.txt'), 'tracked');
|
|
120
|
+
spawnSync('git', ['add', 'tracked.txt'], { cwd: dir, stdio: 'ignore' });
|
|
121
|
+
spawnSync('git', ['-c', 'user.email=test@example.com', '-c', 'user.name=Test', 'commit', '-m', 'init'], {
|
|
122
|
+
cwd: dir,
|
|
123
|
+
stdio: 'ignore'
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const counterFile = path.join(dir, 'counter.state');
|
|
127
|
+
fs.writeFileSync(counterFile, '0');
|
|
128
|
+
|
|
129
|
+
const watcher = new SteadyWatcher({
|
|
130
|
+
pattern: path.join(dir, '*.txt').replace(/\\/g, '/'),
|
|
131
|
+
cmd: nodeCommand(`const fs=require('fs'); const p='${counterFile.replace(/\\/g, '\\\\')}'; fs.writeFileSync(p, String(Number(fs.readFileSync(p,'utf8'))+1))`),
|
|
132
|
+
gitChanged: true,
|
|
133
|
+
delay: 20,
|
|
134
|
+
quiet: true,
|
|
135
|
+
theme: 'none'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const originalCwd = process.cwd();
|
|
139
|
+
try {
|
|
140
|
+
process.chdir(dir);
|
|
141
|
+
await watcher.start();
|
|
142
|
+
await new Promise(resolve => watcher.once('ready', resolve));
|
|
143
|
+
fs.writeFileSync(path.join(dir, 'new-file.txt'), 'untracked');
|
|
144
|
+
await waitFor(() => fs.readFileSync(counterFile, 'utf8') === '1', 5000, 'git-changed untracked add trigger');
|
|
145
|
+
} finally {
|
|
146
|
+
await watcher.close();
|
|
147
|
+
process.chdir(originalCwd);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function testCliDefaultMergeAndNoHash() {
|
|
152
|
+
const { args, opts } = parseCliArgs(['node', 'steady-watch', 'src/**/*', '--no-hash']);
|
|
153
|
+
const merged = mergeOptions(args, opts, {
|
|
154
|
+
delay: 900,
|
|
155
|
+
retry: 2,
|
|
156
|
+
hash: 'sha256',
|
|
157
|
+
gitBase: 'main',
|
|
158
|
+
theme: 'minimal'
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
assert.equal(merged.delay, 900);
|
|
162
|
+
assert.equal(merged.retry, 2);
|
|
163
|
+
assert.equal(merged.gitBase, 'main');
|
|
164
|
+
assert.equal(merged.theme, 'minimal');
|
|
165
|
+
assert.equal(merged.mtimeOnly, true);
|
|
166
|
+
assert.equal(merged.hash, 'sha256');
|
|
167
|
+
|
|
168
|
+
parseCliArgs(['node', 'steady-watch', 'src/**/*', '--hash', 'sha1']);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function testUnexpectedStartExitEvent() {
|
|
172
|
+
const dir = tempDir('start-exit');
|
|
173
|
+
const watchedFile = path.join(dir, 'source.txt');
|
|
174
|
+
fs.writeFileSync(watchedFile, 'initial');
|
|
175
|
+
|
|
176
|
+
const watcher = new SteadyWatcher({
|
|
177
|
+
pattern: path.join(dir, '*.txt').replace(/\\/g, '/'),
|
|
178
|
+
start: nodeCommand('process.exit(7)'),
|
|
179
|
+
restartOnChange: true,
|
|
180
|
+
initialRun: true,
|
|
181
|
+
delay: 20,
|
|
182
|
+
quiet: true,
|
|
183
|
+
theme: 'none'
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const exits = [];
|
|
188
|
+
watcher.on('startExit', (code, signal, expected) => exits.push({ code, signal, expected }));
|
|
189
|
+
await watcher.start();
|
|
190
|
+
await waitFor(() => exits.length === 1, 4000, 'unexpected start exit event');
|
|
191
|
+
assert.deepEqual(exits[0], { code: 7, signal: undefined, expected: false });
|
|
192
|
+
} finally {
|
|
193
|
+
await watcher.close();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function testStartedProcessTreeCleanup() {
|
|
198
|
+
const dir = tempDir('tree');
|
|
199
|
+
const watchedFile = path.join(dir, 'source.txt');
|
|
200
|
+
const pidFile = path.join(dir, 'child.pid');
|
|
201
|
+
const parentFile = path.join(dir, 'parent.js');
|
|
202
|
+
const childFile = path.join(dir, 'child.js');
|
|
203
|
+
fs.writeFileSync(watchedFile, 'initial');
|
|
204
|
+
fs.writeFileSync(childFile, 'setInterval(function(){}, 1000);\n');
|
|
205
|
+
fs.writeFileSync(parentFile, [
|
|
206
|
+
"const { spawn } = require('child_process')",
|
|
207
|
+
"const fs = require('fs')",
|
|
208
|
+
`const child = spawn(process.execPath, [${JSON.stringify(childFile)}], { stdio: 'ignore' })`,
|
|
209
|
+
`fs.writeFileSync(${JSON.stringify(pidFile)}, String(child.pid))`,
|
|
210
|
+
'setInterval(function(){}, 1000)'
|
|
211
|
+
].join(';\n'));
|
|
212
|
+
|
|
213
|
+
const watcher = new SteadyWatcher({
|
|
214
|
+
pattern: watchedFile.replace(/\\/g, '/'),
|
|
215
|
+
start: `"${process.execPath}" "${parentFile}"`,
|
|
216
|
+
restartOnChange: true,
|
|
217
|
+
initialRun: true,
|
|
218
|
+
delay: 20,
|
|
219
|
+
killTimeout: 1000,
|
|
220
|
+
quiet: true,
|
|
221
|
+
theme: 'none'
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
await watcher.start();
|
|
226
|
+
await waitFor(() => fs.existsSync(pidFile), 4000, 'nested child pid');
|
|
227
|
+
const childPid = Number(fs.readFileSync(pidFile, 'utf8'));
|
|
228
|
+
assert.equal(isPidAlive(childPid), true);
|
|
229
|
+
await watcher.close();
|
|
230
|
+
await waitFor(() => !isPidAlive(childPid), 5000, 'nested child cleanup');
|
|
231
|
+
} finally {
|
|
232
|
+
await watcher.close();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function testWindowsCmdResolutionSmoke() {
|
|
237
|
+
if (process.platform !== 'win32') return;
|
|
238
|
+
|
|
239
|
+
const dir = tempDir('cmd');
|
|
240
|
+
const watchedFile = path.join(dir, 'source.txt');
|
|
241
|
+
const outputFile = path.join(dir, 'out.state');
|
|
242
|
+
const scriptFile = path.join(dir, 'write-output.cmd');
|
|
243
|
+
fs.writeFileSync(watchedFile, 'initial');
|
|
244
|
+
fs.writeFileSync(scriptFile, `@echo off\r\necho ok>"${outputFile}"\r\n`);
|
|
245
|
+
|
|
246
|
+
const watcher = new SteadyWatcher({
|
|
247
|
+
pattern: path.join(dir, '*.txt').replace(/\\/g, '/'),
|
|
248
|
+
cmd: `"${scriptFile}"`,
|
|
249
|
+
initialRun: true,
|
|
250
|
+
delay: 20,
|
|
251
|
+
killTimeout: 1000,
|
|
252
|
+
quiet: true,
|
|
253
|
+
theme: 'none'
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
await watcher.start();
|
|
258
|
+
await waitFor(() => fs.existsSync(outputFile), 4000, '.cmd command execution');
|
|
259
|
+
} finally {
|
|
260
|
+
await watcher.close();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const tests = [
|
|
265
|
+
testCliDefaultMergeAndNoHash,
|
|
266
|
+
testBuildFailurePreservesRunningProcess,
|
|
267
|
+
testCoalescesChangesDuringLifecycle,
|
|
268
|
+
testGitChangedUntrackedAddTriggers,
|
|
269
|
+
testUnexpectedStartExitEvent,
|
|
270
|
+
testStartedProcessTreeCleanup,
|
|
271
|
+
testWindowsCmdResolutionSmoke
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
for (const test of tests) {
|
|
275
|
+
await test();
|
|
276
|
+
console.log(`ok ${test.name}`);
|
|
277
|
+
}
|