From a07e5ef67c50643fbd92fea5518d358be7f3cbc7 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 15 Jun 2026 17:23:42 +0200 Subject: [PATCH 1/3] feat: support for custom federationConfigFilePath --- .../angular/src/builders/build/builder.ts | 95 ++++++++++--------- .../angular/src/builders/build/schema.d.ts | 1 + .../angular/src/builders/build/schema.json | 5 + 3 files changed, 54 insertions(+), 47 deletions(-) diff --git a/packages/angular/src/builders/build/builder.ts b/packages/angular/src/builders/build/builder.ts index 7b951bc..ed616a6 100644 --- a/packages/angular/src/builders/build/builder.ts +++ b/packages/angular/src/builders/build/builder.ts @@ -70,30 +70,30 @@ process.stderr.write = function ( const createInternalAngularBuilder = (externals: string[]) => - ( - options: Parameters[0], - context: BuilderContext, - pluginsOrExtensions?: Plugin[] | Parameters[2] - ) => { - let extensions: Parameters[2]; - if (pluginsOrExtensions && Array.isArray(pluginsOrExtensions)) { - extensions = { - codePlugins: pluginsOrExtensions, - }; - } else { - extensions = pluginsOrExtensions as Parameters[2]; - } + ( + options: Parameters[0], + context: BuilderContext, + pluginsOrExtensions?: Plugin[] | Parameters[2] + ) => { + let extensions: Parameters[2]; + if (pluginsOrExtensions && Array.isArray(pluginsOrExtensions)) { + extensions = { + codePlugins: pluginsOrExtensions, + }; + } else { + extensions = pluginsOrExtensions as Parameters[2]; + } - // serveWithVite fetches its own browserOptions independently, so ngBuilderOptions - // modifications don't reach here. Add NF externals to externalDependencies so - // Angular routes them to optimizeDeps.exclude, preventing Vite from trying to - // pre-bundle packages that include native .node binaries. - options.externalDependencies = [...(options.externalDependencies ?? []), ...externals]; + // serveWithVite fetches its own browserOptions independently, so ngBuilderOptions + // modifications don't reach here. Add NF externals to externalDependencies so + // Angular routes them to optimizeDeps.exclude, preventing Vite from trying to + // pre-bundle packages that include native .node binaries. + options.externalDependencies = [...(options.externalDependencies ?? []), ...externals]; - // Todo: share cache with Angular builder: https://github.com/angular/angular-cli/pull/32527 - // options.codeBundleCache = nfOptions.federationCache.bundlerCache; - return buildApplicationInternal(options, context, extensions); - }; + // Todo: share cache with Angular builder: https://github.com/angular/angular-cli/pull/32527 + // options.codeBundleCache = nfOptions.federationCache.bundlerCache; + return buildApplicationInternal(options, context, extensions); + }; export async function* runBuilder( nfBuilderOptions: NfBuilderSchema & NfInternalOptions, @@ -138,9 +138,9 @@ export async function* runBuilder( let ngBuilderOptions = (await context.validateOptions( runViteServer ? ({ - ...targetOptions, - port: nfBuilderOptions.port || targetOptions['port'], - } as JsonObject) + ...targetOptions, + port: nfBuilderOptions.port || targetOptions['port'], + } as JsonObject) : targetOptions, builder )) as JsonObject & ApplicationBuilderOptions; @@ -236,7 +236,7 @@ export async function* runBuilder( projectName: nfBuilderOptions.projectName, workspaceRoot: context.workspaceRoot, outputPath: browserOutputPath, - federationConfig: inferConfigPath(federationTsConfig, context.workspaceRoot), + federationConfig: inferConfigPath(federationTsConfig, context.workspaceRoot, nfBuilderOptions.federationConfigFilePath), tsConfig: federationTsConfig, verbose: ngBuilderOptions.verbose, watch: ngBuilderOptions.watch, @@ -308,10 +308,10 @@ export async function* runBuilder( const middleware = [ ...(isLocalDevelopment ? [ - federationBuildNotifier.createEventMiddleware(req => - removeBaseHref(req, ngBuilderOptions.baseHref) - ), - ] + federationBuildNotifier.createEventMiddleware(req => + removeBaseHref(req, ngBuilderOptions.baseHref) + ), + ] : []), ( @@ -394,22 +394,22 @@ export async function* runBuilder( const builderRun = runViteServer ? serveWithVite( - serverOptions as unknown as Parameters[0], - appBuilderName, - createInternalAngularBuilder(externals), - context, - nfBuilderOptions.skipHtmlTransform - ? {} - : { indexHtml: transformIndexHtml(nfBuilderOptions) }, - { - buildPlugins: plugins, - middleware, - } - ) + serverOptions as unknown as Parameters[0], + appBuilderName, + createInternalAngularBuilder(externals), + context, + nfBuilderOptions.skipHtmlTransform + ? {} + : { indexHtml: transformIndexHtml(nfBuilderOptions) }, + { + buildPlugins: plugins, + middleware, + } + ) : buildApplication(ngBuilderOptions, context, { - codePlugins: plugins, - indexHtmlTransformer: transformIndexHtml(nfBuilderOptions), - }); + codePlugins: plugins, + indexHtmlTransformer: transformIndexHtml(nfBuilderOptions), + }); const rebuildQueue = new RebuildQueue(); @@ -575,9 +575,10 @@ function getLocaleFilter(options: ApplicationBuilderOptions, runViteServer: bool return localize; } -function inferConfigPath(tsConfig: string, workspaceRoot: string): string { +function inferConfigPath(tsConfig: string, workspaceRoot: string, federationConfigFilePath = 'federation.config.mjs'): string { const relProjectPath = path.dirname(tsConfig); - const mjsRelPath = path.join(relProjectPath, 'federation.config.mjs'); + + const mjsRelPath = path.join(relProjectPath, federationConfigFilePath); if (fs.existsSync(path.resolve(workspaceRoot, mjsRelPath))) { return mjsRelPath; diff --git a/packages/angular/src/builders/build/schema.d.ts b/packages/angular/src/builders/build/schema.d.ts index 9828e99..aa917f6 100644 --- a/packages/angular/src/builders/build/schema.d.ts +++ b/packages/angular/src/builders/build/schema.d.ts @@ -9,6 +9,7 @@ export interface NfBuilderSchema extends JsonObject { port: number; rebuildDelay: number; buildNotifications?: BuildNotificationOptions; + federationConfigFilePath?: string; watch?: boolean; skipHtmlTransform: boolean; esmsInitOptions: ESMSInitOptions; diff --git a/packages/angular/src/builders/build/schema.json b/packages/angular/src/builders/build/schema.json index ba352ff..2f2d9d7 100644 --- a/packages/angular/src/builders/build/schema.json +++ b/packages/angular/src/builders/build/schema.json @@ -83,6 +83,11 @@ "description": "You can override the default endpoint to send build completion events to." } } + }, + "federationConfigFilePath": { + "type": "string", + "description": "The path to the federation config file. If not provided, the default path will be used.", + "default": "federation.config.mjs" } } } From 1635000f6b808054063595b87a01bdfe83581296 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 15 Jun 2026 17:25:11 +0200 Subject: [PATCH 2/3] chore: remove whitespace changes for clarity --- .../angular/src/builders/build/builder.ts | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/angular/src/builders/build/builder.ts b/packages/angular/src/builders/build/builder.ts index ed616a6..d79b6be 100644 --- a/packages/angular/src/builders/build/builder.ts +++ b/packages/angular/src/builders/build/builder.ts @@ -70,30 +70,30 @@ process.stderr.write = function ( const createInternalAngularBuilder = (externals: string[]) => - ( - options: Parameters[0], - context: BuilderContext, - pluginsOrExtensions?: Plugin[] | Parameters[2] - ) => { - let extensions: Parameters[2]; - if (pluginsOrExtensions && Array.isArray(pluginsOrExtensions)) { - extensions = { - codePlugins: pluginsOrExtensions, - }; - } else { - extensions = pluginsOrExtensions as Parameters[2]; - } + ( + options: Parameters[0], + context: BuilderContext, + pluginsOrExtensions?: Plugin[] | Parameters[2] + ) => { + let extensions: Parameters[2]; + if (pluginsOrExtensions && Array.isArray(pluginsOrExtensions)) { + extensions = { + codePlugins: pluginsOrExtensions, + }; + } else { + extensions = pluginsOrExtensions as Parameters[2]; + } - // serveWithVite fetches its own browserOptions independently, so ngBuilderOptions - // modifications don't reach here. Add NF externals to externalDependencies so - // Angular routes them to optimizeDeps.exclude, preventing Vite from trying to - // pre-bundle packages that include native .node binaries. - options.externalDependencies = [...(options.externalDependencies ?? []), ...externals]; + // serveWithVite fetches its own browserOptions independently, so ngBuilderOptions + // modifications don't reach here. Add NF externals to externalDependencies so + // Angular routes them to optimizeDeps.exclude, preventing Vite from trying to + // pre-bundle packages that include native .node binaries. + options.externalDependencies = [...(options.externalDependencies ?? []), ...externals]; - // Todo: share cache with Angular builder: https://github.com/angular/angular-cli/pull/32527 - // options.codeBundleCache = nfOptions.federationCache.bundlerCache; - return buildApplicationInternal(options, context, extensions); - }; + // Todo: share cache with Angular builder: https://github.com/angular/angular-cli/pull/32527 + // options.codeBundleCache = nfOptions.federationCache.bundlerCache; + return buildApplicationInternal(options, context, extensions); + }; export async function* runBuilder( nfBuilderOptions: NfBuilderSchema & NfInternalOptions, @@ -138,9 +138,9 @@ export async function* runBuilder( let ngBuilderOptions = (await context.validateOptions( runViteServer ? ({ - ...targetOptions, - port: nfBuilderOptions.port || targetOptions['port'], - } as JsonObject) + ...targetOptions, + port: nfBuilderOptions.port || targetOptions['port'], + } as JsonObject) : targetOptions, builder )) as JsonObject & ApplicationBuilderOptions; @@ -308,10 +308,10 @@ export async function* runBuilder( const middleware = [ ...(isLocalDevelopment ? [ - federationBuildNotifier.createEventMiddleware(req => - removeBaseHref(req, ngBuilderOptions.baseHref) - ), - ] + federationBuildNotifier.createEventMiddleware(req => + removeBaseHref(req, ngBuilderOptions.baseHref) + ), + ] : []), ( @@ -394,22 +394,22 @@ export async function* runBuilder( const builderRun = runViteServer ? serveWithVite( - serverOptions as unknown as Parameters[0], - appBuilderName, - createInternalAngularBuilder(externals), - context, - nfBuilderOptions.skipHtmlTransform - ? {} - : { indexHtml: transformIndexHtml(nfBuilderOptions) }, - { - buildPlugins: plugins, - middleware, - } - ) + serverOptions as unknown as Parameters[0], + appBuilderName, + createInternalAngularBuilder(externals), + context, + nfBuilderOptions.skipHtmlTransform + ? {} + : { indexHtml: transformIndexHtml(nfBuilderOptions) }, + { + buildPlugins: plugins, + middleware, + } + ) : buildApplication(ngBuilderOptions, context, { - codePlugins: plugins, - indexHtmlTransformer: transformIndexHtml(nfBuilderOptions), - }); + codePlugins: plugins, + indexHtmlTransformer: transformIndexHtml(nfBuilderOptions), + }); const rebuildQueue = new RebuildQueue(); From 6eac55b595f73a1fb5fad6a458e5407b5af21646 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 15 Jun 2026 17:40:39 +0200 Subject: [PATCH 3/3] chore: centralizes federation config file names and renames the property --- packages/angular/src/builders/build/builder.ts | 17 +++++++++++++---- packages/angular/src/builders/build/schema.d.ts | 2 +- packages/angular/src/builders/build/schema.json | 2 +- packages/angular/src/config/constants.ts | 2 ++ .../angular/src/schematics/init/schematic.ts | 3 ++- .../src/schematics/update22/schematic.ts | 8 ++++++-- 6 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 packages/angular/src/config/constants.ts diff --git a/packages/angular/src/builders/build/builder.ts b/packages/angular/src/builders/build/builder.ts index d79b6be..076adf8 100644 --- a/packages/angular/src/builders/build/builder.ts +++ b/packages/angular/src/builders/build/builder.ts @@ -1,5 +1,10 @@ import './setup-builder-env-variables.js'; +import { + DEFAULT_NF_CONFIG_FILE_NAME, + LEGACY_NF_CONFIG_FILE_NAME, +} from '../../config/constants.js'; + import * as fs from 'fs'; import * as mrmime from 'mrmime'; import * as path from 'path'; @@ -236,7 +241,7 @@ export async function* runBuilder( projectName: nfBuilderOptions.projectName, workspaceRoot: context.workspaceRoot, outputPath: browserOutputPath, - federationConfig: inferConfigPath(federationTsConfig, context.workspaceRoot, nfBuilderOptions.federationConfigFilePath), + federationConfig: inferConfigPath(federationTsConfig, context.workspaceRoot, nfBuilderOptions.federationConfigPath), tsConfig: federationTsConfig, verbose: ngBuilderOptions.verbose, watch: ngBuilderOptions.watch, @@ -575,16 +580,20 @@ function getLocaleFilter(options: ApplicationBuilderOptions, runViteServer: bool return localize; } -function inferConfigPath(tsConfig: string, workspaceRoot: string, federationConfigFilePath = 'federation.config.mjs'): string { +function inferConfigPath( + tsConfig: string, + workspaceRoot: string, + federationConfigPath = DEFAULT_NF_CONFIG_FILE_NAME +): string { const relProjectPath = path.dirname(tsConfig); - const mjsRelPath = path.join(relProjectPath, federationConfigFilePath); + const mjsRelPath = path.join(relProjectPath, federationConfigPath); if (fs.existsSync(path.resolve(workspaceRoot, mjsRelPath))) { return mjsRelPath; } - return path.join(relProjectPath, 'federation.config.js'); + return path.join(relProjectPath, LEGACY_NF_CONFIG_FILE_NAME); } function transformIndexHtml(nfOptions: NfBuilderSchema): (content: string) => Promise { diff --git a/packages/angular/src/builders/build/schema.d.ts b/packages/angular/src/builders/build/schema.d.ts index aa917f6..f2951c7 100644 --- a/packages/angular/src/builders/build/schema.d.ts +++ b/packages/angular/src/builders/build/schema.d.ts @@ -9,7 +9,7 @@ export interface NfBuilderSchema extends JsonObject { port: number; rebuildDelay: number; buildNotifications?: BuildNotificationOptions; - federationConfigFilePath?: string; + federationConfigPath?: string; watch?: boolean; skipHtmlTransform: boolean; esmsInitOptions: ESMSInitOptions; diff --git a/packages/angular/src/builders/build/schema.json b/packages/angular/src/builders/build/schema.json index 2f2d9d7..46d2b9a 100644 --- a/packages/angular/src/builders/build/schema.json +++ b/packages/angular/src/builders/build/schema.json @@ -84,7 +84,7 @@ } } }, - "federationConfigFilePath": { + "federationConfigPath": { "type": "string", "description": "The path to the federation config file. If not provided, the default path will be used.", "default": "federation.config.mjs" diff --git a/packages/angular/src/config/constants.ts b/packages/angular/src/config/constants.ts new file mode 100644 index 0000000..f310064 --- /dev/null +++ b/packages/angular/src/config/constants.ts @@ -0,0 +1,2 @@ +export const DEFAULT_NF_CONFIG_FILE_NAME = 'federation.config.mjs'; +export const LEGACY_NF_CONFIG_FILE_NAME = 'federation.config.js'; diff --git a/packages/angular/src/schematics/init/schematic.ts b/packages/angular/src/schematics/init/schematic.ts index 61946cc..5d9a5ff 100644 --- a/packages/angular/src/schematics/init/schematic.ts +++ b/packages/angular/src/schematics/init/schematic.ts @@ -1,4 +1,5 @@ import { chain, noop, type Rule, url } from '@angular-devkit/schematics'; +import { DEFAULT_NF_CONFIG_FILE_NAME } from '../../config/constants.js'; import type { NfSchematicSchema } from './schema.js'; import * as path from 'path'; @@ -46,7 +47,7 @@ export default function config(options: NfSchematicSchema): Rule { tree.create(manifestPath, JSON.stringify(remoteMap, null, '\t')); } - const federationConfigPath = path.join(projectRoot, 'federation.config.mjs'); + const federationConfigPath = path.join(projectRoot, DEFAULT_NF_CONFIG_FILE_NAME); const exists = tree.exists(federationConfigPath); diff --git a/packages/angular/src/schematics/update22/schematic.ts b/packages/angular/src/schematics/update22/schematic.ts index a297652..8c1d040 100644 --- a/packages/angular/src/schematics/update22/schematic.ts +++ b/packages/angular/src/schematics/update22/schematic.ts @@ -1,5 +1,9 @@ import type { Rule, Tree } from '@angular-devkit/schematics'; +import { + DEFAULT_NF_CONFIG_FILE_NAME, + LEGACY_NF_CONFIG_FILE_NAME, +} from '../../config/constants.js'; import { getWorkspaceFileName } from '../init/schematic.js'; import * as path from 'path'; @@ -60,8 +64,8 @@ function normalizeBuilderReferences(tree: Tree, workspace: any, workspaceFileNam // only get their package references normalized. function migrateFederationConfigs(tree: Tree, workspace: any): void { for (const { projectRoot } of resolveProjects(workspace)) { - const jsConfigPath = path.join(projectRoot, 'federation.config.js'); - const mjsConfigPath = path.join(projectRoot, 'federation.config.mjs'); + const jsConfigPath = path.join(projectRoot, LEGACY_NF_CONFIG_FILE_NAME); + const mjsConfigPath = path.join(projectRoot, DEFAULT_NF_CONFIG_FILE_NAME); if (!tree.exists(jsConfigPath) && tree.exists(mjsConfigPath)) { const content = tree.readText(mjsConfigPath);