refactor(@angular/build): remove Webpack-specific options from Vite-only dev-server

The `dev-server` build within the `@angular/build` package no longer uses
Webpack. As a result, the Webpack related options have been removed from
the builder schema. These options were not previously used by the Vite-based
development server with the exception of `browserTarget` which was removed
due to it being deprecated on the `@angular-devkit/build-angular` version.
This commit is contained in:
Charles Lyding 2024-04-24 10:31:46 -04:00 committed by Charles
parent 0a59eae5fd
commit c8b1fc045f
8 changed files with 6 additions and 348 deletions

View File

@ -110,12 +110,7 @@ export enum BuildOutputFileType {
// @public
export interface DevServerBuilderOptions {
allowedHosts?: string[];
// @deprecated
browserTarget?: string;
buildTarget?: string;
disableHostCheck?: boolean;
forceEsbuild?: boolean;
buildTarget: string;
headers?: {
[key: string]: string;
};
@ -127,7 +122,6 @@ export interface DevServerBuilderOptions {
port?: number;
prebundle?: PrebundleUnion;
proxyConfig?: string;
publicHost?: string;
servePath?: string;
ssl?: boolean;
sslCert?: string;

View File

@ -86,8 +86,8 @@ async function initialize(
const builderName = await context.getBuilderNameForTarget(normalizedOptions.buildTarget);
if (
!normalizedOptions.disableHostCheck &&
!/^127\.\d+\.\d+\.\d+/g.test(normalizedOptions.host) &&
normalizedOptions.host !== '::1' &&
normalizedOptions.host !== 'localhost'
) {
context.logger.warn(`
@ -96,18 +96,10 @@ locally. It hasn't been reviewed for security issues.
Binding this server to an open connection can result in compromising your application or
computer. Using a different host than the one passed to the "--host" flag might result in
websocket connection issues. You might need to use "--disable-host-check" if that's the
case.
websocket connection issues.
`);
}
if (normalizedOptions.disableHostCheck) {
context.logger.warn(
'Warning: Running a server with --disable-host-check is a security risk. ' +
'See https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a for more information.',
);
}
normalizedOptions.port = await checkPort(normalizedOptions.port, normalizedOptions.host);
return {

View File

@ -35,7 +35,7 @@ export async function normalizeOptions(
const cacheOptions = normalizeCacheOptions(projectMetadata, workspaceRoot);
// Target specifier defaults to the current project's build target using a development configuration
const buildTargetSpecifier = options.buildTarget ?? options.browserTarget ?? `::development`;
const buildTargetSpecifier = options.buildTarget ?? `::development`;
const buildTarget = targetFromTargetString(buildTargetSpecifier, projectName, 'build');
// Initial options to keep
@ -46,18 +46,14 @@ export async function normalizeOptions(
open,
verbose,
watch,
allowedHosts,
disableHostCheck,
liveReload,
hmr,
headers,
proxyConfig,
servePath,
publicHost,
ssl,
sslCert,
sslKey,
forceEsbuild,
prebundle,
} = options;
@ -76,15 +72,11 @@ export async function normalizeOptions(
workspaceRoot,
projectRoot,
cacheOptions,
allowedHosts,
disableHostCheck,
proxyConfig,
servePath,
publicHost,
ssl,
sslCert,
sslKey,
forceEsbuild,
// Prebundling defaults to true but requires caching to function
prebundle: cacheOptions.enabled && (prebundle ?? true),
};

View File

@ -4,12 +4,6 @@
"description": "Dev Server target options for Build Facade.",
"type": "object",
"properties": {
"browserTarget": {
"type": "string",
"description": "A browser builder target to serve in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$",
"x-deprecated": "Use 'buildTarget' instead."
},
"buildTarget": {
"type": "string",
"description": "A build builder target to serve in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.",
@ -67,27 +61,10 @@
"description": "Whether to reload the page on change, using live-reload.",
"default": true
},
"publicHost": {
"type": "string",
"description": "The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies. This option has no effect when using the 'application' or other esbuild-based builders."
},
"allowedHosts": {
"type": "array",
"description": "List of hosts that are allowed to access the dev server. This option has no effect when using the 'application' or other esbuild-based builders.",
"default": [],
"items": {
"type": "string"
}
},
"servePath": {
"type": "string",
"description": "The pathname where the application will be served."
},
"disableHostCheck": {
"type": "boolean",
"description": "Don't verify connected clients are part of allowed hosts. This option has no effect when using the 'application' or other esbuild-based builders.",
"default": false
},
"hmr": {
"type": "boolean",
"description": "Enable hot module replacement.",
@ -102,13 +79,8 @@
"type": "number",
"description": "Enable and define the file watching poll time period in milliseconds."
},
"forceEsbuild": {
"type": "boolean",
"description": "Force the development server to use the 'browser-esbuild' builder when building. This is a developer preview option for the esbuild-based build system.",
"default": false
},
"prebundle": {
"description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders.",
"description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled.",
"oneOf": [
{ "type": "boolean" },
{
@ -127,5 +99,5 @@
}
},
"additionalProperties": false,
"anyOf": [{ "required": ["buildTarget"] }, { "required": ["browserTarget"] }]
"required": ["buildTarget"]
}

View File

@ -1,71 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { executeDevServer } from '../../index';
import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
const FETCH_HEADERS = Object.freeze({ host: 'example.com' });
describeServeBuilder(
executeDevServer,
DEV_SERVER_BUILDER_INFO,
(harness, setupTarget, isViteRun) => {
// TODO(fix-vite): currently this is broken in vite.
(isViteRun ? xdescribe : xdescribe)('option: "allowedHosts"', () => {
beforeEach(async () => {
setupTarget(harness);
// Application code is not needed for these tests
await harness.writeFile('src/main.ts', '');
});
it('does not allow an invalid host when option is not present', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toBe('Invalid Host header');
});
it('does not allow an invalid host when option is an empty array', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
allowedHosts: [],
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toBe('Invalid Host header');
});
it('allows a host when specified in the option', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
allowedHosts: ['example.com'],
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toContain('<title>');
});
});
},
);

View File

@ -1,71 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { executeDevServer } from '../../index';
import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
const FETCH_HEADERS = Object.freeze({ host: 'example.com' });
describeServeBuilder(
executeDevServer,
DEV_SERVER_BUILDER_INFO,
(harness, setupTarget, isViteRun) => {
// This option is not used when using vite.
(isViteRun ? xdescribe : xdescribe)('option: "disableHostCheck"', () => {
beforeEach(async () => {
setupTarget(harness);
// Application code is not needed for these tests
await harness.writeFile('src/main.ts', '');
});
it('does not allow an invalid host when option is not present', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toBe('Invalid Host header');
});
it('does not allow an invalid host when option is false', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
disableHostCheck: false,
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toBe('Invalid Host header');
});
it('allows a host when option is true', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
disableHostCheck: true,
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toContain('<title>');
});
});
},
);

View File

@ -1,79 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { executeDevServer } from '../../index';
import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
const ESBUILD_LOG_TEXT = 'Application bundle generation complete.';
const WEBPACK_LOG_TEXT = 'Compiled successfully.';
describeServeBuilder(
executeDevServer,
DEV_SERVER_BUILDER_INFO,
(harness, setupTarget, isViteRun) => {
describe('option: "forceEsbuild"', () => {
beforeEach(async () => {
setupTarget(harness, {});
// Application code is not needed for these tests
await harness.writeFile('src/main.ts', 'console.log("foo");');
});
it('should use build target specified build system when not present', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
forceEsbuild: undefined,
});
const { result, response, logs } = await executeOnceAndFetch(harness, '/main.js');
expect(result?.success).toBeTrue();
expect(await response?.text()).toContain('console.log');
expect(logs).toContain(
jasmine.objectContaining({
message: jasmine.stringMatching(isViteRun ? ESBUILD_LOG_TEXT : WEBPACK_LOG_TEXT),
}),
);
});
it('should use build target specified build system when false', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
forceEsbuild: false,
});
const { result, response, logs } = await executeOnceAndFetch(harness, '/main.js');
expect(result?.success).toBeTrue();
expect(await response?.text()).toContain('console.log');
expect(logs).toContain(
jasmine.objectContaining({
message: jasmine.stringMatching(isViteRun ? ESBUILD_LOG_TEXT : WEBPACK_LOG_TEXT),
}),
);
});
it('should always use the esbuild build system with Vite when true', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
forceEsbuild: true,
});
const { result, response, logs } = await executeOnceAndFetch(harness, '/main.js');
expect(result?.success).toBeTrue();
expect(await response?.text()).toContain('console.log');
expect(logs).toContain(
jasmine.objectContaining({ message: jasmine.stringMatching(ESBUILD_LOG_TEXT) }),
);
});
});
},
);

View File

@ -1,71 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { executeDevServer } from '../../index';
import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
const FETCH_HEADERS = Object.freeze({ host: 'example.com' });
describeServeBuilder(
executeDevServer,
DEV_SERVER_BUILDER_INFO,
(harness, setupTarget, isViteRun) => {
// This option is not used when using vite.
(isViteRun ? xdescribe : xdescribe)('option: "publicHost"', () => {
beforeEach(async () => {
setupTarget(harness);
// Application code is not needed for these tests
await harness.writeFile('src/main.ts', '');
});
it('does not allow an invalid host when option is not present', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toBe('Invalid Host header');
});
it('does not allow an invalid host when option is a different host', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
publicHost: 'example.net',
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toBe('Invalid Host header');
});
it('allows a host when option is set to used host', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
publicHost: 'example.com',
});
const { result, response } = await executeOnceAndFetch(harness, '/', {
request: { headers: FETCH_HEADERS },
});
expect(result?.success).toBeTrue();
expect(await response?.text()).toContain('<title>');
});
});
},
);