From aff4d525685c840c0db280c345c445ec63561321 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 4 May 2026 15:07:32 -0400 Subject: [PATCH] refactor(@angular/build): eliminate circular dependencies in bundler context Relocate foundational bundler file types and creation utilities to a new, standalone module to resolve multiple recursive dependencies centered around the utilities implementation. By decoupling the core output file definitions from the heavier build contexts, the overall module graph is streamlined, ensuring cleaner isolation and enabling the removal of several longstanding circular paths from the package golden file. --- goldens/circular-deps/packages.json | 31 +--- goldens/public-api/angular/build/index.api.md | 2 +- .../src/builders/application/build-action.ts | 2 +- .../builders/application/chunk-optimizer.ts | 10 +- .../src/builders/application/execute-build.ts | 7 +- .../application/execute-post-bundle.ts | 16 +- .../build/src/builders/application/i18n.ts | 2 +- .../build/src/builders/application/index.ts | 2 +- .../build/src/builders/application/results.ts | 2 +- .../build/src/builders/dev-server/internal.ts | 2 +- .../angular/build/src/builders/karma/utils.ts | 2 +- packages/angular/build/src/index.ts | 2 +- .../esbuild/angular/component-stylesheets.ts | 8 +- .../tools/esbuild/application-code-bundle.ts | 3 +- .../build/src/tools/esbuild/budget-stats.ts | 6 +- .../src/tools/esbuild/bundler-context.ts | 31 +--- .../tools/esbuild/bundler-execution-result.ts | 4 +- .../build/src/tools/esbuild/bundler-files.ts | 138 ++++++++++++++++++ .../build/src/tools/esbuild/i18n-inliner.ts | 3 +- .../src/tools/esbuild/index-html-generator.ts | 2 +- .../angular/build/src/tools/esbuild/utils.ts | 126 +--------------- .../src/utils/server-rendering/manifest.ts | 21 ++- .../src/utils/server-rendering/prerender.ts | 2 +- .../angular/build/src/utils/service-worker.ts | 2 +- .../angular/build/src/utils/test-files.ts | 2 +- 25 files changed, 202 insertions(+), 226 deletions(-) create mode 100644 packages/angular/build/src/tools/esbuild/bundler-files.ts diff --git a/goldens/circular-deps/packages.json b/goldens/circular-deps/packages.json index 4321adf60eaa..fe51488c7066 100644 --- a/goldens/circular-deps/packages.json +++ b/goldens/circular-deps/packages.json @@ -1,30 +1 @@ -[ - [ - "packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts", - "packages/angular/build/src/tools/esbuild/bundler-context.ts", - "packages/angular/build/src/tools/esbuild/utils.ts", - "packages/angular/build/src/tools/esbuild/bundler-execution-result.ts" - ], - [ - "packages/angular/build/src/tools/esbuild/bundler-context.ts", - "packages/angular/build/src/tools/esbuild/utils.ts" - ], - [ - "packages/angular/build/src/tools/esbuild/bundler-context.ts", - "packages/angular/build/src/tools/esbuild/utils.ts", - "packages/angular/build/src/tools/esbuild/bundler-execution-result.ts" - ], - [ - "packages/angular/build/src/tools/esbuild/bundler-context.ts", - "packages/angular/build/src/tools/esbuild/utils.ts", - "packages/angular/build/src/utils/server-rendering/manifest.ts" - ], - [ - "packages/angular/build/src/tools/esbuild/bundler-execution-result.ts", - "packages/angular/build/src/tools/esbuild/utils.ts" - ], - [ - "packages/angular/build/src/tools/esbuild/utils.ts", - "packages/angular/build/src/utils/server-rendering/manifest.ts" - ] -] +[] diff --git a/goldens/public-api/angular/build/index.api.md b/goldens/public-api/angular/build/index.api.md index 3ca6e8d98d12..df3f8fe8c777 100644 --- a/goldens/public-api/angular/build/index.api.md +++ b/goldens/public-api/angular/build/index.api.md @@ -8,7 +8,7 @@ import { BuilderContext } from '@angular-devkit/architect'; import { BuilderOutput } from '@angular-devkit/architect'; import type { ConfigOptions } from 'karma'; import type http from 'node:http'; -import { OutputFile } from 'esbuild'; +import type { OutputFile } from 'esbuild'; import type { Plugin as Plugin_2 } from 'esbuild'; // @public (undocumented) diff --git a/packages/angular/build/src/builders/application/build-action.ts b/packages/angular/build/src/builders/application/build-action.ts index afc59785be7d..d483d32909c0 100644 --- a/packages/angular/build/src/builders/application/build-action.ts +++ b/packages/angular/build/src/builders/application/build-action.ts @@ -9,8 +9,8 @@ import { BuilderContext } from '@angular-devkit/architect'; import { existsSync } from 'node:fs'; import path from 'node:path'; -import { BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context'; import { ExecutionResult, RebuildState } from '../../tools/esbuild/bundler-execution-result'; +import { BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-files'; import { shutdownSassWorkerPool } from '../../tools/esbuild/stylesheets/sass-language'; import { logMessages, withNoProgress, withSpinner } from '../../tools/esbuild/utils'; import { ChangedFiles } from '../../tools/esbuild/watcher'; diff --git a/packages/angular/build/src/builders/application/chunk-optimizer.ts b/packages/angular/build/src/builders/application/chunk-optimizer.ts index 5898c95401ee..1586b48155ea 100644 --- a/packages/angular/build/src/builders/application/chunk-optimizer.ts +++ b/packages/angular/build/src/builders/application/chunk-optimizer.ts @@ -20,13 +20,13 @@ import type { Message, Metafile } from 'esbuild'; import assert from 'node:assert'; import { type Plugin, rollup } from 'rollup'; +import { BundleContextResult } from '../../tools/esbuild/bundler-context'; import { - BuildOutputFile, + type BuildOutputFile, BuildOutputFileType, - BundleContextResult, - InitialFileRecord, -} from '../../tools/esbuild/bundler-context'; -import { createOutputFile } from '../../tools/esbuild/utils'; + type InitialFileRecord, + createOutputFile, +} from '../../tools/esbuild/bundler-files'; import { useRolldownChunks } from '../../utils/environment-options'; import { assertIsError } from '../../utils/error'; import { toPosixPath } from '../../utils/path'; diff --git a/packages/angular/build/src/builders/application/execute-build.ts b/packages/angular/build/src/builders/application/execute-build.ts index 5aefadc9d904..69b6db0c736d 100644 --- a/packages/angular/build/src/builders/application/execute-build.ts +++ b/packages/angular/build/src/builders/application/execute-build.ts @@ -10,12 +10,9 @@ import { BuilderContext } from '@angular-devkit/architect'; import { createAngularCompilation } from '../../tools/angular/compilation'; import { SourceFileCache } from '../../tools/esbuild/angular/source-file-cache'; import { generateBudgetStats } from '../../tools/esbuild/budget-stats'; -import { - BuildOutputFileType, - BundleContextResult, - BundlerContext, -} from '../../tools/esbuild/bundler-context'; +import { BundleContextResult, BundlerContext } from '../../tools/esbuild/bundler-context'; import { ExecutionResult, RebuildState } from '../../tools/esbuild/bundler-execution-result'; +import { BuildOutputFileType } from '../../tools/esbuild/bundler-files'; import { checkCommonJSModules } from '../../tools/esbuild/commonjs-checker'; import { extractLicenses } from '../../tools/esbuild/license-extractor'; import { profileAsync } from '../../tools/esbuild/profiling'; diff --git a/packages/angular/build/src/builders/application/execute-post-bundle.ts b/packages/angular/build/src/builders/application/execute-post-bundle.ts index 5171ca254d5d..aa931aaa5206 100644 --- a/packages/angular/build/src/builders/application/execute-post-bundle.ts +++ b/packages/angular/build/src/builders/application/execute-post-bundle.ts @@ -9,16 +9,16 @@ import type { Metafile } from 'esbuild'; import assert from 'node:assert'; import { - BuildOutputFile, - BuildOutputFileType, - InitialFileRecord, -} from '../../tools/esbuild/bundler-context'; -import { - BuildOutputAsset, - PrerenderedRoutesRecord, + type BuildOutputAsset, + type PrerenderedRoutesRecord, } from '../../tools/esbuild/bundler-execution-result'; +import { + type BuildOutputFile, + BuildOutputFileType, + type InitialFileRecord, + createOutputFile, +} from '../../tools/esbuild/bundler-files'; import { generateIndexHtml } from '../../tools/esbuild/index-html-generator'; -import { createOutputFile } from '../../tools/esbuild/utils'; import { maxWorkers } from '../../utils/environment-options'; import { SERVER_APP_MANIFEST_FILENAME, diff --git a/packages/angular/build/src/builders/application/i18n.ts b/packages/angular/build/src/builders/application/i18n.ts index 081be50e7a9f..c83f1a29a30a 100644 --- a/packages/angular/build/src/builders/application/i18n.ts +++ b/packages/angular/build/src/builders/application/i18n.ts @@ -9,11 +9,11 @@ import { BuilderContext } from '@angular-devkit/architect'; import type { Metafile } from 'esbuild'; import { join } from 'node:path'; -import { BuildOutputFileType, InitialFileRecord } from '../../tools/esbuild/bundler-context'; import { ExecutionResult, PrerenderedRoutesRecord, } from '../../tools/esbuild/bundler-execution-result'; +import { BuildOutputFileType, InitialFileRecord } from '../../tools/esbuild/bundler-files'; import { I18nInliner } from '../../tools/esbuild/i18n-inliner'; import { maxWorkers } from '../../utils/environment-options'; import { loadTranslations } from '../../utils/i18n-options'; diff --git a/packages/angular/build/src/builders/application/index.ts b/packages/angular/build/src/builders/application/index.ts index d4671decc145..f127ef4bdc7f 100644 --- a/packages/angular/build/src/builders/application/index.ts +++ b/packages/angular/build/src/builders/application/index.ts @@ -10,7 +10,7 @@ import { Builder, BuilderContext, BuilderOutput, createBuilder } from '@angular- import assert from 'node:assert'; import fs from 'node:fs/promises'; import path from 'node:path'; -import { BuildOutputFileType } from '../../tools/esbuild/bundler-context'; +import { BuildOutputFileType } from '../../tools/esbuild/bundler-files'; import { createJsonBuildManifest, emitFilesToDisk } from '../../tools/esbuild/utils'; import { colors as ansiColors } from '../../utils/color'; import { deleteOutputDir } from '../../utils/delete-output-dir'; diff --git a/packages/angular/build/src/builders/application/results.ts b/packages/angular/build/src/builders/application/results.ts index 3e3f0dd99315..6fa23f9f19cd 100644 --- a/packages/angular/build/src/builders/application/results.ts +++ b/packages/angular/build/src/builders/application/results.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { BuildOutputFileType } from '../../tools/esbuild/bundler-context'; +import { BuildOutputFileType } from '../../tools/esbuild/bundler-files'; export enum ResultKind { Failure, diff --git a/packages/angular/build/src/builders/dev-server/internal.ts b/packages/angular/build/src/builders/dev-server/internal.ts index a0a6f2de57b4..4e5e97e5cf14 100644 --- a/packages/angular/build/src/builders/dev-server/internal.ts +++ b/packages/angular/build/src/builders/dev-server/internal.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -export { type BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context'; +export { type BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-files'; export { createRxjsEsmResolutionPlugin } from '../../tools/esbuild/rxjs-esm-resolution-plugin'; export { JavaScriptTransformer } from '../../tools/esbuild/javascript-transformer'; export { getFeatureSupport, isZonelessApp } from '../../tools/esbuild/utils'; diff --git a/packages/angular/build/src/builders/karma/utils.ts b/packages/angular/build/src/builders/karma/utils.ts index 6ce8e33ed9aa..9a8aa749c269 100644 --- a/packages/angular/build/src/builders/karma/utils.ts +++ b/packages/angular/build/src/builders/karma/utils.ts @@ -8,7 +8,7 @@ import type { BuilderContext } from '@angular-devkit/architect'; import { createRequire } from 'node:module'; -import { BuildOutputFileType } from '../../tools/esbuild/bundler-context'; +import { BuildOutputFileType } from '../../tools/esbuild/bundler-files'; import { getProjectRootPaths } from '../../utils/project-metadata'; import { findTests, getTestEntrypoints } from './find-tests'; import type { NormalizedKarmaBuilderOptions } from './options'; diff --git a/packages/angular/build/src/index.ts b/packages/angular/build/src/index.ts index 2831f59a38f4..b6a547921db8 100644 --- a/packages/angular/build/src/index.ts +++ b/packages/angular/build/src/index.ts @@ -8,7 +8,7 @@ export { buildApplication, type ApplicationBuilderOptions } from './builders/application'; export type { ApplicationBuilderExtensions } from './builders/application/options'; -export { type BuildOutputFile, BuildOutputFileType } from './tools/esbuild/bundler-context'; +export { type BuildOutputFile, BuildOutputFileType } from './tools/esbuild/bundler-files'; export type { BuildOutputAsset } from './tools/esbuild/bundler-execution-result'; export { diff --git a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts index 3b8d12ec1461..79008d140729 100644 --- a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts +++ b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts @@ -9,12 +9,8 @@ import assert from 'node:assert'; import { createHash } from 'node:crypto'; import path from 'node:path'; -import { - BuildOutputFile, - BuildOutputFileType, - BundleContextResult, - BundlerContext, -} from '../bundler-context'; +import { BundleContextResult, BundlerContext } from '../bundler-context'; +import { type BuildOutputFile, BuildOutputFileType } from '../bundler-files'; import { MemoryCache } from '../cache'; import { BundleStylesheetOptions, diff --git a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts index d11e1b6fb63c..7333843d7196 100644 --- a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts +++ b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts @@ -17,6 +17,7 @@ import { toPosixPath } from '../../utils/path'; import { SERVER_APP_ENGINE_MANIFEST_FILENAME, SERVER_APP_MANIFEST_FILENAME, + SERVER_GENERATED_EXTERNALS, } from '../../utils/server-rendering/manifest'; import { AngularCompilation, NoopCompilation } from '../angular/compilation'; import { createCompilerPlugin } from './angular/compiler-plugin'; @@ -32,7 +33,7 @@ import { createLoaderImportAttributePlugin } from './loader-import-attribute-plu import { createRxjsEsmResolutionPlugin } from './rxjs-esm-resolution-plugin'; import { createServerBundleMetadata } from './server-bundle-metadata-plugin'; import { createSourcemapIgnorelistPlugin } from './sourcemap-ignorelist-plugin'; -import { SERVER_GENERATED_EXTERNALS, getFeatureSupport, isZonelessApp } from './utils'; +import { getFeatureSupport, isZonelessApp } from './utils'; import { createVirtualModulePlugin } from './virtual-module-plugin'; import { createWasmPlugin } from './wasm-plugin'; diff --git a/packages/angular/build/src/tools/esbuild/budget-stats.ts b/packages/angular/build/src/tools/esbuild/budget-stats.ts index ee9b6b3b6dde..a9c32778b3db 100644 --- a/packages/angular/build/src/tools/esbuild/budget-stats.ts +++ b/packages/angular/build/src/tools/esbuild/budget-stats.ts @@ -8,11 +8,7 @@ import type { Metafile } from 'esbuild'; import type { BudgetStats } from '../../utils/bundle-calculator'; -import { - type BuildOutputFile, - BuildOutputFileType, - type InitialFileRecord, -} from './bundler-context'; +import { type BuildOutputFile, BuildOutputFileType, type InitialFileRecord } from './bundler-files'; import { getChunkNameFromMetafile } from './utils'; /** diff --git a/packages/angular/build/src/tools/esbuild/bundler-context.ts b/packages/angular/build/src/tools/esbuild/bundler-context.ts index 864ca2c6fdd9..308e3509acdb 100644 --- a/packages/angular/build/src/tools/esbuild/bundler-context.ts +++ b/packages/angular/build/src/tools/esbuild/bundler-context.ts @@ -20,8 +20,14 @@ import { import assert from 'node:assert'; import { builtinModules } from 'node:module'; import { basename, extname, join, relative } from 'node:path'; +import { SERVER_GENERATED_EXTERNALS } from '../../utils/server-rendering/manifest'; +import { + type BuildOutputFile, + BuildOutputFileType, + type InitialFileRecord, + convertOutputFile, +} from './bundler-files'; import { LoadResultCache, MemoryLoadResultCache } from './load-result-cache'; -import { SERVER_GENERATED_EXTERNALS, convertOutputFile } from './utils'; export type BundleContextResult = | { errors: Message[]; warnings: Message[] } @@ -38,29 +44,6 @@ export type BundleContextResult = externalConfiguration?: string[]; }; -export interface InitialFileRecord { - entrypoint: boolean; - name?: string; - type: 'script' | 'style'; - external?: boolean; - serverFile: boolean; - depth: number; -} - -export enum BuildOutputFileType { - Browser, - Media, - ServerApplication, - ServerRoot, - Root, -} - -export interface BuildOutputFile extends OutputFile { - type: BuildOutputFileType; - readonly size: number; - clone: () => BuildOutputFile; -} - export type BundlerOptionsFactory = ( loadCache: LoadResultCache | undefined, ) => T; diff --git a/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts b/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts index 1ba176287450..bebfd6a3eedd 100644 --- a/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts +++ b/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts @@ -11,8 +11,8 @@ import { normalize } from 'node:path'; import type { ChangedFiles } from '../../tools/esbuild/watcher'; import type { ComponentStylesheetBundler } from './angular/component-stylesheets'; import type { SourceFileCache } from './angular/source-file-cache'; -import type { BuildOutputFile, BuildOutputFileType, BundlerContext } from './bundler-context'; -import { createOutputFile } from './utils'; +import type { BundlerContext } from './bundler-context'; +import { type BuildOutputFile, BuildOutputFileType, createOutputFile } from './bundler-files'; export interface BuildOutputAsset { source: string; diff --git a/packages/angular/build/src/tools/esbuild/bundler-files.ts b/packages/angular/build/src/tools/esbuild/bundler-files.ts new file mode 100644 index 000000000000..ac33d471a395 --- /dev/null +++ b/packages/angular/build/src/tools/esbuild/bundler-files.ts @@ -0,0 +1,138 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import type { OutputFile } from 'esbuild'; +import { createHash } from 'node:crypto'; + +export interface InitialFileRecord { + entrypoint: boolean; + name?: string; + type: 'script' | 'style'; + external?: boolean; + serverFile: boolean; + depth: number; +} + +export enum BuildOutputFileType { + Browser, + Media, + ServerApplication, + ServerRoot, + Root, +} + +export interface BuildOutputFile extends OutputFile { + type: BuildOutputFileType; + readonly size: number; + clone: () => BuildOutputFile; +} + +export function createOutputFile( + path: string, + data: string | Uint8Array, + type: BuildOutputFileType, +): BuildOutputFile { + if (typeof data === 'string') { + let cachedContents: Uint8Array | null = null; + let cachedText: string | null = data; + let cachedHash: string | null = null; + + return { + path, + type, + get contents(): Uint8Array { + cachedContents ??= new TextEncoder().encode(data); + + return cachedContents; + }, + set contents(value: Uint8Array) { + cachedContents = value; + cachedText = null; + }, + get text(): string { + cachedText ??= new TextDecoder('utf-8').decode(this.contents); + + return cachedText; + }, + get size(): number { + return this.contents.byteLength; + }, + get hash(): string { + cachedHash ??= createHash('sha256') + .update(cachedText ?? this.contents) + .digest('hex'); + + return cachedHash; + }, + clone(): BuildOutputFile { + return createOutputFile(this.path, cachedText ?? this.contents, this.type); + }, + }; + } else { + let cachedContents = data; + let cachedText: string | null = null; + let cachedHash: string | null = null; + + return { + get contents(): Uint8Array { + return cachedContents; + }, + set contents(value: Uint8Array) { + cachedContents = value; + cachedText = null; + }, + path, + type, + get size(): number { + return this.contents.byteLength; + }, + get text(): string { + cachedText ??= new TextDecoder('utf-8').decode(this.contents); + + return cachedText; + }, + get hash(): string { + cachedHash ??= createHash('sha256').update(this.contents).digest('hex'); + + return cachedHash; + }, + clone(): BuildOutputFile { + return createOutputFile(this.path, this.contents, this.type); + }, + }; + } +} + +export function convertOutputFile(file: OutputFile, type: BuildOutputFileType): BuildOutputFile { + let { contents: cachedContents } = file; + let cachedText: string | null = null; + + return { + get contents(): Uint8Array { + return cachedContents; + }, + set contents(value: Uint8Array) { + cachedContents = value; + cachedText = null; + }, + hash: file.hash, + path: file.path, + type, + get size(): number { + return this.contents.byteLength; + }, + get text(): string { + cachedText ??= new TextDecoder('utf-8').decode(this.contents); + + return cachedText; + }, + clone(): BuildOutputFile { + return convertOutputFile(this, this.type); + }, + }; +} diff --git a/packages/angular/build/src/tools/esbuild/i18n-inliner.ts b/packages/angular/build/src/tools/esbuild/i18n-inliner.ts index fcb439b84c5c..9e17714df807 100644 --- a/packages/angular/build/src/tools/esbuild/i18n-inliner.ts +++ b/packages/angular/build/src/tools/esbuild/i18n-inliner.ts @@ -10,9 +10,8 @@ import assert from 'node:assert'; import { createHash } from 'node:crypto'; import { extname, join } from 'node:path'; import { WorkerPool } from '../../utils/worker-pool'; -import { BuildOutputFile, BuildOutputFileType } from './bundler-context'; +import { type BuildOutputFile, BuildOutputFileType, createOutputFile } from './bundler-files'; import type { LmdbCacheStore } from './lmdb-cache-store'; -import { createOutputFile } from './utils'; /** * A keyword used to indicate if a JavaScript file may require inlining of translations. diff --git a/packages/angular/build/src/tools/esbuild/index-html-generator.ts b/packages/angular/build/src/tools/esbuild/index-html-generator.ts index 4ccd48f2b595..2fcf56c6aacb 100644 --- a/packages/angular/build/src/tools/esbuild/index-html-generator.ts +++ b/packages/angular/build/src/tools/esbuild/index-html-generator.ts @@ -10,7 +10,7 @@ import assert from 'node:assert'; import path from 'node:path'; import { NormalizedApplicationBuildOptions } from '../../builders/application/options'; import { IndexHtmlGenerator } from '../../utils/index-file/index-html-generator'; -import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-context'; +import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-files'; /** * The maximum number of module preload link elements that should be added for diff --git a/packages/angular/build/src/tools/esbuild/utils.ts b/packages/angular/build/src/tools/esbuild/utils.ts index 2730dafae97c..51eabbbfb84f 100644 --- a/packages/angular/build/src/tools/esbuild/utils.ts +++ b/packages/angular/build/src/tools/esbuild/utils.ts @@ -17,17 +17,14 @@ import { coerce } from 'semver'; import { NormalizedApplicationBuildOptions } from '../../builders/application/options'; import { OutputMode } from '../../builders/application/schema'; import { BudgetCalculatorResult } from '../../utils/bundle-calculator'; -import { - SERVER_APP_ENGINE_MANIFEST_FILENAME, - SERVER_APP_MANIFEST_FILENAME, -} from '../../utils/server-rendering/manifest'; + import { BundleStats, generateEsbuildBuildStatsTable } from '../../utils/stats-table'; -import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-context'; import { BuildOutputAsset, ExecutionResult, PrerenderedRoutesRecord, } from './bundler-execution-result'; +import { type BuildOutputFile, BuildOutputFileType, type InitialFileRecord } from './bundler-files'; export function logBuildStats( metafile: Metafile, @@ -233,111 +230,6 @@ export async function emitFilesToDisk( } } -export function createOutputFile( - path: string, - data: string | Uint8Array, - type: BuildOutputFileType, -): BuildOutputFile { - if (typeof data === 'string') { - let cachedContents: Uint8Array | null = null; - let cachedText: string | null = data; - let cachedHash: string | null = null; - - return { - path, - type, - get contents(): Uint8Array { - cachedContents ??= new TextEncoder().encode(data); - - return cachedContents; - }, - set contents(value: Uint8Array) { - cachedContents = value; - cachedText = null; - }, - get text(): string { - cachedText ??= new TextDecoder('utf-8').decode(this.contents); - - return cachedText; - }, - get size(): number { - return this.contents.byteLength; - }, - get hash(): string { - cachedHash ??= createHash('sha256') - .update(cachedText ?? this.contents) - .digest('hex'); - - return cachedHash; - }, - clone(): BuildOutputFile { - return createOutputFile(this.path, cachedText ?? this.contents, this.type); - }, - }; - } else { - let cachedContents = data; - let cachedText: string | null = null; - let cachedHash: string | null = null; - - return { - get contents(): Uint8Array { - return cachedContents; - }, - set contents(value: Uint8Array) { - cachedContents = value; - cachedText = null; - }, - path, - type, - get size(): number { - return this.contents.byteLength; - }, - get text(): string { - cachedText ??= new TextDecoder('utf-8').decode(this.contents); - - return cachedText; - }, - get hash(): string { - cachedHash ??= createHash('sha256').update(this.contents).digest('hex'); - - return cachedHash; - }, - clone(): BuildOutputFile { - return createOutputFile(this.path, this.contents, this.type); - }, - }; - } -} - -export function convertOutputFile(file: OutputFile, type: BuildOutputFileType): BuildOutputFile { - let { contents: cachedContents } = file; - let cachedText: string | null = null; - - return { - get contents(): Uint8Array { - return cachedContents; - }, - set contents(value: Uint8Array) { - cachedContents = value; - cachedText = null; - }, - hash: file.hash, - path: file.path, - type, - get size(): number { - return this.contents.byteLength; - }, - get text(): string { - cachedText ??= new TextDecoder('utf-8').decode(this.contents); - - return cachedText; - }, - clone(): BuildOutputFile { - return convertOutputFile(this, this.type); - }, - }; -} - /** * Transform browserlists result to esbuild target. * @see https://esbuild.github.io/api/#target @@ -486,17 +378,3 @@ export function getEntryPointName(entryPoint: string): string { .replace(/\.[cm]?[jt]s$/, '') .replace(/[\\/.]/g, '-'); } - -/** - * A set of server-generated dependencies that are treated as external. - * - * These dependencies are marked as external because they are produced by a - * separate bundling process and are not included in the primary bundle. This - * ensures that these generated files are resolved from an external source rather - * than being part of the main bundle. - */ -export const SERVER_GENERATED_EXTERNALS = new Set([ - './polyfills.server.mjs', - './' + SERVER_APP_MANIFEST_FILENAME, - './' + SERVER_APP_ENGINE_MANIFEST_FILENAME, -]); diff --git a/packages/angular/build/src/utils/server-rendering/manifest.ts b/packages/angular/build/src/utils/server-rendering/manifest.ts index 41e40d3d1dd5..bd294b1e7e4b 100644 --- a/packages/angular/build/src/utils/server-rendering/manifest.ts +++ b/packages/angular/build/src/utils/server-rendering/manifest.ts @@ -10,12 +10,29 @@ import type { Metafile } from 'esbuild'; import { extname } from 'node:path'; import { runInThisContext } from 'node:vm'; import { NormalizedApplicationBuildOptions } from '../../builders/application/options'; -import { type BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context'; -import { createOutputFile } from '../../tools/esbuild/utils'; +import { + type BuildOutputFile, + BuildOutputFileType, + createOutputFile, +} from '../../tools/esbuild/bundler-files'; export const SERVER_APP_MANIFEST_FILENAME = 'angular-app-manifest.mjs'; export const SERVER_APP_ENGINE_MANIFEST_FILENAME = 'angular-app-engine-manifest.mjs'; +/** + * A set of server-generated dependencies that are treated as external. + * + * These dependencies are marked as external because they are produced by a + * separate bundling process and are not included in the primary bundle. This + * ensures that these generated files are resolved from an external source rather + * than being part of the main bundle. + */ +export const SERVER_GENERATED_EXTERNALS = new Set([ + './polyfills.server.mjs', + './' + SERVER_APP_MANIFEST_FILENAME, + './' + SERVER_APP_ENGINE_MANIFEST_FILENAME, +]); + interface FilesMapping { path: string; dynamicImport: boolean; diff --git a/packages/angular/build/src/utils/server-rendering/prerender.ts b/packages/angular/build/src/utils/server-rendering/prerender.ts index 1033a7575f88..b4f81f03a800 100644 --- a/packages/angular/build/src/utils/server-rendering/prerender.ts +++ b/packages/angular/build/src/utils/server-rendering/prerender.ts @@ -10,8 +10,8 @@ import { readFile } from 'node:fs/promises'; import { extname, posix } from 'node:path'; import { NormalizedApplicationBuildOptions } from '../../builders/application/options'; import { OutputMode } from '../../builders/application/schema'; -import { BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context'; import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result'; +import { BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-files'; import { assertIsError } from '../error'; import { toPosixPath } from '../path'; import { addLeadingSlash, addTrailingSlash, joinUrlParts, stripLeadingSlash } from '../url'; diff --git a/packages/angular/build/src/utils/service-worker.ts b/packages/angular/build/src/utils/service-worker.ts index 3c8b4cbe6b63..ab8f058003db 100644 --- a/packages/angular/build/src/utils/service-worker.ts +++ b/packages/angular/build/src/utils/service-worker.ts @@ -12,8 +12,8 @@ import type { Config, Filesystem } from '@angular/service-worker/config' with { import * as crypto from 'node:crypto'; import { existsSync, promises as fsPromises } from 'node:fs'; import * as path from 'node:path'; -import { BuildOutputFile, BuildOutputFileType } from '../tools/esbuild/bundler-context'; import { BuildOutputAsset } from '../tools/esbuild/bundler-execution-result'; +import { BuildOutputFile, BuildOutputFileType } from '../tools/esbuild/bundler-files'; import { assertIsError } from './error'; import { toPosixPath } from './path'; diff --git a/packages/angular/build/src/utils/test-files.ts b/packages/angular/build/src/utils/test-files.ts index 522bb1e778c0..d5f0edb2815d 100644 --- a/packages/angular/build/src/utils/test-files.ts +++ b/packages/angular/build/src/utils/test-files.ts @@ -9,7 +9,7 @@ import * as fs from 'node:fs/promises'; import path from 'node:path'; import { ResultFile } from '../builders/application/results'; -import { BuildOutputFileType } from '../tools/esbuild/bundler-context'; +import { BuildOutputFileType } from '../tools/esbuild/bundler-files'; import { emitFilesToDisk } from '../tools/esbuild/utils'; /**