Skip to content

Commit 24a4df1

Browse files
Add resilliency to errors on concurrent build asset writes
1 parent d7ebe8d commit 24a4df1

File tree

1 file changed

+57
-8
lines changed
  • packages/app/src/cli/services/extensions

1 file changed

+57
-8
lines changed

packages/app/src/cli/services/extensions/bundle.ts

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,38 @@ export async function bundleThemeExtension(
6868
options.stdout.write(`Bundling theme extension ${extension.localIdentifier}...`)
6969
const files = await themeExtensionFiles(extension)
7070

71-
await Promise.all(
72-
files.map(function (filepath) {
71+
const results = await Promise.allSettled(
72+
files.map(async function (filepath) {
7373
const relativePathName = relativePath(extension.directory, filepath)
7474
const outputFile = joinPath(extension.outputPath, relativePathName)
75-
if (filepath === outputFile) return
76-
return copyFile(filepath, outputFile)
75+
if (filepath === outputFile) return {status: 'skipped', filepath}
76+
77+
try {
78+
await copyFile(filepath, outputFile)
79+
return {status: 'success', filepath}
80+
// eslint-disable-next-line no-catch-all/no-catch-all
81+
} catch (error) {
82+
// Log warning but don't fail the entire process
83+
// We intentionally catch all errors here to continue copying other files
84+
outputDebug(`Failed to copy file ${filepath}: ${error}`)
85+
return {status: 'failed', filepath, error}
86+
}
7787
}),
7888
)
89+
90+
// Report any failures as warnings
91+
const failures = results.filter((result) => {
92+
return result.status === 'rejected' || (result.status === 'fulfilled' && result.value?.status === 'failed')
93+
})
94+
95+
if (failures.length > 0) {
96+
const failedFiles = failures.map((failure) => {
97+
if (failure.status === 'rejected') return 'unknown file'
98+
const value = (failure as PromiseFulfilledResult<{status: string; filepath: string}>).value
99+
return value.filepath
100+
})
101+
outputDebug(`Warning: ${failures.length} file(s) could not be copied: ${failedFiles.join(', ')}`)
102+
}
79103
}
80104

81105
export async function copyFilesForExtension(
@@ -93,14 +117,39 @@ export async function copyFilesForExtension(
93117
ignore: ignored,
94118
})
95119

96-
await Promise.all(
97-
files.map(function (filepath) {
120+
const results = await Promise.allSettled(
121+
files.map(async function (filepath) {
98122
const relativePathName = relativePath(extension.directory, filepath)
99123
const outputFile = joinPath(extension.outputPath, relativePathName)
100-
if (filepath === outputFile) return
101-
return copyFile(filepath, outputFile)
124+
if (filepath === outputFile) return {status: 'skipped', filepath}
125+
126+
try {
127+
await copyFile(filepath, outputFile)
128+
return {status: 'success', filepath}
129+
// eslint-disable-next-line no-catch-all/no-catch-all
130+
} catch (error) {
131+
// Log warning but don't fail the entire process
132+
// We intentionally catch all errors here to continue copying other files
133+
outputDebug(`Failed to copy file ${filepath}: ${error}`)
134+
return {status: 'failed', filepath, error}
135+
}
102136
}),
103137
)
138+
139+
// Report any failures as warnings
140+
const failures = results.filter((result) => {
141+
return result.status === 'rejected' || (result.status === 'fulfilled' && result.value?.status === 'failed')
142+
})
143+
144+
if (failures.length > 0) {
145+
const failedFiles = failures.map((failure) => {
146+
if (failure.status === 'rejected') return 'unknown file'
147+
const value = (failure as PromiseFulfilledResult<{status: string; filepath: string}>).value
148+
return value.filepath
149+
})
150+
outputDebug(`Warning: ${failures.length} file(s) could not be copied: ${failedFiles.join(', ')}`)
151+
}
152+
104153
options.stdout.write(`${extension.localIdentifier} successfully built`)
105154
}
106155

0 commit comments

Comments
 (0)