Skip to content

Commit 9f816c7

Browse files
authored
Merge pull request #5283 from Shopify/01-27-all_validations_errors_should_be_handled_by_the_dev-session
All validations errors should be handled by the dev-session
2 parents 44b3d47 + 934980b commit 9f816c7

File tree

4 files changed

+65
-3
lines changed

4 files changed

+65
-3
lines changed

packages/app/src/cli/services/dev/app-events/app-event-watcher.test.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,8 @@ describe('app-event-watcher', () => {
382382
}
383383

384384
// Given
385-
const esbuildError = {message: 'Build failed'}
386-
flowExtension.buildForBundle = vi.fn().mockRejectedValueOnce(esbuildError)
385+
const buildError = {message: 'Build failed'}
386+
flowExtension.buildForBundle = vi.fn().mockRejectedValueOnce(buildError)
387387

388388
const buildOutputPath = joinPath(tmpDir, '.shopify', 'bundle')
389389
const app = testAppLinked({
@@ -406,6 +406,44 @@ describe('app-event-watcher', () => {
406406
expect(stderr.write).toHaveBeenCalledWith(`Build failed`)
407407
})
408408
})
409+
410+
test('uncaught errors are emitted', async () => {
411+
await inTemporaryDirectory(async (tmpDir) => {
412+
const fileWatchEvent: WatcherEvent = {
413+
type: 'file_updated',
414+
path: '/extensions/ui_extension_1/src/file.js',
415+
extensionPath: '/extensions/ui_extension_1',
416+
startTime: [0, 0],
417+
}
418+
419+
// Given
420+
const uncaughtError = new Error('Unexpected error')
421+
422+
const buildOutputPath = joinPath(tmpDir, '.shopify', 'bundle')
423+
const app = testAppLinked({
424+
allExtensions: [extension1],
425+
configuration: {scopes: '', extension_directories: [], path: 'shopify.app.custom.toml'},
426+
})
427+
const mockFileWatcher = new MockFileWatcher(app, outputOptions, [fileWatchEvent])
428+
429+
// When
430+
const mockManager = new MockESBuildContextManager()
431+
mockManager.updateContexts = vi.fn().mockRejectedValueOnce(uncaughtError)
432+
433+
const watcher = new AppEventWatcher(app, 'url', buildOutputPath, mockManager, mockFileWatcher)
434+
const stderr = {write: vi.fn()} as unknown as Writable
435+
const stdout = {write: vi.fn()} as unknown as Writable
436+
const errorHandler = vi.fn()
437+
watcher.onError(errorHandler)
438+
439+
await watcher.start({stdout, stderr, signal: abortController.signal})
440+
441+
await flushPromises()
442+
443+
// Then
444+
expect(errorHandler).toHaveBeenCalledWith(uncaughtError)
445+
})
446+
})
409447
})
410448
})
411449
// Mock class for ESBuildContextManager

packages/app/src/cli/services/dev/app-events/app-event-watcher.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export class AppEventWatcher extends EventEmitter {
165165
this.emit('all', appEvent)
166166
})
167167
.catch((error) => {
168-
this.options.stderr.write(`Error handling event: ${error.message}`)
168+
this.emit('error', error)
169169
})
170170
})
171171
await this.fileWatcher.start()
@@ -204,6 +204,12 @@ export class AppEventWatcher extends EventEmitter {
204204
return this
205205
}
206206

207+
onError(listener: (error: Error) => Promise<void> | void) {
208+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
209+
this.addListener('error', listener)
210+
return this
211+
}
212+
207213
/**
208214
* Deletes the build output for the given extensions.
209215
*

packages/app/src/cli/services/dev/processes/dev-session.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ describe('pushUpdatesForDevSession', () => {
196196
expect(stdout.write).toHaveBeenCalledWith(expect.stringContaining('Action required'))
197197
expect(stdout.write).toHaveBeenCalledWith(expect.stringContaining('Scopes updated'))
198198
expect(contextSpy).toHaveBeenCalledWith({outputPrefix: 'dev-session', stripAnsi: false}, expect.anything())
199+
contextSpy.mockRestore()
199200
})
200201

201202
test('update is retried if there is an error', async () => {
@@ -269,4 +270,18 @@ describe('pushUpdatesForDevSession', () => {
269270
expect(devSessionStatusManager.status.isReady).toBe(false)
270271
expect(devSessionStatusManager.status.previewURL).toBeUndefined()
271272
})
273+
274+
test('handles error events from the app watcher', async () => {
275+
// Given
276+
const testError = new Error('Watcher error')
277+
278+
// When
279+
await pushUpdatesForDevSession({stderr, stdout, abortSignal: abortController.signal}, options)
280+
appWatcher.emit('error', testError)
281+
await flushPromises()
282+
283+
// Then
284+
expect(stdout.write).toHaveBeenCalledWith(expect.stringContaining('Error'))
285+
expect(stdout.write).toHaveBeenCalledWith(expect.stringContaining('Watcher error'))
286+
})
272287
})

packages/app/src/cli/services/dev/processes/dev-session.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ export const pushUpdatesForDevSession: DevProcessFunction<DevSessionOptions> = a
150150
const result = await bundleExtensionsAndUpload({...processOptions, app: event.app})
151151
await handleDevSessionResult(result, {...processOptions, app: event.app})
152152
})
153+
.onError(async (error) => {
154+
await handleDevSessionResult({status: 'unknown-error', error}, processOptions)
155+
})
153156
}
154157

155158
async function handleDevSessionResult(

0 commit comments

Comments
 (0)