Skip to content

Commit 66a33c6

Browse files
clydinangular-robot[bot]
authored andcommitted
refactor(@angular-devkit/build-angular): extract option processing for dev server builder
To provide support for additional development server integration, the `dev-server` builder's option processing has been reorganized into separate files. The main builder bootstrapping logic has also been separated into another file. This additionally helps reduce the overall size of the main Webpack-based development server file.
1 parent 0ac5f27 commit 66a33c6

File tree

6 files changed

+213
-75
lines changed

6 files changed

+213
-75
lines changed

goldens/public-api/angular_devkit/build_angular/index.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ export function executeBrowserBuilder(options: BrowserBuilderOptions, context: B
143143
}): Observable<BrowserBuilderOutput>;
144144

145145
// @public
146-
export function executeDevServerBuilder(options: DevServerBuilderOptions_2, context: BuilderContext, transforms?: {
147-
webpackConfiguration?: ExecutionTransformer<webpack.Configuration>;
146+
export function executeDevServerBuilder(options: DevServerBuilderOptions, context: BuilderContext, transforms?: {
147+
webpackConfiguration?: ExecutionTransformer<Configuration>;
148148
logging?: WebpackLoggingCallback;
149149
indexHtml?: IndexHtmlTransform;
150150
}): Observable<DevServerBuilderOutput>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
10+
import { EMPTY, Observable, defer, switchMap } from 'rxjs';
11+
import { ExecutionTransformer } from '../../transforms';
12+
import { checkPort } from '../../utils/check-port';
13+
import { IndexHtmlTransform } from '../../utils/index-file/index-html-generator';
14+
import { purgeStaleBuildCache } from '../../utils/purge-cache';
15+
import { normalizeOptions } from './options';
16+
import { Schema as DevServerBuilderOptions } from './schema';
17+
import { DevServerBuilderOutput, serveWebpackBrowser } from './webpack-server';
18+
19+
/**
20+
* A Builder that executes a development server based on the provided browser target option.
21+
* @param options Dev Server options.
22+
* @param context The build context.
23+
* @param transforms A map of transforms that can be used to hook into some logic (such as
24+
* transforming webpack configuration before passing it to webpack).
25+
*
26+
* @experimental Direct usage of this function is considered experimental.
27+
*/
28+
export function execute(
29+
options: DevServerBuilderOptions,
30+
context: BuilderContext,
31+
transforms: {
32+
webpackConfiguration?: ExecutionTransformer<import('webpack').Configuration>;
33+
logging?: import('@angular-devkit/build-webpack').WebpackLoggingCallback;
34+
indexHtml?: IndexHtmlTransform;
35+
} = {},
36+
): Observable<DevServerBuilderOutput> {
37+
// Determine project name from builder context target
38+
const projectName = context.target?.project;
39+
if (!projectName) {
40+
context.logger.error(`The 'dev-server' builder requires a target to be specified.`);
41+
42+
return EMPTY;
43+
}
44+
45+
return defer(() => initialize(options, projectName, context)).pipe(
46+
switchMap(({ builderName, normalizedOptions }) => {
47+
// Issue a warning that the dev-server does not currently support the experimental esbuild-
48+
// based builder and will use Webpack.
49+
if (builderName === '@angular-devkit/build-angular:browser-esbuild') {
50+
context.logger.warn(
51+
'WARNING: The experimental esbuild-based builder is not currently supported ' +
52+
'by the dev-server. The stable Webpack-based builder will be used instead.',
53+
);
54+
}
55+
56+
return serveWebpackBrowser(normalizedOptions, builderName, context, transforms);
57+
}),
58+
);
59+
}
60+
61+
async function initialize(
62+
initialOptions: DevServerBuilderOptions,
63+
projectName: string,
64+
context: BuilderContext,
65+
) {
66+
// Purge old build disk cache.
67+
await purgeStaleBuildCache(context);
68+
69+
const normalizedOptions = await normalizeOptions(context, projectName, initialOptions);
70+
const builderName = await context.getBuilderNameForTarget(normalizedOptions.browserTarget);
71+
72+
if (
73+
!normalizedOptions.disableHostCheck &&
74+
!/^127\.\d+\.\d+\.\d+/g.test(normalizedOptions.host) &&
75+
normalizedOptions.host !== 'localhost'
76+
) {
77+
context.logger.warn(`
78+
Warning: This is a simple server for use in testing or debugging Angular applications
79+
locally. It hasn't been reviewed for security issues.
80+
81+
Binding this server to an open connection can result in compromising your application or
82+
computer. Using a different host than the one passed to the "--host" flag might result in
83+
websocket connection issues. You might need to use "--disable-host-check" if that's the
84+
case.
85+
`);
86+
}
87+
88+
if (normalizedOptions.disableHostCheck) {
89+
context.logger.warn(
90+
'Warning: Running a server with --disable-host-check is a security risk. ' +
91+
'See https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a for more information.',
92+
);
93+
}
94+
95+
normalizedOptions.port = await checkPort(normalizedOptions.port, normalizedOptions.host);
96+
97+
return { builderName, normalizedOptions };
98+
}

packages/angular_devkit/build_angular/src/builders/dev-server/index.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77
*/
88

99
import { createBuilder } from '@angular-devkit/architect';
10+
import { execute } from './builder';
1011
import { Schema as DevServerBuilderOptions } from './schema';
11-
import { DevServerBuilderOutput, serveWebpackBrowser } from './webpack-server';
12+
import { DevServerBuilderOutput } from './webpack-server';
1213

13-
export { DevServerBuilderOptions, DevServerBuilderOutput, serveWebpackBrowser };
14-
export default createBuilder<DevServerBuilderOptions, DevServerBuilderOutput>(serveWebpackBrowser);
14+
export { DevServerBuilderOptions, DevServerBuilderOutput, execute as executeDevServerBuilder };
15+
export default createBuilder<DevServerBuilderOptions, DevServerBuilderOutput>(execute);
16+
17+
// Temporary export to support specs
18+
export { execute as serveWebpackBrowser };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { BuilderContext, targetFromTargetString } from '@angular-devkit/architect';
10+
import path from 'node:path';
11+
import { normalizeCacheOptions } from '../../utils/normalize-cache';
12+
import { Schema as DevServerOptions } from './schema';
13+
14+
export type NormalizedDevServerOptions = Awaited<ReturnType<typeof normalizeOptions>>;
15+
16+
/**
17+
* Normalize the user provided options by creating full paths for all path based options
18+
* and converting multi-form options into a single form that can be directly used
19+
* by the build process.
20+
*
21+
* @param context The context for current builder execution.
22+
* @param projectName The name of the project for the current execution.
23+
* @param options An object containing the options to use for the build.
24+
* @returns An object containing normalized options required to perform the build.
25+
*/
26+
export async function normalizeOptions(
27+
context: BuilderContext,
28+
projectName: string,
29+
options: DevServerOptions,
30+
) {
31+
const workspaceRoot = context.workspaceRoot;
32+
const projectMetadata = await context.getProjectMetadata(projectName);
33+
const projectRoot = path.join(workspaceRoot, (projectMetadata.root as string | undefined) ?? '');
34+
35+
const cacheOptions = normalizeCacheOptions(projectMetadata, workspaceRoot);
36+
37+
const browserTarget = targetFromTargetString(options.browserTarget);
38+
39+
// Initial options to keep
40+
const {
41+
host,
42+
port,
43+
poll,
44+
open,
45+
verbose,
46+
watch,
47+
allowedHosts,
48+
disableHostCheck,
49+
liveReload,
50+
hmr,
51+
headers,
52+
proxyConfig,
53+
servePath,
54+
publicHost,
55+
ssl,
56+
sslCert,
57+
sslKey,
58+
} = options;
59+
60+
// Return all the normalized options
61+
return {
62+
browserTarget,
63+
host: host ?? 'localhost',
64+
port: port ?? 4200,
65+
poll,
66+
open,
67+
verbose,
68+
watch,
69+
liveReload,
70+
hmr,
71+
headers,
72+
workspaceRoot,
73+
projectRoot,
74+
cacheOptions,
75+
allowedHosts,
76+
disableHostCheck,
77+
proxyConfig,
78+
servePath,
79+
publicHost,
80+
ssl,
81+
sslCert,
82+
sslKey,
83+
};
84+
}

0 commit comments

Comments
 (0)