Skip to content

Commit 6d9b360

Browse files
clydinalan-agius4
authored andcommitted
fix(@angular/build): prevent esbuild service hang on internal component stylesheet builds
Switching to esbuild.context() + rebuild() for all builds in PR #33267 meant that all component stylesheet builds (which are separate BundlerContext instances) also created individual esbuild contexts. For builds with large amounts of stylesheets, this resulted in creating and concurrently disposing many esbuild contexts, leading to a timing deadlock/hang in the esbuild service child process. This change adds an alwaysUseContext parameter (defaulting to false) to BundlerContext to allow the main/global bundle contexts to continue using esbuild.context() (to ensure their plugins' onDispose callbacks run), while allowing component stylesheets to safely fall back to one-shot esbuild.build() during non-incremental/one-shot builds.
1 parent 5c77c33 commit 6d9b360

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

packages/angular/build/src/builders/application/setup-bundling.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export function setupBundlerContexts(
6666
angularCompilationContext,
6767
templateUpdates,
6868
),
69+
true,
6970
),
7071
);
7172

@@ -82,6 +83,7 @@ export function setupBundlerContexts(
8283
workspaceRoot,
8384
watch,
8485
browserPolyfillBundleOptions,
86+
true,
8587
);
8688
if (typeof browserPolyfillBundleOptions === 'function') {
8789
otherContexts.push(browserPolyfillContext);
@@ -95,7 +97,9 @@ export function setupBundlerContexts(
9597
for (const initial of [true, false]) {
9698
const bundleOptions = createGlobalStylesBundleOptions(options, target, initial);
9799
if (bundleOptions) {
98-
otherContexts.push(new BundlerContext(workspaceRoot, watch, bundleOptions, () => initial));
100+
otherContexts.push(
101+
new BundlerContext(workspaceRoot, watch, bundleOptions, true, () => initial),
102+
);
99103
}
100104
}
101105
}
@@ -105,7 +109,9 @@ export function setupBundlerContexts(
105109
for (const initial of [true, false]) {
106110
const bundleOptions = createGlobalScriptsBundleOptions(options, target, initial);
107111
if (bundleOptions) {
108-
otherContexts.push(new BundlerContext(workspaceRoot, watch, bundleOptions, () => initial));
112+
otherContexts.push(
113+
new BundlerContext(workspaceRoot, watch, bundleOptions, true, () => initial),
114+
);
109115
}
110116
}
111117
}
@@ -125,6 +131,7 @@ export function setupBundlerContexts(
125131
stylesheetBundler,
126132
angularCompilationContext.createSecondaryContext(),
127133
),
134+
true,
128135
),
129136
);
130137

@@ -141,6 +148,7 @@ export function setupBundlerContexts(
141148
stylesheetBundler,
142149
angularCompilationContext.createSecondaryContext(),
143150
),
151+
true,
144152
),
145153
);
146154
}
@@ -153,7 +161,9 @@ export function setupBundlerContexts(
153161
);
154162

155163
if (serverPolyfillBundleOptions) {
156-
otherContexts.push(new BundlerContext(workspaceRoot, watch, serverPolyfillBundleOptions));
164+
otherContexts.push(
165+
new BundlerContext(workspaceRoot, watch, serverPolyfillBundleOptions, true),
166+
);
157167
}
158168
}
159169

packages/angular/build/src/tools/esbuild/bundler-context.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export class BundlerContext {
7070
private workspaceRoot: string,
7171
private incremental: boolean,
7272
options: BuildOptions | BundlerOptionsFactory,
73+
private alwaysUseContext = false,
7374
private initialFilter?: (initial: Readonly<InitialFileRecord>) => boolean,
7475
) {
7576
// To cache the results an option factory is needed to capture the full set of dependencies
@@ -217,7 +218,7 @@ export class BundlerContext {
217218
if (this.#esbuildContext) {
218219
// Rebuild using the existing incremental build context
219220
result = await this.#esbuildContext.rebuild();
220-
} else {
221+
} else if (this.incremental || this.alwaysUseContext) {
221222
// Create a build context and perform the build.
222223
// Context creation does not perform a build.
223224
const esbuildContext = await context(this.#esbuildOptions);
@@ -227,6 +228,15 @@ export class BundlerContext {
227228
}
228229
this.#esbuildContext = esbuildContext;
229230
result = await this.#esbuildContext.rebuild();
231+
} else {
232+
// For non-incremental builds, perform a single build
233+
if (this.#disposed) {
234+
throw new Error('BundlerContext was disposed during build.');
235+
}
236+
result = await build(this.#esbuildOptions);
237+
if (this.#disposed) {
238+
throw new Error('BundlerContext was disposed during build.');
239+
}
230240
}
231241
} catch (failure) {
232242
// Build failures will throw an exception which contains errors/warnings

0 commit comments

Comments
 (0)