diff --git a/docs/generated/packages/angular/executors/dev-server.json b/docs/generated/packages/angular/executors/dev-server.json index 8583a356e756f..f3178390d9767 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 4e8a9ae472327..7361c3db07843 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 8e9f7adada233..fdd7409e993a7 100644 --- a/packages/angular/src/builders/dev-server/dev-server.impl.ts +++ b/packages/angular/src/builders/dev-server/dev-server.impl.ts @@ -10,8 +10,8 @@ import { normalizePath, parseTargetString, readCachedProjectGraph, + stripIndents, type Target, - targetToTargetString, } from '@nx/devkit'; import { getRootTsConfigPath } from '@nx/js'; import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils'; @@ -24,6 +24,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,12 +46,22 @@ type BuildTargetOptions = { customWebpackConfig?: { path?: string }; indexFileTransformer?: string; plugins?: string[] | PluginSpec[]; + esbuildMiddleware?: string[]; }; export function executeDevServerBuilder( rawOptions: Schema, context: import('@angular-devkit/architect').BuilderContext ) { + if (rawOptions.esbuildMiddleware) { + const { major: angularMajorVersion, version: angularVersion } = + getInstalledAngularVersionInfo(); + if (angularMajorVersion < 17) { + throw new Error(stripIndents`The "esbuildMiddleware" option is only supported in Angular >= 17.0.0. You are currently using "${angularVersion}". + You can resolve this error by removing the "esbuildMiddleware" option or by migrating to Angular 17.0.0.`); + } + } + process.env.NX_TSCONFIG_PATH = getRootTsConfigPath(); const options = normalizeOptions(rawOptions); @@ -164,8 +175,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 +231,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 88e50eda311f2..6ca097e205a3f 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 f4281250a2279..ab5142d7e511e 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 8127c94b1014a..b598e9f8471f0 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(); + } +}