|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | import remapping from '@ampproject/remapping'; |
10 | | -import type { TransformResult } from 'esbuild'; |
| 10 | +import type { BuildFailure, TransformResult } from 'esbuild'; |
11 | 11 | import { minify } from 'terser'; |
12 | 12 | import { EsbuildExecutor } from './esbuild-executor'; |
13 | 13 |
|
@@ -100,6 +100,13 @@ let esbuild: EsbuildExecutor | undefined; |
100 | 100 | export default async function ({ asset, options }: OptimizeRequest) { |
101 | 101 | // esbuild is used as a first pass |
102 | 102 | const esbuildResult = await optimizeWithEsbuild(asset.code, asset.name, options); |
| 103 | + if (isEsBuildFailure(esbuildResult)) { |
| 104 | + return { |
| 105 | + name: asset.name, |
| 106 | + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
| 107 | + errors: await esbuild!.formatMessages(esbuildResult.errors, { kind: 'error' }), |
| 108 | + }; |
| 109 | + } |
103 | 110 |
|
104 | 111 | // terser is used as a second pass |
105 | 112 | const terserResult = await optimizeWithTerser( |
@@ -144,28 +151,36 @@ async function optimizeWithEsbuild( |
144 | 151 | content: string, |
145 | 152 | name: string, |
146 | 153 | options: OptimizeRequest['options'], |
147 | | -): Promise<TransformResult> { |
| 154 | +): Promise<TransformResult | BuildFailure> { |
148 | 155 | if (!esbuild) { |
149 | 156 | esbuild = new EsbuildExecutor(options.alwaysUseWasm); |
150 | 157 | } |
151 | 158 |
|
152 | | - return esbuild.transform(content, { |
153 | | - minifyIdentifiers: !options.keepIdentifierNames, |
154 | | - minifySyntax: true, |
155 | | - // NOTE: Disabling whitespace ensures unused pure annotations are kept |
156 | | - minifyWhitespace: false, |
157 | | - pure: ['forwardRef'], |
158 | | - legalComments: options.removeLicenses ? 'none' : 'inline', |
159 | | - sourcefile: name, |
160 | | - sourcemap: options.sourcemap && 'external', |
161 | | - define: options.define, |
162 | | - // This option should always be disabled for browser builds as we don't rely on `.name` |
163 | | - // and causes deadcode to be retained which makes `NG_BUILD_MANGLE` unusable to investigate tree-shaking issues. |
164 | | - // We enable `keepNames` only for server builds as Domino relies on `.name`. |
165 | | - // Once we no longer rely on Domino for SSR we should be able to remove this. |
166 | | - keepNames: options.keepNames, |
167 | | - target: options.target, |
168 | | - }); |
| 159 | + try { |
| 160 | + return await esbuild.transform(content, { |
| 161 | + minifyIdentifiers: !options.keepIdentifierNames, |
| 162 | + minifySyntax: true, |
| 163 | + // NOTE: Disabling whitespace ensures unused pure annotations are kept |
| 164 | + minifyWhitespace: false, |
| 165 | + pure: ['forwardRef'], |
| 166 | + legalComments: options.removeLicenses ? 'none' : 'inline', |
| 167 | + sourcefile: name, |
| 168 | + sourcemap: options.sourcemap && 'external', |
| 169 | + define: options.define, |
| 170 | + // This option should always be disabled for browser builds as we don't rely on `.name` |
| 171 | + // and causes deadcode to be retained which makes `NG_BUILD_MANGLE` unusable to investigate tree-shaking issues. |
| 172 | + // We enable `keepNames` only for server builds as Domino relies on `.name`. |
| 173 | + // Once we no longer rely on Domino for SSR we should be able to remove this. |
| 174 | + keepNames: options.keepNames, |
| 175 | + target: options.target, |
| 176 | + }); |
| 177 | + } catch (error) { |
| 178 | + if (isEsBuildFailure(error)) { |
| 179 | + return error; |
| 180 | + } |
| 181 | + |
| 182 | + throw error; |
| 183 | + } |
169 | 184 | } |
170 | 185 |
|
171 | 186 | /** |
@@ -218,3 +233,12 @@ async function optimizeWithTerser( |
218 | 233 |
|
219 | 234 | return { code: result.code, map: result.map as object }; |
220 | 235 | } |
| 236 | + |
| 237 | +/** |
| 238 | + * Determines if an unknown value is an esbuild BuildFailure error object thrown by esbuild. |
| 239 | + * @param value A potential esbuild BuildFailure error object. |
| 240 | + * @returns `true` if the object is determined to be a BuildFailure object; otherwise, `false`. |
| 241 | + */ |
| 242 | +function isEsBuildFailure(value: unknown): value is BuildFailure { |
| 243 | + return !!value && typeof value === 'object' && 'errors' in value && 'warnings' in value; |
| 244 | +} |
0 commit comments