Skip to content

Commit ed0b573

Browse files
committed
fix(@angular-devkit/build-angular): correctly handle glob negation in proxy config when using vite
This commit fixes an issue were negated globs in proxy config were not process correctly when using vite. Closes #26970
1 parent 910531a commit ed0b573

File tree

2 files changed

+60
-50
lines changed

2 files changed

+60
-50
lines changed

packages/angular_devkit/build_angular/src/builders/dev-server/tests/options/proxy-config_spec.ts

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import * as http from 'http';
9+
import { createServer } from 'node:http';
10+
import { JasmineBuilderHarness } from '../../../../testing/jasmine-helpers';
1011
import { executeDevServer } from '../../index';
1112
import { executeOnceAndFetch } from '../execute-fetch';
1213
import { describeServeBuilder } from '../jasmine-helpers';
1314
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
1415

15-
describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
16+
describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget, isVite) => {
1617
describe('option: "proxyConfig"', () => {
1718
beforeEach(async () => {
1819
setupTarget(harness);
@@ -27,21 +28,18 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
2728
proxyConfig: 'proxy.config.json',
2829
});
2930

30-
const proxyServer = createProxyServer();
31+
const proxyServer = await createProxyServer();
3132
try {
32-
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
33-
const proxyAddress = proxyServer.address() as import('net').AddressInfo;
34-
3533
await harness.writeFiles({
36-
'proxy.config.json': `{ "/api/*": { "target": "http://127.0.0.1:${proxyAddress.port}" } }`,
34+
'proxy.config.json': `{ "/api/*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }`,
3735
});
3836

3937
const { result, response } = await executeOnceAndFetch(harness, '/api/test');
4038

4139
expect(result?.success).toBeTrue();
4240
expect(await response?.text()).toContain('TEST_API_RETURN');
4341
} finally {
44-
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
42+
await proxyServer.close();
4543
}
4644
});
4745

@@ -51,15 +49,12 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
5149
proxyConfig: 'proxy.config.json',
5250
});
5351

54-
const proxyServer = createProxyServer();
52+
const proxyServer = await createProxyServer();
5553
try {
56-
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
57-
const proxyAddress = proxyServer.address() as import('net').AddressInfo;
58-
5954
await harness.writeFiles({
6055
'proxy.config.json': `
6156
// JSON file with comments
62-
{ "/api/*": { "target": "http://127.0.0.1:${proxyAddress.port}" } }
57+
{ "/api/*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }
6358
`,
6459
});
6560

@@ -68,7 +63,7 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
6863
expect(result?.success).toBeTrue();
6964
expect(await response?.text()).toContain('TEST_API_RETURN');
7065
} finally {
71-
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
66+
await proxyServer.close();
7267
}
7368
});
7469

@@ -77,22 +72,18 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
7772
...BASE_OPTIONS,
7873
proxyConfig: 'proxy.config.js',
7974
});
80-
81-
const proxyServer = createProxyServer();
75+
const proxyServer = await createProxyServer();
8276
try {
83-
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
84-
const proxyAddress = proxyServer.address() as import('net').AddressInfo;
85-
8677
await harness.writeFiles({
87-
'proxy.config.js': `module.exports = { "/api/*": { "target": "http://127.0.0.1:${proxyAddress.port}" } }`,
78+
'proxy.config.js': `module.exports = { "/api/*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }`,
8879
});
8980

9081
const { result, response } = await executeOnceAndFetch(harness, '/api/test');
9182

9283
expect(result?.success).toBeTrue();
9384
expect(await response?.text()).toContain('TEST_API_RETURN');
9485
} finally {
95-
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
86+
await proxyServer.close();
9687
}
9788
});
9889

@@ -102,13 +93,10 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
10293
proxyConfig: 'proxy.config.js',
10394
});
10495

105-
const proxyServer = createProxyServer();
96+
const proxyServer = await createProxyServer();
10697
try {
107-
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
108-
const proxyAddress = proxyServer.address() as import('net').AddressInfo;
109-
11098
await harness.writeFiles({
111-
'proxy.config.js': `export default { "/api/*": { "target": "http://127.0.0.1:${proxyAddress.port}" } }`,
99+
'proxy.config.js': `export default { "/api/*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }`,
112100
'package.json': '{ "type": "module" }',
113101
});
114102

@@ -117,7 +105,7 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
117105
expect(result?.success).toBeTrue();
118106
expect(await response?.text()).toContain('TEST_API_RETURN');
119107
} finally {
120-
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
108+
await proxyServer.close();
121109
}
122110
});
123111

@@ -127,10 +115,9 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
127115
proxyConfig: 'proxy.config.cjs',
128116
});
129117

130-
const proxyServer = createProxyServer();
118+
const proxyServer = await createProxyServer();
131119
try {
132-
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
133-
const proxyAddress = proxyServer.address() as import('net').AddressInfo;
120+
const proxyAddress = proxyServer.address;
134121

135122
await harness.writeFiles({
136123
'proxy.config.cjs': `module.exports = { "/api/*": { "target": "http://127.0.0.1:${proxyAddress.port}" } }`,
@@ -141,7 +128,7 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
141128
expect(result?.success).toBeTrue();
142129
expect(await response?.text()).toContain('TEST_API_RETURN');
143130
} finally {
144-
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
131+
await proxyServer.close();
145132
}
146133
});
147134

@@ -151,21 +138,18 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
151138
proxyConfig: 'proxy.config.mjs',
152139
});
153140

154-
const proxyServer = createProxyServer();
141+
const proxyServer = await createProxyServer();
155142
try {
156-
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
157-
const proxyAddress = proxyServer.address() as import('net').AddressInfo;
158-
159143
await harness.writeFiles({
160-
'proxy.config.mjs': `export default { "/api/*": { "target": "http://127.0.0.1:${proxyAddress.port}" } }`,
144+
'proxy.config.mjs': `export default { "/api/*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }`,
161145
});
162146

163147
const { result, response } = await executeOnceAndFetch(harness, '/api/test');
164148

165149
expect(result?.success).toBeTrue();
166150
expect(await response?.text()).toContain('TEST_API_RETURN');
167151
} finally {
168-
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
152+
await proxyServer.close();
169153
}
170154
});
171155

@@ -175,21 +159,18 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
175159
proxyConfig: 'proxy.config.json',
176160
});
177161

178-
const proxyServer = createProxyServer();
162+
const proxyServer = await createProxyServer();
179163
try {
180-
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
181-
const proxyAddress = proxyServer.address() as import('net').AddressInfo;
182-
183164
await harness.writeFiles({
184-
'proxy.config.json': `[ { "context": ["/api", "/abc"], "target": "http://127.0.0.1:${proxyAddress.port}" } ]`,
165+
'proxy.config.json': `[ { "context": ["/api", "/abc"], "target": "http://127.0.0.1:${proxyServer.address.port}" } ]`,
185166
});
186167

187168
const { result, response } = await executeOnceAndFetch(harness, '/api/test');
188169

189170
expect(result?.success).toBeTrue();
190171
expect(await response?.text()).toContain('TEST_API_RETURN');
191172
} finally {
192-
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
173+
await proxyServer.close();
193174
}
194175
});
195176

@@ -232,18 +213,39 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
232213
}),
233214
);
234215
});
216+
217+
it('supports negatation of globs', async () => {
218+
harness.useTarget('serve', {
219+
...BASE_OPTIONS,
220+
proxyConfig: 'proxy.config.json',
221+
});
222+
223+
const proxyServer = await createProxyServer();
224+
try {
225+
await harness.writeFiles({
226+
'proxy.config.json': `
227+
{ "!something/**/*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }
228+
`,
229+
});
230+
231+
const { result, response } = await executeOnceAndFetch(harness, '/api/test');
232+
233+
expect(result?.success).toBeTrue();
234+
expect(await response?.text()).toContain('TEST_API_RETURN');
235+
} finally {
236+
await proxyServer.close();
237+
}
238+
});
235239
});
236240
});
237241

238242
/**
239243
* Creates an HTTP Server used for proxy testing that provides a `/test` endpoint
240244
* that returns a 200 response with a body of `TEST_API_RETURN`. All other requests
241245
* will return a 404 response.
242-
*
243-
* @returns An HTTP Server instance.
244246
*/
245-
function createProxyServer() {
246-
return http.createServer((request, response) => {
247+
async function createProxyServer() {
248+
const proxyServer = createServer((request, response) => {
247249
if (request.url?.endsWith('/test')) {
248250
response.writeHead(200);
249251
response.end('TEST_API_RETURN');
@@ -252,4 +254,11 @@ function createProxyServer() {
252254
response.end();
253255
}
254256
});
257+
258+
await new Promise<void>((resolve) => proxyServer.listen(0, '127.0.0.1', resolve));
259+
260+
return {
261+
address: proxyServer.address() as import('net').AddressInfo,
262+
close: () => new Promise<void>((resolve) => proxyServer.close(() => resolve())),
263+
};
255264
}

packages/angular_devkit/build_angular/src/utils/load-proxy-config.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { existsSync } from 'node:fs';
1111
import { readFile } from 'node:fs/promises';
1212
import { extname, resolve } from 'node:path';
1313
import { pathToFileURL } from 'node:url';
14-
import { parse as parseGlob } from 'picomatch';
14+
import { makeRe as makeRegExpFromGlob } from 'picomatch';
1515
import { assertIsError } from './error';
1616
import { loadEsmModule } from './load-esm';
1717

@@ -129,8 +129,9 @@ function normalizeProxyConfiguration(
129129
// TODO: Consider upstreaming glob support
130130
for (const key of Object.keys(normalizedProxy)) {
131131
if (isDynamicPattern(key)) {
132-
const { output } = parseGlob(key);
133-
normalizedProxy[`^${output}$`] = normalizedProxy[key];
132+
// Remove leading and trailing '/'.
133+
const pattern = makeRegExpFromGlob(key).toString().slice(1, -1);
134+
normalizedProxy[pattern] = normalizedProxy[key];
134135
delete normalizedProxy[key];
135136
}
136137
}

0 commit comments

Comments
 (0)