From c534c8ab11de9391fc747946800d079aebb1cfef Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 10 Dec 2025 18:43:55 +0100 Subject: [PATCH 1/4] fix(native-federation): Added promise to wait for dispose function --- .../src/utils/angular-esbuild-adapter.ts | 13 ++++-- .../utils/create-awaitable-compiler-plugin.ts | 42 +++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts diff --git a/libs/native-federation/src/utils/angular-esbuild-adapter.ts b/libs/native-federation/src/utils/angular-esbuild-adapter.ts index ffed4544..8e9ae93d 100644 --- a/libs/native-federation/src/utils/angular-esbuild-adapter.ts +++ b/libs/native-federation/src/utils/angular-esbuild-adapter.ts @@ -42,6 +42,7 @@ import { RebuildEvents, RebuildHubs } from './rebuild-events'; import JSON5 from 'json5'; import { isDeepStrictEqual } from 'node:util'; +import { createAwaitableCompilerPlugin } from './create-awaitable-compiler-plugin'; export type MemResultHandler = ( outfiles: esbuild.OutputFile[], @@ -270,6 +271,11 @@ async function runEsbuild( pluginOptions.styleOptions.externalDependencies = []; + const [compilerPlugin, pluginDisposed] = createAwaitableCompilerPlugin( + pluginOptions.pluginOptions, + pluginOptions.styleOptions, + ); + const config: esbuild.BuildOptions = { entryPoints: entryPoints.map((ep) => ({ in: ep.fileName, @@ -294,10 +300,7 @@ async function runEsbuild( target: target, logLimit: kind === 'shared-package' ? 1 : 0, plugins: (plugins as any) || [ - createCompilerPlugin( - pluginOptions.pluginOptions, - pluginOptions.styleOptions, - ), + compilerPlugin, ...(mappedPaths && mappedPaths.length > 0 ? [createSharedMappingsPlugin(mappedPaths)] : []), @@ -317,6 +320,7 @@ async function runEsbuild( const abortHandler = async () => { await ctx.cancel(); await ctx.dispose(); + await pluginDisposed; }; if (signal) { @@ -342,6 +346,7 @@ async function runEsbuild( } else { if (signal) signal.removeEventListener('abort', abortHandler); await ctx.dispose(); + await pluginDisposed; } return writtenFiles; } catch (error) { diff --git a/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts b/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts new file mode 100644 index 00000000..8baf87be --- /dev/null +++ b/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts @@ -0,0 +1,42 @@ +import * as esbuild from 'esbuild'; +import { createCompilerPlugin } from '@angular/build/private'; + +export function createAwaitableCompilerPlugin( + pluginOptions: any, + styleOptions: any, +): [esbuild.Plugin, Promise] { + const originalPlugin = createCompilerPlugin(pluginOptions, styleOptions); + + let resolveDispose: () => void; + const onDisposePromise = new Promise((resolve) => { + resolveDispose = resolve; + }); + + const wrappedPlugin: esbuild.Plugin = { + ...originalPlugin, + setup(build: esbuild.PluginBuild) { + let onDisposeCallback: (() => void | Promise) | undefined; + + // Wrap the build object to intercept onDispose + const wrappedBuild = new Proxy(build, { + get(target, prop) { + if (prop === 'onDispose') { + return (callback: () => void | Promise) => { + onDisposeCallback = callback; + return target.onDispose(async () => { + await callback(); + resolveDispose(); + }); + }; + } + return target[prop as keyof esbuild.PluginBuild]; + }, + }); + + // Call original setup with wrapped build + return originalPlugin.setup(wrappedBuild); + }, + }; + + return [wrappedPlugin, onDisposePromise]; +} From b29efdf9d023061b589989736ab9330e9493ef11 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 10 Dec 2025 19:07:21 +0100 Subject: [PATCH 2/4] fix(native-federation): callback is now synchronous. --- .../src/utils/create-awaitable-compiler-plugin.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts b/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts index 8baf87be..fdb464f4 100644 --- a/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts +++ b/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts @@ -15,16 +15,13 @@ export function createAwaitableCompilerPlugin( const wrappedPlugin: esbuild.Plugin = { ...originalPlugin, setup(build: esbuild.PluginBuild) { - let onDisposeCallback: (() => void | Promise) | undefined; - // Wrap the build object to intercept onDispose const wrappedBuild = new Proxy(build, { get(target, prop) { if (prop === 'onDispose') { return (callback: () => void | Promise) => { - onDisposeCallback = callback; - return target.onDispose(async () => { - await callback(); + return target.onDispose(() => { + callback(); resolveDispose(); }); }; @@ -33,7 +30,6 @@ export function createAwaitableCompilerPlugin( }, }); - // Call original setup with wrapped build return originalPlugin.setup(wrappedBuild); }, }; From cbfddfac11cfc6c2fc50ec73653c60d06b3e3277 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Thu, 11 Dec 2025 08:38:00 +0100 Subject: [PATCH 3/4] fix: Bumped target to es2022 --- tsconfig.base.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 3cd628b7..cd242712 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -9,9 +9,9 @@ "experimentalDecorators": true, "esModuleInterop": true, "importHelpers": true, - "target": "es2015", + "target": "es2022", "module": "esnext", - "lib": ["es2017", "dom"], + "lib": ["es2022", "dom"], "skipLibCheck": true, "skipDefaultLibCheck": true, "baseUrl": ".", From 0b773bb0862eccd9853a6024393f57361659ad2b Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Thu, 11 Dec 2025 08:50:12 +0100 Subject: [PATCH 4/4] fix(native-federation): Added typing to wrapped plugin --- .../src/utils/create-awaitable-compiler-plugin.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts b/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts index fdb464f4..1b3e125b 100644 --- a/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts +++ b/libs/native-federation/src/utils/create-awaitable-compiler-plugin.ts @@ -1,9 +1,11 @@ import * as esbuild from 'esbuild'; import { createCompilerPlugin } from '@angular/build/private'; +type CreateCompilerPluginParams = Parameters; + export function createAwaitableCompilerPlugin( - pluginOptions: any, - styleOptions: any, + pluginOptions: CreateCompilerPluginParams[0], + styleOptions: CreateCompilerPluginParams[1], ): [esbuild.Plugin, Promise] { const originalPlugin = createCompilerPlugin(pluginOptions, styleOptions);