diff --git a/node-src/runBuild.ts b/node-src/runBuild.ts index 4c0da2074..dad72a9c6 100644 --- a/node-src/runBuild.ts +++ b/node-src/runBuild.ts @@ -67,8 +67,7 @@ export async function runBuild(ctx: Context) { throw rewriteErrorMessage(err, missingStories(ctx)); } if (ctx.extraOptions.experimental_abortSignal?.aborted) { - ctx.userError = true; - setExitCode(ctx, exitCodes.BUILD_WAS_CANCELED); + setExitCode(ctx, exitCodes.BUILD_WAS_CANCELED, true); throw rewriteErrorMessage(err, buildCanceled()); } throw rewriteErrorMessage(err, taskError(ctx, err)); @@ -84,14 +83,17 @@ export async function runBuild(ctx: Context) { } } catch (error) { const errors = [].concat(error); // GraphQLClient might throw an array of errors - ctx.options.experimental_onTaskError?.(ctx, { - formattedError: fatalError(ctx, errors), - originalError: error, - }); - if (errors.length && !ctx.userError) { - ctx.log.info(''); - ctx.log.error(fatalError(ctx, errors)); + if (errors.length) { + const formattedError = fatalError(ctx, errors); + ctx.options.experimental_onTaskError?.(ctx, { + formattedError, + originalError: errors[0], + }); + if (!ctx.userError) { + ctx.log.info(''); + ctx.log.error(formattedError); + } } if (!ctx.exitCode) { diff --git a/node-src/tasks/build.ts b/node-src/tasks/build.ts index 74862a854..e8cf1cbbe 100644 --- a/node-src/tasks/build.ts +++ b/node-src/tasks/build.ts @@ -69,14 +69,24 @@ export const buildStorybook = async (ctx: Context) => { logFile.on('error', reject); }); + const { experimental_abortSignal: abortSignal } = ctx.extraOptions; + try { const { command } = ctx.spawnParams; ctx.log.debug('Using spawnParams:', JSON.stringify(ctx.spawnParams, null, 2)); - await Promise.race([ - execa.command(command, { stdio: [null, logFile, logFile] }), - timeoutAfter(ctx.env.STORYBOOK_BUILD_TIMEOUT), - ]); + + let subprocess; + if (abortSignal) { + abortSignal.onabort = () => { + subprocess?.kill('SIGTERM', { forceKillAfterTimeout: 2000 }); + }; + } + subprocess = execa.command(command, { stdio: [null, logFile, logFile] }); + await Promise.race([subprocess, timeoutAfter(ctx.env.STORYBOOK_BUILD_TIMEOUT)]); } catch (e) { + endActivity(ctx); + abortSignal?.throwIfAborted(); + const buildLog = fs.readFileSync(ctx.buildLogFile, 'utf8'); ctx.log.error(buildFailed(ctx, e, buildLog)); setExitCode(ctx, exitCodes.NPM_BUILD_STORYBOOK_FAILED, true); diff --git a/node-src/ui/components/activity.ts b/node-src/ui/components/activity.ts index 6149b43a4..9e60ef63c 100644 --- a/node-src/ui/components/activity.ts +++ b/node-src/ui/components/activity.ts @@ -4,18 +4,18 @@ import { Context, Task } from '../../types'; const renderLoop = (ctx: Context, render: (frame: number) => void) => { const interval = ctx.options.interactive ? 100 : ctx.env.CHROMATIC_OUTPUT_INTERVAL; const maxFrames = ctx.env.CHROMATIC_TIMEOUT / interval; - let done = false; + + let timeout: NodeJS.Timeout; const tick = (frame = 0) => { render(frame); - if (!done && frame < maxFrames) { - setTimeout(() => tick(frame + 1), interval); + if (frame < maxFrames) { + timeout = setTimeout(() => tick(frame + 1), interval); } }; + tick(); return { - end() { - done = true; - }, + end: () => clearTimeout(timeout), }; };