@@ -4,6 +4,8 @@ import * as path from "path"
44import { fileURLToPath } from "url"
55import process from "node:process"
66import * as console from "node:console"
7+ import { setTimeout , clearTimeout } from "node:timers"
8+ import chokidar from "chokidar"
79
810import { copyPaths , copyWasms , copyLocales , setupLocaleWatcher } from "@roo-code/build"
911
@@ -121,9 +123,122 @@ async function main() {
121123 ] )
122124
123125 if ( watch ) {
124- await Promise . all ( [ extensionCtx . watch ( ) , workerCtx . watch ( ) ] )
126+ // Use chokidar for file watching with polling support
127+ // This is more reliable than esbuild's native watcher in environments like code-server
128+ const usePolling = process . env . CHOKIDAR_USEPOLLING === "true"
129+ const pollInterval = parseInt ( process . env . CHOKIDAR_INTERVAL || "1000" , 10 )
130+
131+ console . log ( `[${ name } ] ========================================` )
132+ console . log ( `[${ name } ] Starting watch mode` )
133+ console . log ( `[${ name } ] CHOKIDAR_USEPOLLING: ${ process . env . CHOKIDAR_USEPOLLING } ` )
134+ console . log ( `[${ name } ] Polling enabled: ${ usePolling } ` )
135+ console . log ( `[${ name } ] Poll interval: ${ pollInterval } ms` )
136+ console . log ( `[${ name } ] Watching directory: ${ srcDir } ` )
137+ console . log ( `[${ name } ] CWD: ${ process . cwd ( ) } ` )
138+ console . log ( `[${ name } ] ========================================` )
139+
140+ // Initial build
141+ await Promise . all ( [ extensionCtx . rebuild ( ) , workerCtx . rebuild ( ) ] )
125142 copyLocales ( srcDir , distDir )
126143 setupLocaleWatcher ( srcDir , distDir )
144+
145+ // Set up chokidar watcher - watch the srcDir directly
146+ console . log ( `[${ name } ] Setting up chokidar watcher...` )
147+ console . log ( `[${ name } ] srcDir:` , srcDir )
148+
149+ // List files to verify they exist
150+ const extensionTs = path . join ( srcDir , "extension.ts" )
151+ console . log ( `[${ name } ] extension.ts exists:` , fs . existsSync ( extensionTs ) )
152+
153+ const watcher = chokidar . watch ( srcDir , {
154+ ignored : ( filePath ) => {
155+ // Ignore node_modules, dist, and test files
156+ const relativePath = path . relative ( srcDir , filePath )
157+ return relativePath . includes ( "node_modules" ) ||
158+ relativePath . includes ( "dist" ) ||
159+ relativePath . endsWith ( ".spec.ts" ) ||
160+ relativePath . endsWith ( ".test.ts" )
161+ } ,
162+ persistent : true ,
163+ usePolling,
164+ interval : pollInterval ,
165+ ignoreInitial : false , // Count files during initial scan
166+ depth : 10 ,
167+ } )
168+
169+ console . log ( `[${ name } ] Watcher created, waiting for ready event...` )
170+
171+ let rebuildTimeout = null
172+ let fileCount = 0
173+ let isReady = false
174+
175+ const triggerRebuild = ( eventType , filePath ) => {
176+ if ( ! isReady ) return
177+
178+ // Ignore directories that are written to during build
179+ const ignoredPaths = [
180+ "/dist/" , "\\dist\\" , "/dist" , "\\dist" ,
181+ "/node_modules/" , "\\node_modules\\" ,
182+ "/assets/" , "\\assets\\" ,
183+ "/webview-ui/" , "\\webview-ui\\" ,
184+ ]
185+ for ( const ignored of ignoredPaths ) {
186+ if ( filePath . includes ( ignored ) ) {
187+ return
188+ }
189+ }
190+
191+ // Only rebuild for .ts, .tsx source files (not .json since those can be copied)
192+ const shouldRebuild = ( filePath . endsWith ( ".ts" ) || filePath . endsWith ( ".tsx" ) ) &&
193+ ! filePath . endsWith ( ".d.ts" ) && ! filePath . endsWith ( ".spec.ts" ) && ! filePath . endsWith ( ".test.ts" )
194+ if ( ! shouldRebuild ) {
195+ return
196+ }
197+
198+ // Debounce rebuilds
199+ if ( rebuildTimeout ) {
200+ clearTimeout ( rebuildTimeout )
201+ }
202+ rebuildTimeout = setTimeout ( async ( ) => {
203+ console . log ( `[${ name } ] File ${ eventType } : ${ path . relative ( srcDir , filePath ) } ` )
204+ console . log ( `[esbuild-problem-matcher#onStart]` )
205+ try {
206+ await Promise . all ( [ extensionCtx . rebuild ( ) , workerCtx . rebuild ( ) ] )
207+ console . log ( `[esbuild-problem-matcher#onEnd]` )
208+ } catch ( err ) {
209+ console . error ( `[${ name } ] Rebuild failed:` , err . message )
210+ console . log ( `[esbuild-problem-matcher#onEnd]` )
211+ }
212+ } , 200 )
213+ }
214+
215+ watcher . on ( "change" , ( p ) => triggerRebuild ( "changed" , p ) )
216+ watcher . on ( "add" , ( p ) => {
217+ if ( ! isReady && ( p . endsWith ( ".ts" ) || p . endsWith ( ".tsx" ) || p . endsWith ( ".json" ) ) ) {
218+ fileCount ++
219+ }
220+ triggerRebuild ( "added" , p )
221+ } )
222+ watcher . on ( "unlink" , ( p ) => triggerRebuild ( "deleted" , p ) )
223+ watcher . on ( "error" , ( err ) => console . error ( `[${ name } ] Watcher error:` , err ) )
224+ watcher . on ( "ready" , ( ) => {
225+ isReady = true
226+ console . log ( `[${ name } ] ========================================` )
227+ console . log ( `[${ name } ] Watcher ready!` )
228+ console . log ( `[${ name } ] Watching ${ fileCount } files` )
229+ console . log ( `[${ name } ] Listening for changes...` )
230+ console . log ( `[${ name } ] ========================================` )
231+ } )
232+
233+ // Also add a raw event listener to see ALL events
234+ watcher . on ( "raw" , ( event , rawPath , details ) => {
235+ if ( process . env . DEBUG_WATCHER === "true" ) {
236+ console . log ( `[${ name } ] Raw event:` , event , rawPath )
237+ }
238+ } )
239+
240+ // Keep the process running
241+ await new Promise ( ( ) => { } )
127242 } else {
128243 await Promise . all ( [ extensionCtx . rebuild ( ) , workerCtx . rebuild ( ) ] )
129244 await Promise . all ( [ extensionCtx . dispose ( ) , workerCtx . dispose ( ) ] )
0 commit comments