diff --git a/docs/generated/packages/angular/executors/dev-server.json b/docs/generated/packages/angular/executors/dev-server.json index 8583a356e756f0..f3178390d9767a 100644 --- a/docs/generated/packages/angular/executors/dev-server.json +++ b/docs/generated/packages/angular/executors/dev-server.json @@ -109,6 +109,14 @@ "type": "boolean", "description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options.", "x-priority": "important" + }, + "esbuildMiddleware": { + "description": "A list of HTTP request middleware functions. _Note: this is only supported in Angular versions >= 17.0.0_.", + "type": "array", + "items": { + "type": "string", + "description": "The path to the middleware function. Relative to the workspace root." + } } }, "additionalProperties": false, diff --git a/packages/angular/.eslintrc.json b/packages/angular/.eslintrc.json index 4e8a9ae4723272..7361c3db078437 100644 --- a/packages/angular/.eslintrc.json +++ b/packages/angular/.eslintrc.json @@ -45,6 +45,7 @@ "ignoredDependencies": [ "nx", "eslint", + "vite", "rxjs", "semver", // These are installed by ensurePackage so missing in package.json diff --git a/packages/angular/src/builders/dev-server/dev-server.impl.ts b/packages/angular/src/builders/dev-server/dev-server.impl.ts index 8e9f7adada233e..9e2fa565817fef 100644 --- a/packages/angular/src/builders/dev-server/dev-server.impl.ts +++ b/packages/angular/src/builders/dev-server/dev-server.impl.ts @@ -11,7 +11,6 @@ import { parseTargetString, readCachedProjectGraph, type Target, - targetToTargetString, } from '@nx/devkit'; import { getRootTsConfigPath } from '@nx/js'; import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils'; @@ -24,6 +23,7 @@ import { combineLatest, from } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { getInstalledAngularVersionInfo } from '../../executors/utilities/angular-version-utils'; import { + loadMiddleware, loadPlugins, type PluginSpec, } from '../../executors/utilities/esbuild-extensions'; @@ -45,6 +45,7 @@ type BuildTargetOptions = { customWebpackConfig?: { path?: string }; indexFileTransformer?: string; plugins?: string[] | PluginSpec[]; + esbuildMiddleware?: string[]; }; export function executeDevServerBuilder( @@ -164,8 +165,11 @@ export function executeDevServerBuilder( return combineLatest([ from(import('@angular-devkit/build-angular')), from(loadPlugins(buildTargetOptions.plugins, buildTargetOptions.tsConfig)), + from( + loadMiddleware(options.esbuildMiddleware, buildTargetOptions.tsConfig) + ), ]).pipe( - switchMap(([{ executeDevServerBuilder }, plugins]) => + switchMap(([{ executeDevServerBuilder }, plugins, middleware]) => executeDevServerBuilder( delegateBuilderOptions, context, @@ -217,6 +221,7 @@ export function executeDevServerBuilder( }, { buildPlugins: plugins, + middleware, } ) ) diff --git a/packages/angular/src/builders/dev-server/schema.d.ts b/packages/angular/src/builders/dev-server/schema.d.ts index 88e50eda311f2f..6ca097e205a3fb 100644 --- a/packages/angular/src/builders/dev-server/schema.d.ts +++ b/packages/angular/src/builders/dev-server/schema.d.ts @@ -17,6 +17,7 @@ interface BaseSchema { watch?: boolean; poll?: number; buildLibsFromSource?: boolean; + esbuildMiddleware?: string[]; } export type SchemaWithBrowserTarget = BaseSchema & { diff --git a/packages/angular/src/builders/dev-server/schema.json b/packages/angular/src/builders/dev-server/schema.json index f4281250a2279b..ab5142d7e511ee 100644 --- a/packages/angular/src/builders/dev-server/schema.json +++ b/packages/angular/src/builders/dev-server/schema.json @@ -115,6 +115,14 @@ "type": "boolean", "description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options.", "x-priority": "important" + }, + "esbuildMiddleware": { + "description": "A list of HTTP request middleware functions. _Note: this is only supported in Angular versions >= 17.0.0_.", + "type": "array", + "items": { + "type": "string", + "description": "The path to the middleware function. Relative to the workspace root." + } } }, "additionalProperties": false, diff --git a/packages/angular/src/executors/utilities/esbuild-extensions.ts b/packages/angular/src/executors/utilities/esbuild-extensions.ts index 8127c94b1014ac..b598e9f8471f05 100644 --- a/packages/angular/src/executors/utilities/esbuild-extensions.ts +++ b/packages/angular/src/executors/utilities/esbuild-extensions.ts @@ -1,5 +1,6 @@ import { registerTsProject } from '@nx/js/src/internal'; import type { Plugin } from 'esbuild'; +import type { Connect } from 'vite'; import { loadModule } from './module-loader'; export type PluginSpec = { @@ -39,3 +40,19 @@ async function loadPlugin(pluginSpec: string | PluginSpec): Promise { return plugin; } + +export async function loadMiddleware( + middlewareFns: string[] | undefined, + tsConfig: string +): Promise { + if (!middlewareFns?.length) { + return []; + } + const cleanupTranspiler = registerTsProject(tsConfig); + + try { + return await Promise.all(middlewareFns.map((fnPath) => loadModule(fnPath))); + } finally { + cleanupTranspiler(); + } +}