watch-docs.tsā¢4.33 kB
#!/usr/bin/env bun
import { spawn } from 'node:child_process';
import { watch } from 'node:fs';
import { debounce } from 'lodash-es';
/**
* @fileoverview Watch mode for documentation development.
* Automatically rebuilds TypeDoc documentation when source files change.
*
* @author VyOS MCP Server
* @version 1.0.0
* @since 2025-01-13
*/
const SRC_DIR = './src';
const DOCS_DIR = './docs';
let isBuilding = false;
/**
* Build documentation
*/
async function buildDocs(): Promise<void> {
if (isBuilding) {
console.log('ā³ Build already in progress, skipping...');
return;
}
isBuilding = true;
console.log('š Rebuilding documentation...');
const startTime = Date.now();
try {
const result = await new Promise<void>((resolve, reject) => {
const child = spawn(
'bun',
[
'run',
'docs',
],
{
stdio: 'pipe',
shell: true,
},
);
let output = '';
let errorOutput = '';
child.stdout?.on('data', (data) => {
output += data.toString();
});
child.stderr?.on('data', (data) => {
errorOutput += data.toString();
});
child.on('close', (code) => {
if (code === 0) {
resolve();
} else {
reject(
new Error(
`Documentation build failed with code ${code}\n${errorOutput}`,
),
);
}
});
child.on('error', reject);
});
const duration = Date.now() - startTime;
console.log(`ā
Documentation rebuilt in ${duration}ms`);
} catch (error) {
console.error('ā Documentation build failed:', error.message);
} finally {
isBuilding = false;
}
}
/**
* Debounced build function to avoid excessive rebuilds
*/
const debouncedBuild = debounce(buildDocs, 500);
/**
* Start watching for file changes
*/
function startWatching(): void {
console.log('š Watching for changes in:', SRC_DIR);
console.log('š Documentation output:', DOCS_DIR);
console.log('š Press Ctrl+C to stop\n');
// Initial build
buildDocs();
// Watch for changes
const watcher = watch(
SRC_DIR,
{
recursive: true,
},
(eventType, filename) => {
if (!filename) return;
// Only watch TypeScript files
if (!filename.endsWith('.ts')) return;
console.log(`š ${eventType}: ${filename}`);
debouncedBuild();
},
);
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\nš Stopping documentation watcher...');
watcher.close();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\nš Stopping documentation watcher...');
watcher.close();
process.exit(0);
});
}
// Check if lodash-es is available, if not use a simple debounce
if (!Bun.which('lodash-es')) {
// Simple debounce implementation
function simpleDebounce<T extends (...args: any[]) => any>(
func: T,
wait: number,
): (...args: Parameters<T>) => void {
let timeout: Timer | null = null;
return (...args: Parameters<T>) => {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
func(...args);
}, wait);
};
}
const debouncedBuildSimple = simpleDebounce(buildDocs, 500);
// Replace the lodash debounce with our simple implementation
console.log('š¦ Using built-in debounce (lodash-es not available)');
console.log('š Watching for changes in:', SRC_DIR);
console.log('š Documentation output:', DOCS_DIR);
console.log('š Press Ctrl+C to stop\n');
// Initial build
buildDocs();
// Watch for changes
const watcher = watch(
SRC_DIR,
{
recursive: true,
},
(eventType, filename) => {
if (!filename) return;
// Only watch TypeScript files
if (!filename.endsWith('.ts')) return;
console.log(`š ${eventType}: ${filename}`);
debouncedBuildSimple();
},
);
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\nš Stopping documentation watcher...');
watcher.close();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\nš Stopping documentation watcher...');
watcher.close();
process.exit(0);
});
} else {
startWatching();
}