From 56343e8f03bd3f24b28e9869d743622a12c425b6 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Mon, 20 Oct 2025 17:55:19 +0200 Subject: [PATCH] Enhance meet.sh and typings scripts: add typings watch scripts for CE and PRO, and improve flag handling in watch-with-typings-guard.mjs --- meet.sh | 22 +- pnpm-workspace.yaml | 1 + .../{watch-typings.sh => watch-typings-ce.sh} | 2 + scripts/dev/watch-typings-pro.sh | 38 ++++ scripts/dev/watch-with-typings-guard.mjs | 199 +++++++++++++----- 5 files changed, 199 insertions(+), 63 deletions(-) rename scripts/dev/{watch-typings.sh => watch-typings-ce.sh} (95%) create mode 100755 scripts/dev/watch-typings-pro.sh diff --git a/meet.sh b/meet.sh index d112eac..00b4c6d 100755 --- a/meet.sh +++ b/meet.sh @@ -120,7 +120,7 @@ show_help() { echo " Start development mode with watchers" echo echo -e " ${BLUE}start${NC}" - echo " Start services in production or CI mode" + echo " Start OpenVidu Meet in production or CI mode" echo -e " ${YELLOW}Options:${NC} --prod Start in production mode" echo -e " ${NC} --ci Start in CI mode" echo @@ -376,10 +376,10 @@ add_common_dev_commands() { CMD_COLORS+=("bgRed.white") CMD_COMMANDS+=("npm --prefix $OV_COMPONENTS_DIR install && npm --prefix $OV_COMPONENTS_DIR run lib:serve") - # Typings watcher + # Typings watcher. It generates the typings-ready.flag file when done for other watchers to wait on. CMD_NAMES+=("typings-ce") CMD_COLORS+=("bgGreen.black") - CMD_COMMANDS+=("./scripts/dev/watch-typings.sh") + CMD_COMMANDS+=("./scripts/dev/watch-typings-ce.sh") # shared-meet-components watcher CMD_NAMES+=("shared-meet-components") @@ -410,12 +410,12 @@ add_pro_commands() { # Run backend-pro CMD_NAMES+=("backend-pro") - CMD_COLORS+=("blue") + CMD_COLORS+=("cyan") CMD_COMMANDS+=("node ./scripts/dev/watch-with-typings-guard.mjs 'pnpm run dev:pro-backend'") # Watch backend-ce CMD_NAMES+=("backend-ce-watch") - CMD_COLORS+=("cyan") + CMD_COLORS+=("bgCyan.white") CMD_COMMANDS+=("node ./scripts/dev/watch-with-typings-guard.mjs 'pnpm run --filter @openvidu-meet/backend build:watch'") # Run frontend-pro after components-angular and shared-meet-components are ready @@ -423,10 +423,10 @@ add_pro_commands() { CMD_COLORS+=("magenta") CMD_COMMANDS+=("wait-on ${components_path} && wait-on ${shared_meet_components_path} && sleep 1 && node ./scripts/dev/watch-with-typings-guard.mjs 'pnpm run dev:pro-frontend'") - # Run @openvidu-meet-pro/typings watcher + # Typings watcher for PRO edition. It generates the typings-ready.flag file when done for other watchers to wait on. CMD_NAMES+=("typings-pro") - CMD_COLORS+=("brightGreen") - CMD_COMMANDS+=("pnpm --filter @openvidu-meet-pro/typings run build:watch") + CMD_COLORS+=("bgGreen.black") + CMD_COMMANDS+=("./scripts/dev/watch-typings-pro.sh") } # Helper: Add REST API docs and browser-sync commands @@ -543,8 +543,8 @@ dev() { launch_dev_watchers "$edition" "$components_path" } -# Start services -start_services() { +# Start OpenVidu Meet services in prod or ci mode +start() { MODE="" for arg in "$@"; do case "$arg" in @@ -820,7 +820,7 @@ main() { dev ;; start) - start_services "$@" + start "$@" ;; start-testapp) start_testapp diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b30af1d..9f08461 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,6 +6,7 @@ packages: - meet-ce/backend - meet-pro/frontend - meet-pro/backend + - meet-pro/typings - testapp ignoredBuiltDependencies: diff --git a/scripts/dev/watch-typings.sh b/scripts/dev/watch-typings-ce.sh similarity index 95% rename from scripts/dev/watch-typings.sh rename to scripts/dev/watch-typings-ce.sh index 83dc237..245cc31 100755 --- a/scripts/dev/watch-typings.sh +++ b/scripts/dev/watch-typings-ce.sh @@ -1,4 +1,6 @@ #!/bin/bash + +# Path to the flag file indicating typings are ready FLAG_PATH="./meet-ce/typings/dist/typings-ready.flag" # Remove the flag file if it exists diff --git a/scripts/dev/watch-typings-pro.sh b/scripts/dev/watch-typings-pro.sh new file mode 100755 index 0000000..80e950c --- /dev/null +++ b/scripts/dev/watch-typings-pro.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Path to the flag file indicating typings are ready +FLAG_PATH="./meet-pro/typings/dist/typings-ready.flag" + +# Remove the flag file if it exists +rm -f $FLAG_PATH + +# Create an empty directory for the flag file if it doesn't exist +mkdir -p "$(dirname "$FLAG_PATH")" + +echo "Starting typings watch mode..." +echo "Waiting for initial compilation..." + +# Run tsc in watch mode +pnpm --filter @openvidu-meet-pro/typings run dev | while read line; do + echo "$line" + + # Check for compilation start (remove flag to signal "not ready") + if echo "$line" | grep -q "File change detected. Starting incremental compilation"; then + rm -f $FLAG_PATH + echo "Typings recompiling..." + fi + + # Check for successful compilation (create flag to signal "ready") + if echo "$line" | grep -q "Found 0 errors"; then + # Add small delay to ensure all files are written to disk + sleep 0.2 + touch $FLAG_PATH + echo "✅ Typings ready!" + fi + + # Check for compilation errors + if echo "$line" | grep -q "error TS"; then + rm -f $FLAG_PATH + echo "❌ Typings compilation failed!" + fi +done diff --git a/scripts/dev/watch-with-typings-guard.mjs b/scripts/dev/watch-with-typings-guard.mjs index ae2a24d..3063b47 100755 --- a/scripts/dev/watch-with-typings-guard.mjs +++ b/scripts/dev/watch-with-typings-guard.mjs @@ -12,8 +12,11 @@ const __dirname = dirname(__filename); // Configuration const CE_TYPINGS_FLAG_PATH = resolve(__dirname, '../../meet-ce/typings/dist/typings-ready.flag'); const CE_TYPINGS_DIST = resolve(__dirname, '../../meet-ce/typings/dist'); +const PRO_TYPINGS_FLAG_PATH = resolve(__dirname, '../../meet-pro/typings/dist/typings-ready.flag'); +const PRO_TYPINGS_DIST = resolve(__dirname, '../../meet-pro/typings/dist'); const DEBOUNCE_MS = 500; // Wait 500ms after flag appears before restarting const KILL_TIMEOUT_MS = 5000; // Max time to wait for process to die +const POLL_DIR_MS = 1000; // Polling interval for directories that don't yet exist // Get command from arguments const command = process.argv.slice(2).join(' '); @@ -25,7 +28,8 @@ if (!command) { } let childProcess = null; -let isTypingsReady = false; +// We'll support multiple targets (CE and PRO). Each target has its own ready state. +const targets = []; let pendingRestart = null; let hasStartedOnce = false; let isKilling = false; @@ -34,9 +38,12 @@ let isKilling = false; * Start the child process */ async function startProcess() { - if (!isTypingsReady) { + // Process should start only when all required targets report ready + const allReady = targets.length > 0 && targets.every((t) => t.isReady); + if (!allReady) { if (!hasStartedOnce) { - console.log('Waiting for typings to be ready...'); + const names = targets.map((t) => t.name).join(', '); + console.log(`Waiting for typings to be ready for: ${names}...`); } return; } @@ -155,86 +162,174 @@ function scheduleRestart() { * Check if typings are ready */ function checkTypingsReady() { - const wasReady = isTypingsReady; - isTypingsReady = existsSync(CE_TYPINGS_FLAG_PATH); - - if (!wasReady && isTypingsReady) { - console.log('✅ Typings are ready!'); - scheduleRestart(); - } else if (wasReady && !isTypingsReady) { - console.log('Typings recompiling... (process will restart when ready)'); + let changed = false; + for (const t of targets) { + const wasReady = t.isReady; + t.isReady = existsSync(t.flagPath); + if (!wasReady && t.isReady) { + console.log(`✅ Typings ready for ${t.name}!`); + changed = true; + } else if (wasReady && !t.isReady) { + console.log(`Typings recompiling for ${t.name}... (process will restart when ready)`); + changed = true; + } } - return isTypingsReady; + if (changed) { + scheduleRestart(); + } + + return targets.every((t) => t.isReady); } /** * Watch the flag file */ -function watchFlag() { - console.log(`Watching typings flag: ${CE_TYPINGS_FLAG_PATH}`); +/** + * Create a watcher for a single target that watches its flag dir and dist dir. + * If the directories don't exist yet, we poll until they appear then create watchers. + */ +function watchTarget(target) { + console.log(`Setting up watchers for ${target.name}`); // Initial check checkTypingsReady(); - // Watch the parent directory of the flag - const flagDir = dirname(CE_TYPINGS_FLAG_PATH); + const flagDir = dirname(target.flagPath); + const distDir = target.distPath; - const watcher = watch(flagDir, { recursive: false }, (eventType, filename) => { - if (filename === 'typings-ready.flag') { - checkTypingsReady(); + function createWatchers() { + try { + if (!target.flagWatcher && existsSync(flagDir)) { + target.flagWatcher = watch(flagDir, { recursive: false }, (eventType, filename) => { + if (filename === 'typings-ready.flag') { + checkTypingsReady(); + } + }); + + target.flagWatcher.on('error', (error) => { + console.error(`❌ Watcher error for ${target.name} flag:`, error); + }); + } + + if (!target.distWatcher && existsSync(distDir)) { + target.distWatcher = watch(distDir, { recursive: true }, (eventType, filename) => { + if (filename === 'typings-ready.flag') return; + if (childProcess && target.isReady) { + console.log(`Detected change in ${target.name} typings: ${filename} (will restart when compilation finishes)`); + } + }); + + target.distWatcher.on('error', (error) => { + console.error(`❌ Watcher error for ${target.name} dist:`, error); + }); + } + + // If we have at least one watcher, stop polling + if ((target.flagWatcher || target.distWatcher) && target.poller) { + clearInterval(target.poller); + target.poller = null; + } + } catch (err) { + // Rare race - ignore and keep polling + // console.error(`Error creating watcher for ${target.name}:`, err.message); } - }); + } - watcher.on('error', (error) => { - console.error('❌ Watcher error:', error); - }); + // If directories are already present, create watchers immediately + if (existsSync(flagDir) || existsSync(distDir)) { + createWatchers(); + } - return watcher; + // Start polling for directory creation if watchers not yet created + if (!target.flagWatcher && !target.distWatcher) { + target.poller = setInterval(() => { + createWatchers(); + }, POLL_DIR_MS); + } + + return () => { + if (target.flagWatcher) { + try { target.flagWatcher.close(); } catch (e) {} + target.flagWatcher = null; + } + if (target.distWatcher) { + try { target.distWatcher.close(); } catch (e) {} + target.distWatcher = null; + } + if (target.poller) { + clearInterval(target.poller); + target.poller = null; + } + }; } /** * Watch typings/dist for changes (to trigger restart when ready) */ -function watchTypingsDist() { - console.log(`Watching typings dist: ${CE_TYPINGS_DIST}`); +// Setup targets depending on the provided command. If command mentions meet-pro we include PRO +function setupTargetsFromCommand(cmd) { + const normalized = (cmd || '').toLowerCase(); + const usePro = /\b(?:meet-pro|pro)\b/.test(normalized); - const watcher = watch(CE_TYPINGS_DIST, { recursive: true }, (eventType, filename) => { - // Ignore the flag file itself (handled by watchFlag) - if (filename === 'typings-ready.flag') { - return; - } + // Always include CE by default unless the command explicitly targets only pro and not CE. + // If both are mentioned include both. + const includeCE = true; // keep CE by default - // Only log changes, but don't restart yet - // Restart will happen when flag is recreated (via checkTypingsReady) - if (childProcess && isTypingsReady) { - console.log(`Detected change in typings: ${filename} (will restart when compilation finishes)`); - } - }); + if (includeCE) { + targets.push({ + name: 'CE', + flagPath: CE_TYPINGS_FLAG_PATH, + distPath: CE_TYPINGS_DIST, + isReady: false, + flagWatcher: null, + distWatcher: null, + poller: null, + }); + } - watcher.on('error', (error) => { - console.error('❌ Watcher error:', error); - }); + if (usePro) { + targets.push({ + name: 'PRO', + flagPath: PRO_TYPINGS_FLAG_PATH, + distPath: PRO_TYPINGS_DIST, + isReady: false, + flagWatcher: null, + distWatcher: null, + poller: null, + }); + } - return watcher; + // If the command only mentions PRO and you don't want CE, you could tweak logic here. } -// Start watching -const flagWatcher = watchFlag(); -const distWatcher = watchTypingsDist(); +// Setup targets based on command +setupTargetsFromCommand(command); + +// Create watchers for each target and keep cleanup functions +const cleanupFns = []; +for (const t of targets) { + const cleanup = watchTarget(t); + cleanupFns.push(cleanup); +} + +// Kick an initial check/start attempt (in case flags already ready) +checkTypingsReady(); // Cleanup on exit -process.on('SIGINT', async () => { +async function doCleanupAndExit(code = 0) { console.log('\n🛑 Stopping...'); await killProcess(); - flagWatcher.close(); - distWatcher.close(); - process.exit(0); + for (const fn of cleanupFns) { + try { fn(); } catch (e) {} + } + process.exit(code); +} + +process.on('SIGINT', async () => { + await doCleanupAndExit(0); }); process.on('SIGTERM', async () => { - await killProcess(); - flagWatcher.close(); - distWatcher.close(); - process.exit(0); + await doCleanupAndExit(0); });