1- import { AppEvent , AppEventWatcher , EventType , ExtensionEvent } from './app-event-watcher.js'
1+ import { AppEventWatcher , EventType , ExtensionEvent } from './app-event-watcher.js'
22import { OutputContextOptions , WatcherEvent , FileWatcher } from './file-watcher.js'
3- import { ESBuildContextManager } from './app-watcher-esbuild.js'
43import {
54 testAppAccessConfigExtension ,
65 testAppConfigExtensions ,
@@ -22,7 +21,6 @@ import {joinPath} from '@shopify/cli-kit/node/path'
2221import { Writable } from 'stream'
2322
2423vi . mock ( '../../../models/app/loader.js' )
25- vi . mock ( './app-watcher-esbuild.js' )
2624
2725// Extensions 1 and 1B simulate extensions defined in the same directory (same toml)
2826const extension1 = await testUIExtension ( {
@@ -336,6 +334,24 @@ describe('app-event-watcher', () => {
336334 stdout = { write : vi . fn ( ) }
337335 stderr = { write : vi . fn ( ) }
338336 abortController = new AbortController ( )
337+
338+ // Mock buildForBundle on all test extensions so the watcher doesn't attempt real builds
339+ const allExtensions = [
340+ extension1 ,
341+ extension1B ,
342+ extension2 ,
343+ extension1Updated ,
344+ extension1BUpdated ,
345+ flowExtension ,
346+ posExtension ,
347+ posExtensionUpdated ,
348+ appAccessExtension ,
349+ webhookExtension ,
350+ ]
351+ for ( const ext of allExtensions ) {
352+ vi . spyOn ( ext , 'buildForBundle' ) . mockResolvedValue ( )
353+ vi . spyOn ( ext , 'rescanImports' ) . mockResolvedValue ( false )
354+ }
339355 } )
340356
341357 afterEach ( ( ) => {
@@ -360,9 +376,8 @@ describe('app-event-watcher', () => {
360376 configuration : testAppConfiguration ,
361377 } )
362378
363- const mockManager = new MockESBuildContextManager ( )
364379 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
365- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
380+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
366381 const emitSpy = vi . spyOn ( watcher , 'emit' )
367382 await watcher . start ( { stdout, stderr, signal : abortController . signal } )
368383
@@ -433,9 +448,8 @@ describe('app-event-watcher', () => {
433448 } )
434449 const generateTypesSpy = vi . spyOn ( app , 'generateExtensionTypes' )
435450
436- const mockManager = new MockESBuildContextManager ( )
437451 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
438- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
452+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
439453 const emitSpy = vi . spyOn ( watcher , 'emit' )
440454
441455 // When
@@ -468,9 +482,8 @@ describe('app-event-watcher', () => {
468482 configuration : testAppConfiguration ,
469483 } )
470484
471- const mockManager = new MockESBuildContextManager ( )
472485 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
473- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
486+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
474487 const emitSpy = vi . spyOn ( watcher , 'emit' )
475488
476489 // When
@@ -503,9 +516,8 @@ describe('app-event-watcher', () => {
503516 configuration : testAppConfiguration ,
504517 } )
505518
506- const mockManager = new MockESBuildContextManager ( )
507519 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
508- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
520+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
509521 const emitSpy = vi . spyOn ( watcher , 'emit' )
510522
511523 // When
@@ -535,9 +547,8 @@ describe('app-event-watcher', () => {
535547 } )
536548 const generateTypesSpy = vi . spyOn ( app , 'generateExtensionTypes' )
537549
538- const mockManager = new MockESBuildContextManager ( )
539550 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
540- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
551+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
541552 const emitSpy = vi . spyOn ( watcher , 'emit' )
542553
543554 // When
@@ -571,19 +582,18 @@ describe('app-event-watcher', () => {
571582 ] ,
572583 }
573584
574- const mockManager = new MockESBuildContextManager ( )
575- mockManager . rebuildContext = vi . fn ( ) . mockRejectedValueOnce ( esbuildError )
576-
577585 const buildOutputPath = joinPath ( tmpDir , '.shopify' , 'bundle' )
578586 const app = testAppLinked ( {
579587 allExtensions : [ extension1 ] ,
580588 configPath : 'shopify.app.custom.toml' ,
581589 configuration : testAppConfiguration ,
582590 } )
591+ // First call succeeds (initial build on start), second call fails (file watcher triggered build)
592+ vi . spyOn ( extension1 , 'buildForBundle' ) . mockResolvedValueOnce ( ) . mockRejectedValueOnce ( esbuildError )
583593 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
584594
585595 // When
586- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
596+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
587597 const emitSpy = vi . spyOn ( watcher , 'emit' )
588598 const stderr = { write : vi . fn ( ) } as unknown as Writable
589599 const stdout = { write : vi . fn ( ) } as unknown as Writable
@@ -627,9 +637,9 @@ describe('app-event-watcher', () => {
627637 } )
628638
629639 // When
630- const mockManager = new MockESBuildContextManager ( )
640+
631641 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
632- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
642+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
633643 const emitSpy = vi . spyOn ( watcher , 'emit' )
634644 const stderr = { write : vi . fn ( ) } as unknown as Writable
635645 const stdout = { write : vi . fn ( ) } as unknown as Writable
@@ -661,13 +671,14 @@ describe('app-event-watcher', () => {
661671 configPath : 'shopify.app.custom.toml' ,
662672 configuration : testAppConfiguration ,
663673 } )
674+
675+ // Make rescanImports throw to simulate an uncaught error in the watcher pipeline
676+ vi . spyOn ( extension1 , 'rescanImports' ) . mockRejectedValueOnce ( uncaughtError )
677+
664678 const mockFileWatcher = new MockFileWatcher ( app , outputOptions , [ fileWatchEvent ] )
665679
666680 // When
667- const mockManager = new MockESBuildContextManager ( )
668- mockManager . updateContexts = vi . fn ( ) . mockRejectedValueOnce ( uncaughtError )
669-
670- const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockManager , mockFileWatcher )
681+ const watcher = new AppEventWatcher ( app , 'url' , buildOutputPath , mockFileWatcher )
671682 const emitSpy = vi . spyOn ( watcher , 'emit' )
672683 const stderr = { write : vi . fn ( ) } as unknown as Writable
673684 const stdout = { write : vi . fn ( ) } as unknown as Writable
@@ -684,26 +695,6 @@ describe('app-event-watcher', () => {
684695 } )
685696 } )
686697} )
687- // Mock class for ESBuildContextManager
688- // It handles the ESBuild contexts for the extensions that are being watched
689- class MockESBuildContextManager extends ESBuildContextManager {
690- contexts = {
691- // The keys are the extension handles, the values are the ESBuild contexts mocked
692- uid1 : [ { rebuild : vi . fn ( ) , watch : vi . fn ( ) , serve : vi . fn ( ) , cancel : vi . fn ( ) , dispose : vi . fn ( ) } ] ,
693- uid1B : [ { rebuild : vi . fn ( ) , watch : vi . fn ( ) , serve : vi . fn ( ) , cancel : vi . fn ( ) , dispose : vi . fn ( ) } ] ,
694- uid2 : [ { rebuild : vi . fn ( ) , watch : vi . fn ( ) , serve : vi . fn ( ) , cancel : vi . fn ( ) , dispose : vi . fn ( ) } ] ,
695- 'test-ui-extension' : [ { rebuild : vi . fn ( ) , watch : vi . fn ( ) , serve : vi . fn ( ) , cancel : vi . fn ( ) , dispose : vi . fn ( ) } ] ,
696- }
697-
698- constructor ( ) {
699- super ( { dotEnvVariables : { } , url : 'url' , outputPath : 'outputPath' } )
700- }
701-
702- async createContexts ( extensions : ExtensionInstance [ ] ) { }
703- async updateContexts ( appEvent : AppEvent ) { }
704- async deleteContexts ( extensions : ExtensionInstance [ ] ) { }
705- }
706-
707698// Mock class for FileWatcher
708699// Used to trigger mocked file system events immediately after the watcher is started.
709700class MockFileWatcher extends FileWatcher {
0 commit comments