Skip to content

Commit 72629bd

Browse files
committed
refactor: move esbuild index generator, code bundle option and execution results
This commit extracts code in separate files. (cherry picked from commit 6bebc45)
1 parent 15e0a88 commit 72629bd

File tree

4 files changed

+304
-222
lines changed

4 files changed

+304
-222
lines changed

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

Lines changed: 10 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -7,95 +7,36 @@
77
*/
88

99
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
10-
import type { BuildOptions, OutputFile } from 'esbuild';
10+
import type { OutputFile } from 'esbuild';
1111
import fs from 'node:fs/promises';
1212
import path from 'node:path';
13-
import { SourceFileCache, createCompilerPlugin } from '../../tools/esbuild/angular/compiler-plugin';
13+
import { SourceFileCache } from '../../tools/esbuild/angular/compiler-plugin';
14+
import { createCodeBundleOptions } from '../../tools/esbuild/application-code-bundle';
1415
import { BundlerContext } from '../../tools/esbuild/bundler-context';
16+
import { ExecutionResult, RebuildState } from '../../tools/esbuild/bundler-execution-result';
1517
import { checkCommonJSModules } from '../../tools/esbuild/commonjs-checker';
16-
import { createExternalPackagesPlugin } from '../../tools/esbuild/external-packages-plugin';
1718
import { createGlobalScriptsBundleOptions } from '../../tools/esbuild/global-scripts';
1819
import { createGlobalStylesBundleOptions } from '../../tools/esbuild/global-styles';
20+
import { generateIndexHtml } from '../../tools/esbuild/index-html-generator';
1921
import { extractLicenses } from '../../tools/esbuild/license-extractor';
20-
import { createSourcemapIngorelistPlugin } from '../../tools/esbuild/sourcemap-ignorelist-plugin';
2122
import { shutdownSassWorkerPool } from '../../tools/esbuild/stylesheets/sass-language';
2223
import {
2324
calculateEstimatedTransferSizes,
24-
createOutputFileFromText,
25-
getFeatureSupport,
2625
logBuildStats,
2726
logMessages,
2827
withNoProgress,
2928
withSpinner,
3029
writeResultFiles,
3130
} from '../../tools/esbuild/utils';
32-
import { createVirtualModulePlugin } from '../../tools/esbuild/virtual-module-plugin';
33-
import type { ChangedFiles } from '../../tools/esbuild/watcher';
3431
import { copyAssets } from '../../utils/copy-assets';
3532
import { assertIsError } from '../../utils/error';
3633
import { transformSupportedBrowsersToTargets } from '../../utils/esbuild-targets';
37-
import { IndexHtmlGenerator } from '../../utils/index-file/index-html-generator';
3834
import { augmentAppWithServiceWorkerEsbuild } from '../../utils/service-worker';
3935
import { getSupportedBrowsers } from '../../utils/supported-browsers';
4036
import { logBuilderStatusWarnings } from './builder-status-warnings';
4137
import { BrowserEsbuildOptions, NormalizedBrowserOptions, normalizeOptions } from './options';
4238
import { Schema as BrowserBuilderOptions } from './schema';
4339

44-
interface RebuildState {
45-
rebuildContexts: BundlerContext[];
46-
codeBundleCache?: SourceFileCache;
47-
fileChanges: ChangedFiles;
48-
}
49-
50-
/**
51-
* Represents the result of a single builder execute call.
52-
*/
53-
class ExecutionResult {
54-
readonly outputFiles: OutputFile[] = [];
55-
readonly assetFiles: { source: string; destination: string }[] = [];
56-
57-
constructor(
58-
private rebuildContexts: BundlerContext[],
59-
private codeBundleCache?: SourceFileCache,
60-
) {}
61-
62-
addOutputFile(path: string, content: string): void {
63-
this.outputFiles.push(createOutputFileFromText(path, content));
64-
}
65-
66-
get output() {
67-
return {
68-
success: this.outputFiles.length > 0,
69-
};
70-
}
71-
72-
get outputWithFiles() {
73-
return {
74-
success: this.outputFiles.length > 0,
75-
outputFiles: this.outputFiles,
76-
assetFiles: this.assetFiles,
77-
};
78-
}
79-
80-
get watchFiles() {
81-
return this.codeBundleCache?.referencedFiles ?? [];
82-
}
83-
84-
createRebuildState(fileChanges: ChangedFiles): RebuildState {
85-
this.codeBundleCache?.invalidate([...fileChanges.modified, ...fileChanges.removed]);
86-
87-
return {
88-
rebuildContexts: this.rebuildContexts,
89-
codeBundleCache: this.codeBundleCache,
90-
fileChanges,
91-
};
92-
}
93-
94-
async dispose(): Promise<void> {
95-
await Promise.allSettled(this.rebuildContexts.map((context) => context.dispose()));
96-
}
97-
}
98-
9940
async function execute(
10041
options: NormalizedBrowserOptions,
10142
context: BuilderContext,
@@ -188,39 +129,11 @@ async function execute(
188129

189130
// Generate index HTML file
190131
if (indexHtmlOptions) {
191-
// Create an index HTML generator that reads from the in-memory output files
192-
const indexHtmlGenerator = new IndexHtmlGenerator({
193-
indexPath: indexHtmlOptions.input,
194-
entrypoints: indexHtmlOptions.insertionOrder,
195-
sri: options.subresourceIntegrity,
196-
optimization: optimizationOptions,
197-
crossOrigin: options.crossOrigin,
198-
});
199-
200-
/** Virtual output path to support reading in-memory files. */
201-
const virtualOutputPath = '/';
202-
indexHtmlGenerator.readAsset = async function (filePath: string): Promise<string> {
203-
// Remove leading directory separator
204-
const relativefilePath = path.relative(virtualOutputPath, filePath);
205-
const file = executionResult.outputFiles.find((file) => file.path === relativefilePath);
206-
if (file) {
207-
return file.text;
208-
}
209-
210-
throw new Error(`Output file does not exist: ${path}`);
211-
};
212-
213-
const { content, warnings, errors } = await indexHtmlGenerator.process({
214-
baseHref: options.baseHref,
215-
lang: undefined,
216-
outputPath: virtualOutputPath,
217-
files: [...initialFiles].map(([file, record]) => ({
218-
name: record.name ?? '',
219-
file,
220-
extension: path.extname(file),
221-
})),
222-
});
223-
132+
const { errors, warnings, content } = await generateIndexHtml(
133+
initialFiles,
134+
executionResult,
135+
options,
136+
);
224137
for (const error of errors) {
225138
context.logger.error(error);
226139
}
@@ -283,131 +196,6 @@ async function execute(
283196
return executionResult;
284197
}
285198

286-
function createCodeBundleOptions(
287-
options: NormalizedBrowserOptions,
288-
target: string[],
289-
browsers: string[],
290-
sourceFileCache?: SourceFileCache,
291-
): BuildOptions {
292-
const {
293-
workspaceRoot,
294-
entryPoints,
295-
optimizationOptions,
296-
sourcemapOptions,
297-
tsconfig,
298-
outputNames,
299-
outExtension,
300-
fileReplacements,
301-
externalDependencies,
302-
preserveSymlinks,
303-
stylePreprocessorOptions,
304-
advancedOptimizations,
305-
inlineStyleLanguage,
306-
jit,
307-
tailwindConfiguration,
308-
} = options;
309-
310-
const buildOptions: BuildOptions = {
311-
absWorkingDir: workspaceRoot,
312-
bundle: true,
313-
format: 'esm',
314-
entryPoints,
315-
entryNames: outputNames.bundles,
316-
assetNames: outputNames.media,
317-
target,
318-
supported: getFeatureSupport(target),
319-
mainFields: ['es2020', 'browser', 'module', 'main'],
320-
conditions: ['es2020', 'es2015', 'module'],
321-
resolveExtensions: ['.ts', '.tsx', '.mjs', '.js'],
322-
metafile: true,
323-
legalComments: options.extractLicenses ? 'none' : 'eof',
324-
logLevel: options.verbose ? 'debug' : 'silent',
325-
minify: optimizationOptions.scripts,
326-
pure: ['forwardRef'],
327-
outdir: workspaceRoot,
328-
outExtension: outExtension ? { '.js': `.${outExtension}` } : undefined,
329-
sourcemap: sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
330-
splitting: true,
331-
tsconfig,
332-
external: externalDependencies,
333-
write: false,
334-
platform: 'browser',
335-
preserveSymlinks,
336-
plugins: [
337-
createSourcemapIngorelistPlugin(),
338-
createCompilerPlugin(
339-
// JS/TS options
340-
{
341-
sourcemap: !!sourcemapOptions.scripts,
342-
thirdPartySourcemaps: sourcemapOptions.vendor,
343-
tsconfig,
344-
jit,
345-
advancedOptimizations,
346-
fileReplacements,
347-
sourceFileCache,
348-
loadResultCache: sourceFileCache?.loadResultCache,
349-
},
350-
// Component stylesheet options
351-
{
352-
workspaceRoot,
353-
optimization: !!optimizationOptions.styles.minify,
354-
sourcemap:
355-
// Hidden component stylesheet sourcemaps are inaccessible which is effectively
356-
// the same as being disabled. Disabling has the advantage of avoiding the overhead
357-
// of sourcemap processing.
358-
!!sourcemapOptions.styles && (sourcemapOptions.hidden ? false : 'inline'),
359-
outputNames,
360-
includePaths: stylePreprocessorOptions?.includePaths,
361-
externalDependencies,
362-
target,
363-
inlineStyleLanguage,
364-
preserveSymlinks,
365-
browsers,
366-
tailwindConfiguration,
367-
},
368-
),
369-
],
370-
define: {
371-
// Only set to false when script optimizations are enabled. It should not be set to true because
372-
// Angular turns `ngDevMode` into an object for development debugging purposes when not defined
373-
// which a constant true value would break.
374-
...(optimizationOptions.scripts ? { 'ngDevMode': 'false' } : undefined),
375-
'ngJitMode': jit ? 'true' : 'false',
376-
},
377-
};
378-
379-
if (options.externalPackages) {
380-
buildOptions.plugins ??= [];
381-
buildOptions.plugins.push(createExternalPackagesPlugin());
382-
}
383-
384-
const polyfills = options.polyfills ? [...options.polyfills] : [];
385-
if (jit) {
386-
polyfills.push('@angular/compiler');
387-
}
388-
389-
if (polyfills?.length) {
390-
const namespace = 'angular:polyfills';
391-
buildOptions.entryPoints = {
392-
...buildOptions.entryPoints,
393-
['polyfills']: namespace,
394-
};
395-
396-
buildOptions.plugins?.unshift(
397-
createVirtualModulePlugin({
398-
namespace,
399-
loadContent: () => ({
400-
contents: polyfills.map((file) => `import '${file.replace(/\\/g, '/')}';`).join('\n'),
401-
loader: 'js',
402-
resolveDir: workspaceRoot,
403-
}),
404-
}),
405-
);
406-
}
407-
408-
return buildOptions;
409-
}
410-
411199
/**
412200
* Main execution function for the esbuild-based application builder.
413201
* The options are compatible with the Webpack-based builder.

0 commit comments

Comments
 (0)