Skip to content

Commit 6996d29

Browse files
committed
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 6996d29

2 files changed

Lines changed: 22 additions & 4 deletions

File tree

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export function setupBundlerContexts(
6666
angularCompilationContext,
6767
templateUpdates,
6868
),
69+
undefined,
70+
true,
6971
),
7072
);
7173

@@ -82,6 +84,8 @@ export function setupBundlerContexts(
8284
workspaceRoot,
8385
watch,
8486
browserPolyfillBundleOptions,
87+
undefined,
88+
true,
8589
);
8690
if (typeof browserPolyfillBundleOptions === 'function') {
8791
otherContexts.push(browserPolyfillContext);
@@ -95,7 +99,9 @@ export function setupBundlerContexts(
9599
for (const initial of [true, false]) {
96100
const bundleOptions = createGlobalStylesBundleOptions(options, target, initial);
97101
if (bundleOptions) {
98-
otherContexts.push(new BundlerContext(workspaceRoot, watch, bundleOptions, () => initial));
102+
otherContexts.push(
103+
new BundlerContext(workspaceRoot, watch, bundleOptions, () => initial, true),
104+
);
99105
}
100106
}
101107
}
@@ -105,7 +111,9 @@ export function setupBundlerContexts(
105111
for (const initial of [true, false]) {
106112
const bundleOptions = createGlobalScriptsBundleOptions(options, target, initial);
107113
if (bundleOptions) {
108-
otherContexts.push(new BundlerContext(workspaceRoot, watch, bundleOptions, () => initial));
114+
otherContexts.push(
115+
new BundlerContext(workspaceRoot, watch, bundleOptions, () => initial, true),
116+
);
109117
}
110118
}
111119
}
@@ -125,6 +133,8 @@ export function setupBundlerContexts(
125133
stylesheetBundler,
126134
angularCompilationContext.createSecondaryContext(),
127135
),
136+
undefined,
137+
true,
128138
),
129139
);
130140

@@ -141,6 +151,8 @@ export function setupBundlerContexts(
141151
stylesheetBundler,
142152
angularCompilationContext.createSecondaryContext(),
143153
),
154+
undefined,
155+
true,
144156
),
145157
);
146158
}
@@ -153,7 +165,9 @@ export function setupBundlerContexts(
153165
);
154166

155167
if (serverPolyfillBundleOptions) {
156-
otherContexts.push(new BundlerContext(workspaceRoot, watch, serverPolyfillBundleOptions));
168+
otherContexts.push(
169+
new BundlerContext(workspaceRoot, watch, serverPolyfillBundleOptions, undefined, true),
170+
);
157171
}
158172
}
159173

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export class BundlerContext {
7171
private incremental: boolean,
7272
options: BuildOptions | BundlerOptionsFactory,
7373
private initialFilter?: (initial: Readonly<InitialFileRecord>) => boolean,
74+
private alwaysUseContext = false,
7475
) {
7576
// To cache the results an option factory is needed to capture the full set of dependencies
7677
this.#shouldCacheResult = incremental && typeof options === 'function';
@@ -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,9 @@ 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+
result = await build(this.#esbuildOptions);
230234
}
231235
} catch (failure) {
232236
// Build failures will throw an exception which contains errors/warnings

0 commit comments

Comments
 (0)