Skip to content

Commit 3b0f0fd

Browse files
nickwesselmanclaude
andcommitted
Await render() in renderTasks/renderSingleTask to prevent dangling promises
The previous pattern resolved the caller's promise from the onComplete callback, which fires before unmountInk() is scheduled. This left the render() promise dangling, causing test timeouts in theme-downloader tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 81cc05d commit 3b0f0fd

2 files changed

Lines changed: 38 additions & 10 deletions

File tree

.changeset/cool-papers-behave.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'@shopify/app': patch
33
---
44

5-
[fix] Task progress bars once again clear when complete
5+
Task progress bars once again clear when complete

packages/cli-kit/src/public/node/ui.tsx

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -486,15 +486,26 @@ interface RenderTasksOptions {
486486
export async function renderTasks<TContext>(
487487
tasks: Task<TContext>[],
488488
{renderOptions, noProgressBar}: RenderTasksOptions = {},
489-
) {
490-
return new Promise<TContext>((resolve, reject) => {
491-
render(<Tasks tasks={tasks} onComplete={resolve} noProgressBar={noProgressBar} />, {
489+
): Promise<TContext> {
490+
let result: TContext | undefined
491+
let taskError: Error | undefined
492+
await render(
493+
<Tasks
494+
tasks={tasks}
495+
onComplete={(ctx) => {
496+
result = ctx
497+
}}
498+
noProgressBar={noProgressBar}
499+
/>,
500+
{
492501
...renderOptions,
493502
exitOnCtrlC: false,
494-
})
495-
.then(() => {})
496-
.catch(reject)
503+
},
504+
).catch((error) => {
505+
taskError = error
497506
})
507+
if (taskError) throw taskError
508+
return result as TContext
498509
}
499510

500511
export interface RenderSingleTaskOptions<T> {
@@ -521,12 +532,29 @@ export async function renderSingleTask<T>({
521532
onAbort,
522533
renderOptions,
523534
}: RenderSingleTaskOptions<T>): Promise<T> {
524-
return new Promise<T>((resolve, reject) => {
525-
render(<SingleTask title={title} task={task} onComplete={resolve} onError={reject} onAbort={onAbort} />, {
535+
let result: T | undefined
536+
let taskError: Error | undefined
537+
await render(
538+
<SingleTask
539+
title={title}
540+
task={task}
541+
onComplete={(innerResult) => {
542+
result = innerResult
543+
}}
544+
onError={(error) => {
545+
taskError = error
546+
}}
547+
onAbort={onAbort}
548+
/>,
549+
{
526550
...renderOptions,
527551
exitOnCtrlC: false,
528-
}).catch(reject)
552+
},
553+
).catch((error) => {
554+
taskError ??= error
529555
})
556+
if (taskError) throw taskError
557+
return result as T
530558
}
531559

532560
export interface RenderTextPromptOptions extends Omit<TextPromptProps, 'onSubmit'> {

0 commit comments

Comments
 (0)