diff options
Diffstat (limited to 'tree-sitter/dsk/dsk-cli/src/commands/dev.ts')
-rw-r--r-- | tree-sitter/dsk/dsk-cli/src/commands/dev.ts | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/tree-sitter/dsk/dsk-cli/src/commands/dev.ts b/tree-sitter/dsk/dsk-cli/src/commands/dev.ts new file mode 100644 index 0000000..92b2867 --- /dev/null +++ b/tree-sitter/dsk/dsk-cli/src/commands/dev.ts @@ -0,0 +1,105 @@ +/** + * Dev Command - Watch grammar.js and rebuild/test on change + */ + +import { Command } from 'commander'; +import chokidar from 'chokidar'; +import chalk from 'chalk'; +import { existsSync } from 'fs'; +import { execa } from 'execa'; + +/** + * Create the dev command + */ +export function createDevCommand(): Command { + const devCommand = new Command('dev'); + + devCommand + .description('Watch grammar.js and run build + test on changes') + .option('-v, --verbose', 'Show detailed build output') + .option('--quiet', 'Suppress non-error logs') + .option('--debounce <ms>', 'Debounce delay in milliseconds', '150') + .action(async (options) => { + if (!existsSync('grammar.js')) { + console.error(chalk.red('โ No grammar.js found. Are you in a DSL project directory?')); + process.exit(1); + } + + const verbose: boolean = Boolean(options.verbose); + const quiet: boolean = Boolean(options.quiet); + const debounceMs: number = Number.parseInt(options.debounce, 10) || 150; + + if (!quiet) { + console.log(chalk.blue('๐ Watching grammar.js for changes...')); + } + + // Initial build + test + await runBuildAndTest({ verbose, quiet }); + + // Watcher with debounced rebuilds + const watcher = chokidar.watch('grammar.js', { ignoreInitial: true }); + let isRunning = false; + let rerunRequested = false; + let debounceTimer: NodeJS.Timeout | null = null; + + const runOnce = async () => { + if (isRunning) { + rerunRequested = true; + return; + } + isRunning = true; + if (!quiet) { + console.log(chalk.yellow('โป Change detected. Rebuilding...')); + } + try { + await runBuildAndTest({ verbose, quiet }); + if (!quiet) { + console.log(chalk.green('โ Rebuild and tests completed.')); + } + } catch (e) { + // Errors already printed by build/test + } finally { + isRunning = false; + if (rerunRequested) { + rerunRequested = false; + runOnce(); + } + } + }; + + const debouncedTrigger = () => { + if (debounceTimer) clearTimeout(debounceTimer); + debounceTimer = setTimeout(runOnce, debounceMs); + }; + + watcher.on('change', debouncedTrigger); + }); + + return devCommand; +} + +async function runBuildAndTest(opts: { verbose?: boolean; quiet?: boolean }): Promise<void> { + const { verbose, quiet } = opts; + if (!quiet) { + console.log(chalk.blue('๐๏ธ Building...')); + } + try { + const buildArgs = ['build', ...(verbose ? ['--verbose'] : [])]; + await execa('dsk', buildArgs, { stdio: 'inherit' }); + } catch (error: any) { + console.error(chalk.red('โ Build failed. Fix errors and save again.')); + throw error; + } + + if (!quiet) { + console.log(chalk.blue('๐งช Testing...')); + } + try { + await execa('dsk', ['test'], { stdio: 'inherit' }); + } catch (error: any) { + console.error(chalk.red('โ Tests failed. Fix tests and save again.')); + throw error; + } +} + + |