diff --git a/packages/theme/src/cli/utilities/errors.ts b/packages/theme/src/cli/utilities/errors.ts index 1d0257d241..b6ee5c89e4 100644 --- a/packages/theme/src/cli/utilities/errors.ts +++ b/packages/theme/src/cli/utilities/errors.ts @@ -3,12 +3,27 @@ import {outputDebug} from '@shopify/cli-kit/node/output' import {renderError, renderFatalError} from '@shopify/cli-kit/node/ui' /** - * Renders an error banner for a failed syncing operation without interrupting the process. + * Renders an error banner for a thrown error with a headline. + * @param headline - The headline for the error. + * @param error - The error to render. + */ +export function renderThrownError(headline: string, error: Error | AbortError) { + if (error instanceof AbortError) { + error.message = `${headline}\n${error.message}` + renderFatalError(error) + } else { + renderError({headline, body: error.message}) + outputDebug(`${headline}\n${error.stack ?? error.message}`) + } +} + +/** + * Creates a function that renders a failed syncing operation without interrupting the process. * @param resource - The file that was being operated on, or a headline for the error. * @param preset - The type of operation that failed. * @returns A function that accepts an error and renders it. */ -export function renderCatchError(resource: string, preset?: 'delete' | 'upload') { +export function createRenderCatchError(resource: string, preset?: 'delete' | 'upload') { let headline = resource if (preset) { @@ -18,31 +33,19 @@ export function renderCatchError(resource: string, preset?: 'delete' | 'upload') : `Failed to upload file "${resource}" to remote theme.` } - return (error: Error | AbortError) => { - if (error instanceof AbortError) { - error.message = `${headline}.\n${error.message}` - renderFatalError(error) - } else { - renderError({headline, body: error.message}) - outputDebug(`${headline}\n${error.stack ?? error.message}`) - } + return (error: Error) => { + renderThrownError(headline, error) } } /** - * Renders a fatal error banner and exits the process without continuing. + * Creates a function that renders a failed syncing operation and exits the process. * @param headline - The headline for the error. * @returns A function that accepts an error and renders it. */ -export function abortCatchError(headline: string) { - return (error: Error | AbortError): never => { - if (error instanceof AbortError) { - error.message = `${headline}\n${error.message}` - renderFatalError(error) - } else { - renderError({headline, body: error.message}) - } - +export function createAbortCatchError(headline: string) { + return (error: Error): never => { + renderThrownError(headline, error) process.exit(1) } } diff --git a/packages/theme/src/cli/utilities/theme-environment/theme-environment.ts b/packages/theme/src/cli/utilities/theme-environment/theme-environment.ts index 5bce94df1b..ba5290d3f6 100644 --- a/packages/theme/src/cli/utilities/theme-environment/theme-environment.ts +++ b/packages/theme/src/cli/utilities/theme-environment/theme-environment.ts @@ -5,7 +5,7 @@ import {getProxyHandler} from './proxy.js' import {reconcileAndPollThemeEditorChanges} from './remote-theme-watcher.js' import {uploadTheme} from '../theme-uploader.js' import {renderTasksToStdErr} from '../theme-ui.js' -import {abortCatchError} from '../errors.js' +import {createAbortCatchError} from '../errors.js' import {createApp, defineEventHandler, defineLazyEventHandler, toNodeListener} from 'h3' import {fetchChecksums} from '@shopify/cli-kit/node/themes/api' import {createServer} from 'node:http' @@ -29,7 +29,7 @@ export function setupDevServer(theme: Theme, ctx: DevServerContext) { } function ensureThemeEnvironmentSetup(theme: Theme, ctx: DevServerContext) { - const abort = abortCatchError('Failed to perform the initial theme synchronization.') + const abort = createAbortCatchError('Failed to perform the initial theme synchronization.') const remoteChecksumsPromise = fetchChecksums(theme.id, ctx.session).catch(abort) diff --git a/packages/theme/src/cli/utilities/theme-environment/theme-polling.ts b/packages/theme/src/cli/utilities/theme-environment/theme-polling.ts index 67d2c0f5fe..67bfbaf861 100644 --- a/packages/theme/src/cli/utilities/theme-environment/theme-polling.ts +++ b/packages/theme/src/cli/utilities/theme-environment/theme-polling.ts @@ -1,5 +1,5 @@ import {timestampDateFormat} from '../../constants.js' -import {renderCatchError} from '../errors.js' +import {renderThrownError} from '../errors.js' import {Checksum, Theme, ThemeFileSystem} from '@shopify/cli-kit/node/themes/types' import {fetchChecksums, fetchThemeAsset} from '@shopify/cli-kit/node/themes/api' import {outputDebug, outputInfo, outputContent, outputToken} from '@shopify/cli-kit/node/output' @@ -46,7 +46,7 @@ export function pollThemeEditorChanges( if (error.message !== lastError) { lastError = error.message - renderCatchError('Error while polling for changes.')(error) + renderThrownError('Error while polling for changes.', error) } if (failedPollingAttempts >= maxPollingAttempts) { diff --git a/packages/theme/src/cli/utilities/theme-fs.ts b/packages/theme/src/cli/utilities/theme-fs.ts index 629898c94b..cec3d5e89e 100644 --- a/packages/theme/src/cli/utilities/theme-fs.ts +++ b/packages/theme/src/cli/utilities/theme-fs.ts @@ -5,7 +5,7 @@ import { getPatternsFromShopifyIgnore, } from './asset-ignore.js' import {Notifier} from './notifier.js' -import {renderCatchError} from './errors.js' +import {createRenderCatchError} from './errors.js' import {DEFAULT_IGNORE_PATTERNS, timestampDateFormat} from '../constants.js' import {glob, readFile, ReadOptions, fileExists, mkdir, writeFile, removeFile} from '@shopify/cli-kit/node/fs' import {joinPath, basename, relativePath} from '@shopify/cli-kit/node/path' @@ -163,7 +163,7 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti return true }) - .catch(renderCatchError(fileKey, 'upload')) + .catch(createRenderCatchError(fileKey, 'upload')) emitEvent(eventName, { fileKey, @@ -200,7 +200,7 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti unsyncedFileKeys.delete(fileKey) outputSyncResult('delete', fileKey) }) - .catch(renderCatchError(fileKey, 'delete')) + .catch(createRenderCatchError(fileKey, 'delete')) } const directoriesToWatch = new Set( diff --git a/packages/theme/src/cli/utilities/theme-uploader.ts b/packages/theme/src/cli/utilities/theme-uploader.ts index 156ad95ccf..f521ee4bb9 100644 --- a/packages/theme/src/cli/utilities/theme-uploader.ts +++ b/packages/theme/src/cli/utilities/theme-uploader.ts @@ -1,7 +1,7 @@ import {partitionThemeFiles} from './theme-fs.js' import {rejectGeneratedStaticAssets} from './asset-checksum.js' import {renderTasksToStdErr} from './theme-ui.js' -import {renderCatchError} from './errors.js' +import {createRenderCatchError} from './errors.js' import {AdminSession} from '@shopify/cli-kit/node/session' import {Result, Checksum, Theme, ThemeFileSystem} from '@shopify/cli-kit/node/themes/types' import {AssetParams, bulkUploadThemeAssets, deleteThemeAsset} from '@shopify/cli-kit/node/themes/api' @@ -139,7 +139,7 @@ function buildDeleteJob( .catch((error: Error) => { failedDeleteAttempts++ if (failedDeleteAttempts > 3) throw error - renderCatchError(file.key, 'delete')(error) + createRenderCatchError(file.key, 'delete')(error) }) .finally(() => { progress.current++