Skip to content

Commit 1f34e44

Browse files
clydinangular-robot[bot]
authored andcommitted
refactor(@angular-devkit/build-angular): use helper to bundle all elements of esbuild builder
The code to execute the individual bundling elements of the esbuild-based browser application builder has been consolidated within a bundler context helper method. This moves the execution and result merging code into a single location and allows for more flexibility to control which elements should be executed per build. The global styles and global scripts bundling is now fully skipped if the options are not present in the build configuration.
1 parent c0a0f72 commit 1f34e44

File tree

2 files changed

+95
-64
lines changed

2 files changed

+95
-64
lines changed

packages/angular_devkit/build_angular/src/builders/browser-esbuild/esbuild.ts

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ import {
2222
import { basename, extname, relative } from 'node:path';
2323
import { FileInfo } from '../../utils/index-file/augment-index-html';
2424

25+
export type BundleContextResult =
26+
| { errors: Message[]; warnings: Message[] }
27+
| {
28+
errors: undefined;
29+
warnings: Message[];
30+
metafile: Metafile;
31+
outputFiles: OutputFile[];
32+
initialFiles: FileInfo[];
33+
};
34+
2535
/**
2636
* Determines if an unknown value is an esbuild BuildFailure error object thrown by esbuild.
2737
* @param value A potential esbuild BuildFailure error object.
@@ -43,6 +53,50 @@ export class BundlerContext {
4353
};
4454
}
4555

56+
static async bundleAll(contexts: Iterable<BundlerContext>): Promise<BundleContextResult> {
57+
const individualResults = await Promise.all([...contexts].map((context) => context.bundle()));
58+
59+
// Return directly if only one result
60+
if (individualResults.length === 1) {
61+
return individualResults[0];
62+
}
63+
64+
let errors: Message[] | undefined;
65+
const warnings: Message[] = [];
66+
const metafile: Metafile = { inputs: {}, outputs: {} };
67+
const initialFiles = [];
68+
const outputFiles = [];
69+
for (const result of individualResults) {
70+
warnings.push(...result.warnings);
71+
if (result.errors) {
72+
errors ??= [];
73+
errors.push(...result.errors);
74+
continue;
75+
}
76+
77+
// Combine metafiles used for the stats option as well as bundle budgets and console output
78+
if (result.metafile) {
79+
metafile.inputs = { ...metafile.inputs, ...result.metafile.inputs };
80+
metafile.outputs = { ...metafile.outputs, ...result.metafile.outputs };
81+
}
82+
83+
initialFiles.push(...result.initialFiles);
84+
outputFiles.push(...result.outputFiles);
85+
}
86+
87+
if (errors !== undefined) {
88+
return { errors, warnings };
89+
}
90+
91+
return {
92+
errors,
93+
warnings,
94+
metafile,
95+
initialFiles,
96+
outputFiles,
97+
};
98+
}
99+
46100
/**
47101
* Executes the esbuild build function and normalizes the build result in the event of a
48102
* build failure that results in no output being generated.
@@ -52,16 +106,7 @@ export class BundlerContext {
52106
* @returns If output files are generated, the full esbuild BuildResult; if not, the
53107
* warnings and errors for the attempted build.
54108
*/
55-
async bundle(): Promise<
56-
| { errors: Message[]; warnings: Message[] }
57-
| {
58-
errors: undefined;
59-
warnings: Message[];
60-
metafile: Metafile;
61-
outputFiles: OutputFile[];
62-
initialFiles: FileInfo[];
63-
}
64-
> {
109+
async bundle(): Promise<BundleContextResult> {
65110
let result;
66111
try {
67112
if (this.#esbuildContext) {

packages/angular_devkit/build_angular/src/builders/browser-esbuild/index.ts

Lines changed: 40 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ async function execute(
108108
const target = transformSupportedBrowsersToTargets(browsers);
109109

110110
// Reuse rebuild state or create new bundle contexts for code and global stylesheets
111+
const bundlerContexts = [];
112+
113+
// Application code
111114
const codeBundleCache = options.watch
112115
? rebuildState?.codeBundleCache ?? new SourceFileCache()
113116
: undefined;
@@ -118,37 +121,38 @@ async function execute(
118121
!!options.watch,
119122
createCodeBundleOptions(options, target, browsers, codeBundleCache),
120123
);
121-
const globalStylesBundleContext =
122-
rebuildState?.globalStylesRebuild ??
123-
new BundlerContext(
124+
bundlerContexts.push(codeBundleContext);
125+
// Global Stylesheets
126+
let globalStylesBundleContext;
127+
if (options.globalStyles.length > 0) {
128+
globalStylesBundleContext =
129+
rebuildState?.globalStylesRebuild ??
130+
new BundlerContext(
131+
workspaceRoot,
132+
!!options.watch,
133+
createGlobalStylesBundleOptions(
134+
options,
135+
target,
136+
browsers,
137+
codeBundleCache?.loadResultCache,
138+
),
139+
);
140+
bundlerContexts.push(globalStylesBundleContext);
141+
}
142+
// Global Scripts
143+
if (options.globalScripts.length > 0) {
144+
const globalScriptsBundleContext = new BundlerContext(
124145
workspaceRoot,
125146
!!options.watch,
126-
createGlobalStylesBundleOptions(options, target, browsers, codeBundleCache?.loadResultCache),
147+
createGlobalScriptsBundleOptions(options),
127148
);
149+
bundlerContexts.push(globalScriptsBundleContext);
150+
}
128151

129-
const globalScriptsBundleContext = new BundlerContext(
130-
workspaceRoot,
131-
!!options.watch,
132-
createGlobalScriptsBundleOptions(options),
133-
);
134-
135-
const [codeResults, styleResults, scriptResults] = await Promise.all([
136-
// Execute esbuild to bundle the application code
137-
codeBundleContext.bundle(),
138-
// Execute esbuild to bundle the global stylesheets
139-
globalStylesBundleContext.bundle(),
140-
globalScriptsBundleContext.bundle(),
141-
]);
152+
const bundlingResult = await BundlerContext.bundleAll(bundlerContexts);
142153

143154
// Log all warnings and errors generated during bundling
144-
await logMessages(context, {
145-
errors: [
146-
...(codeResults.errors || []),
147-
...(styleResults.errors || []),
148-
...(scriptResults.errors || []),
149-
],
150-
warnings: [...codeResults.warnings, ...styleResults.warnings, ...scriptResults.warnings],
151-
});
155+
await logMessages(context, bundlingResult);
152156

153157
const executionResult = new ExecutionResult(
154158
codeBundleContext,
@@ -157,40 +161,22 @@ async function execute(
157161
);
158162

159163
// Return if the bundling has errors
160-
if (codeResults.errors || styleResults.errors || scriptResults.errors) {
164+
if (bundlingResult.errors) {
161165
return executionResult;
162166
}
163167

164-
// Filter global stylesheet initial files
165-
styleResults.initialFiles = styleResults.initialFiles.filter(
166-
({ name }) => options.globalStyles.find((style) => style.name === name)?.initial,
167-
);
168+
// Filter global stylesheet initial files. Currently all initial CSS files are from the global styles option.
169+
if (options.globalScripts.length > 0) {
170+
bundlingResult.initialFiles = bundlingResult.initialFiles.filter(
171+
({ file, name }) =>
172+
!file.endsWith('.css') ||
173+
options.globalStyles.find((style) => style.name === name)?.initial,
174+
);
175+
}
168176

169-
// Combine the bundling output files
170-
const initialFiles: FileInfo[] = [
171-
...codeResults.initialFiles,
172-
...styleResults.initialFiles,
173-
...scriptResults.initialFiles,
174-
];
175-
executionResult.outputFiles.push(
176-
...codeResults.outputFiles,
177-
...styleResults.outputFiles,
178-
...scriptResults.outputFiles,
179-
);
177+
const { metafile, initialFiles, outputFiles } = bundlingResult;
180178

181-
// Combine metafiles used for the stats option as well as bundle budgets and console output
182-
const metafile = {
183-
inputs: {
184-
...codeResults.metafile?.inputs,
185-
...styleResults.metafile?.inputs,
186-
...scriptResults.metafile?.inputs,
187-
},
188-
outputs: {
189-
...codeResults.metafile?.outputs,
190-
...styleResults.metafile?.outputs,
191-
...scriptResults.metafile?.outputs,
192-
},
193-
};
179+
executionResult.outputFiles.push(...outputFiles);
194180

195181
// Check metafile for CommonJS module usage if optimizing scripts
196182
if (optimizationOptions.scripts) {

0 commit comments

Comments
 (0)