From b853d0e2d60e4f59a782ca4e9d4149997940053b Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 8 Jan 2024 12:48:44 -0500 Subject: [PATCH] refactor(@angular-devkit/build-angular): add experimental builder selector extension for dev-server When using the experimental programmatic API for the development server, the choice of builder used to execute the actual underlying build can now be chosen via a selector function extension option. The returned string value must be one of the first-party esbuild-based builders for the Vite-based development server to be used. All other returned values will cause the Webpack-based development server to be used. The Vite-based development server currently requires one of those builders to be used (either `@angular-devkit/build-angular:application` or `@angular-devkit/build-angular:browser-esbuild`). (cherry picked from commit 18a11f503cab3f22f8be7b10aab5d5ef3bcc7194) --- .../angular_devkit/build_angular/index.md | 1 + .../src/builders/dev-server/builder.ts | 74 +++++++++++++------ 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/goldens/public-api/angular_devkit/build_angular/index.md b/goldens/public-api/angular_devkit/build_angular/index.md index e8661be2e0d1..371239bf1584 100644 --- a/goldens/public-api/angular_devkit/build_angular/index.md +++ b/goldens/public-api/angular_devkit/build_angular/index.md @@ -212,6 +212,7 @@ export function executeDevServerBuilder(options: DevServerBuilderOptions, contex }, extensions?: { buildPlugins?: Plugin_2[]; middleware?: ((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: unknown) => void) => void)[]; + builderSelector?: (info: BuilderSelectorInfo, logger: BuilderContext['logger']) => string; }): Observable; // @public diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts index e3af10c6e791..f575c1ffead9 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts @@ -48,6 +48,7 @@ export function execute( res: http.ServerResponse, next: (err?: unknown) => void, ) => void)[]; + builderSelector?: (info: BuilderSelectorInfo, logger: BuilderContext['logger']) => string; }, ): Observable { // Determine project name from builder context target @@ -58,28 +59,16 @@ export function execute( return EMPTY; } - return defer(() => initialize(options, projectName, context)).pipe( + return defer(() => initialize(options, projectName, context, extensions?.builderSelector)).pipe( switchMap(({ builderName, normalizedOptions }) => { // Use vite-based development server for esbuild-based builds - if ( - builderName === '@angular-devkit/build-angular:application' || - builderName === '@angular-devkit/build-angular:browser-esbuild' || - normalizedOptions.forceEsbuild - ) { + if (isEsbuildBased(builderName)) { if (transforms?.logging || transforms?.webpackConfiguration) { throw new Error( 'The `application` and `browser-esbuild` builders do not support Webpack transforms.', ); } - if ( - normalizedOptions.forceEsbuild && - builderName === '@angular-devkit/build-angular:browser' - ) { - // The compatibility builder should be used if esbuild is force enabled with the official Webpack-based builder. - builderName = '@angular-devkit/build-angular:browser-esbuild'; - } - return defer(() => import('./vite-server')).pipe( switchMap(({ serveWithVite }) => serveWithVite(normalizedOptions, builderName, context, transforms, extensions), @@ -110,6 +99,7 @@ async function initialize( initialOptions: DevServerBuilderOptions, projectName: string, context: BuilderContext, + builderSelector = defaultBuilderSelector, ) { // Purge old build disk cache. await purgeStaleBuildCache(context); @@ -140,14 +130,56 @@ case. ); } - if (normalizedOptions.forceEsbuild && !builderName.startsWith('@angular-devkit/build-angular:')) { - context.logger.warn( - 'Warning: Forcing the use of the esbuild-based build system with third-party builders' + - ' may cause unexpected behavior and/or build failures.', - ); + normalizedOptions.port = await checkPort(normalizedOptions.port, normalizedOptions.host); + + return { + builderName: builderSelector( + { builderName, forceEsbuild: !!normalizedOptions.forceEsbuild }, + context.logger, + ), + normalizedOptions, + }; +} + +function isEsbuildBased( + builderName: string, +): builderName is + | '@angular-devkit/build-angular:application' + | '@angular-devkit/build-angular:browser-esbuild' { + if ( + builderName === '@angular-devkit/build-angular:application' || + builderName === '@angular-devkit/build-angular:browser-esbuild' + ) { + return true; } - normalizedOptions.port = await checkPort(normalizedOptions.port, normalizedOptions.host); + return false; +} + +interface BuilderSelectorInfo { + builderName: string; + forceEsbuild: boolean; +} + +function defaultBuilderSelector( + info: BuilderSelectorInfo, + logger: BuilderContext['logger'], +): string { + if (isEsbuildBased(info.builderName)) { + return info.builderName; + } + + if (info.forceEsbuild) { + if (!info.builderName.startsWith('@angular-devkit/build-angular:')) { + logger.warn( + 'Warning: Forcing the use of the esbuild-based build system with third-party builders' + + ' may cause unexpected behavior and/or build failures.', + ); + } + + // The compatibility builder should be used if esbuild is force enabled. + return '@angular-devkit/build-angular:browser-esbuild'; + } - return { builderName, normalizedOptions }; + return info.builderName; }