From 661e6fc240cbbecbeb11c289ce53ca6c0c0c6b11 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Thu, 22 May 2025 12:41:34 -0400 Subject: [PATCH] perf(@angular/build): directly check code for Angular partial linking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To more completely avoid having to load any Angular compiler linking code when analyzing for code transformations during a build, the declaration function checks are now performed directly. This is in contrast to the previous behavior that required importing the linker code to use a helper function included in the `@angular/compiler-cli` package. Avoiding the need to load and parse a potentially large amount of code to perform the check can improve performance for the cases where no linking is required. To ensure the check stays up to date and while the list of function names rarely changes, the linker must always use the existing function name prefix (`ɵɵngDeclare`) for any declaration functions. --- .../esbuild/javascript-transformer-worker.ts | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts b/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts index 3d7c8d2ca126..8fa551b38ba2 100644 --- a/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts +++ b/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts @@ -27,6 +27,14 @@ interface JavaScriptTransformRequest { const textDecoder = new TextDecoder(); const textEncoder = new TextEncoder(); +/** + * The function name prefix for all Angular partial compilation functions. + * Used to determine if linking of a JavaScript file is required. + * If any additional declarations are added or otherwise changed in the linker, + * the names MUST begin with this prefix. + */ +const LINKER_DECLARATION_PREFIX = 'ɵɵngDeclare'; + export default async function transformJavaScript( request: JavaScriptTransformRequest, ): Promise { @@ -46,11 +54,6 @@ let linkerPluginCreator: | typeof import('@angular/compiler-cli/linker/babel').createEs2015LinkerPlugin | undefined; -/** - * Cached instance of the compiler-cli linker's needsLinking function. - */ -let needsLinking: typeof import('@angular/compiler-cli/linker').needsLinking | undefined; - async function transformWithBabel( filename: string, data: string, @@ -125,17 +128,10 @@ async function requiresLinking(path: string, source: string): Promise { return false; } - if (!needsLinking) { - // Load ESM `@angular/compiler-cli/linker` using the TypeScript dynamic import workaround. - // Once TypeScript provides support for keeping the dynamic import this workaround can be - // changed to a direct dynamic import. - const linkerModule = await loadEsmModule( - '@angular/compiler-cli/linker', - ); - needsLinking = linkerModule.needsLinking; - } - - return needsLinking(path, source); + // Check if the source code includes one of the declaration functions. + // There is a low chance of a false positive but the names are fairly unique + // and the result would be an unnecessary no-op additional plugin pass. + return source.includes(LINKER_DECLARATION_PREFIX); } async function createLinkerPlugin(options: Omit) {