Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
}
},
"resolutions": {
"typescript": "5.9.3"
"typescript": "5.9.3",
"undici-types": "^7.16.0"
}
}
1 change: 1 addition & 0 deletions packages/angular/build/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ ts_project(
":node_modules/sass",
":node_modules/source-map-support",
":node_modules/tinyglobby",
":node_modules/undici",
":node_modules/vite",
":node_modules/vitest",
":node_modules/watchpack",
Expand Down
5 changes: 3 additions & 2 deletions packages/angular/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@
"semver": "7.7.3",
"source-map-support": "0.5.21",
"tinyglobby": "0.2.15",
"undici": "7.16.0",
"vite": "7.1.11",
"watchpack": "2.4.4"
},
"optionalDependencies": {
"lmdb": "3.4.3"
},
"devDependencies": {
"@angular/ssr": "workspace:*",
"@angular-devkit/core": "workspace:*",
"@angular/ssr": "workspace:*",
"jsdom": "27.0.1",
"less": "4.4.2",
"ng-packagr": "21.0.0-rc.0",
Expand All @@ -59,9 +60,9 @@
"vitest": "4.0.6"
},
"peerDependencies": {
"@angular/core": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/compiler": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/compiler-cli": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/core": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/localize": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/platform-browser": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/platform-server": "0.0.0-ANGULAR-FW-PEER-DEP",
Expand Down
30 changes: 19 additions & 11 deletions packages/angular/build/src/builders/dev-server/vite/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { ComponentStyleRecord } from '../../../tools/vite/middlewares';
import {
ServerSsrMode,
createAngularMemoryPlugin,
createAngularServerSideSSLPlugin,
createAngularSetupMiddlewaresPlugin,
createAngularSsrTransformPlugin,
createRemoveIdPrefixPlugin,
Expand Down Expand Up @@ -207,16 +208,19 @@ export async function setupServer(
preTransformRequests,
cacheDir,
),
ssr: createSsrConfig(
externalMetadata,
serverOptions,
prebundleTransformer,
zoneless,
target,
prebundleLoaderExtensions,
thirdPartySourcemaps,
define,
),
ssr:
ssrMode === ServerSsrMode.NoSsr
? undefined
: createSsrConfig(
externalMetadata,
serverOptions,
prebundleTransformer,
zoneless,
target,
prebundleLoaderExtensions,
thirdPartySourcemaps,
define,
),
plugins: [
createAngularSetupMiddlewaresPlugin({
outputFiles,
Expand Down Expand Up @@ -258,11 +262,15 @@ export async function setupServer(
};

if (serverOptions.ssl) {
configuration.plugins ??= [];
if (!serverOptions.sslCert || !serverOptions.sslKey) {
const { default: basicSslPlugin } = await import('@vitejs/plugin-basic-ssl');
configuration.plugins ??= [];
configuration.plugins.push(basicSslPlugin());
}

if (ssrMode !== ServerSsrMode.NoSsr) {
configuration.plugins?.push(createAngularServerSideSSLPlugin());
}
}

return configuration;
Expand Down
1 change: 1 addition & 0 deletions packages/angular/build/src/tools/vite/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export { createAngularMemoryPlugin } from './angular-memory-plugin';
export { createRemoveIdPrefixPlugin } from './id-prefix-plugin';
export { createAngularSetupMiddlewaresPlugin, ServerSsrMode } from './setup-middlewares-plugin';
export { createAngularSsrTransformPlugin } from './ssr-transform-plugin';
export { createAngularServerSideSSLPlugin } from './ssr-ssl-plugin';
45 changes: 45 additions & 0 deletions packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @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.dev/license
*/

import { rootCertificates } from 'node:tls';
import type { Plugin } from 'vite';

export function createAngularServerSideSSLPlugin(): Plugin {
return {
name: 'angular-ssr-ssl-plugin',
apply: 'serve',
async configureServer({ config, httpServer }) {
const {
ssr,
server: { https: cert },
} = config;

if (!ssr || !cert) {
return;
}

// TODO(alanagius): Replace `undici` with `tls.setDefaultCACertificates` once we only support Node.js 22.18.0+ and 24.5.0+.
// See: https://nodejs.org/api/tls.html#tlssetdefaultcacertificatescerts
const { getGlobalDispatcher, setGlobalDispatcher, Agent } = await import('undici');
const originalDispatcher = getGlobalDispatcher();
const certificates = Array.isArray(cert) ? cert : [cert];

setGlobalDispatcher(
new Agent({
connect: {
ca: [...rootCertificates, ...certificates],
},
}),
);

httpServer?.on('close', () => {
setGlobalDispatcher(originalDispatcher);
});
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { Architect, BuilderRun } from '@angular-devkit/architect';
import { tags } from '@angular-devkit/core';
import { Agent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';
import { Agent } from 'undici';
import { createArchitect, host } from '../../../testing/test-utils';
import { DevServerBuilderOutput } from '../index';

Expand All @@ -35,20 +35,12 @@ describe('Dev Server Builder ssl', () => {
expect(output.success).toBe(true);
expect(output.baseUrl).toMatch(/^https:\/\/localhost:\d+\//);

// The self-signed certificate used by the dev server will cause fetch to fail
// unless reject unauthorized is disabled.
const originalDispatcher = getGlobalDispatcher();
setGlobalDispatcher(
new Agent({
const response = await fetch(output.baseUrl, {
dispatcher: new Agent({
connect: { rejectUnauthorized: false },
}),
);
try {
const response = await fetch(output.baseUrl);
expect(await response.text()).toContain('<title>HelloWorldApp</title>');
} finally {
setGlobalDispatcher(originalDispatcher);
}
});
expect(await response.text()).toContain('<title>HelloWorldApp</title>');
});

it('supports key and cert', async () => {
Expand Down Expand Up @@ -122,19 +114,11 @@ describe('Dev Server Builder ssl', () => {
expect(output.success).toBe(true);
expect(output.baseUrl).toMatch(/^https:\/\/localhost:\d+\//);

// The self-signed certificate used by the dev server will cause fetch to fail
// unless reject unauthorized is disabled.
const originalDispatcher = getGlobalDispatcher();
setGlobalDispatcher(
new Agent({
const response = await fetch(output.baseUrl, {
dispatcher: new Agent({
connect: { rejectUnauthorized: false },
}),
);
try {
const response = await fetch(output.baseUrl);
expect(await response.text()).toContain('<title>HelloWorldApp</title>');
} finally {
setGlobalDispatcher(originalDispatcher);
}
});
expect(await response.text()).toContain('<title>HelloWorldApp</title>');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { Architect } from '@angular-devkit/architect';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as browserSync from 'browser-sync';
import { Agent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';
import { Agent } from 'undici';
import { createArchitect, host } from '../../../testing/test-utils';
import { SSRDevServerBuilderOutput } from '../index';

Expand Down Expand Up @@ -85,20 +85,13 @@ describe('Serve SSR Builder', () => {
expect(output.success).toBe(true);
expect(output.baseUrl).toBe(`https://localhost:${output.port}`);

// The self-signed certificate used by the dev server will cause fetch to fail
// unless reject unauthorized is disabled.
const originalDispatcher = getGlobalDispatcher();
setGlobalDispatcher(
new Agent({
const response = await fetch(`https://localhost:${output.port}/index.html`, {
dispatcher: new Agent({
connect: { rejectUnauthorized: false },
}),
);
try {
const response = await fetch(`https://localhost:${output.port}/index.html`);
expect(await response.text()).toContain('<title>HelloWorldApp</title>');
} finally {
setGlobalDispatcher(originalDispatcher);
}
});

expect(await response.text()).toContain('<title>HelloWorldApp</title>');

await run.stop();
});
Expand Down
11 changes: 5 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/legacy-cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ e2e_suites(
# Extra runtime deps due to bundling issues.
# TODO: Clean this up.
"//:node_modules/express",
"//:node_modules/undici",
],
runner = ":runner_entrypoint",
)
1 change: 1 addition & 0 deletions tests/legacy-cli/e2e/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ts_project(
"//:node_modules/express",
"//:node_modules/fast-glob",
"//:node_modules/semver",
"//:node_modules/undici",
"//tests/legacy-cli/e2e/utils",
],
)
Loading
Loading