Skip to content

Commit 01247c9

Browse files
clydinKeen Yee Liau
authored andcommitted
fix(@angular-devkit/build-angular): stop dev server fallback outside of serve path
The serve path represents the base of the application. Accessing a different path (`/api/` for instance) should not cause the application to load if the application's base is `/test/`
1 parent 0b700c3 commit 01247c9

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,16 @@ export function buildServerConfig(
259259
host: serverOptions.host,
260260
port: serverOptions.port,
261261
headers: { 'Access-Control-Allow-Origin': '*' },
262-
historyApiFallback: {
262+
historyApiFallback: !!browserOptions.index && {
263263
index: `${servePath}/${path.basename(browserOptions.index)}`,
264264
disableDotRule: true,
265265
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
266+
rewrites: [
267+
{
268+
from: new RegExp(`^(?!${servePath})/.*`),
269+
to: context => url.format(context.parsedUrl),
270+
},
271+
],
266272
} as WebpackDevServer.HistoryApiFallbackConfig,
267273
stats: false,
268274
compress: styles || scripts,

packages/angular_devkit/build_angular/test/dev-server/serve-path_spec_large.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('Dev Server Builder serve path', () => {
2727
await Promise.all(runs.map(r => r.stop()));
2828
});
2929

30-
it('works', async () => {
30+
it('uses the servePath option when specified', async () => {
3131
const run = await architect.scheduleTarget(target, { servePath: 'test/' });
3232
runs.push(run);
3333
const output = await run.result as DevServerBuilderOutput;
@@ -37,4 +37,39 @@ describe('Dev Server Builder serve path', () => {
3737
const response = await fetch(`${output.baseUrl}/polyfills.js`);
3838
expect(await response.text()).toContain('window["webpackJsonp"]');
3939
}, 30000);
40+
41+
it('does not fallback when request is outside serve path', async () => {
42+
const run = await architect.scheduleTarget(target, { servePath: 'test/' });
43+
44+
await expectAsync(run.result).toBeResolvedTo(
45+
jasmine.objectContaining({ success: true, baseUrl: 'http://localhost:4200/test' }),
46+
);
47+
48+
// fallback processing requires an accept header
49+
await expectAsync(
50+
fetch('http://localhost:4200', { headers: { 'accept': 'text/html' } }),
51+
).toBeResolvedTo(jasmine.objectContaining({ status: 404 }));
52+
53+
await expectAsync(
54+
fetch('http://localhost:4200/api/', { headers: { 'accept': 'text/html' } }),
55+
).toBeResolvedTo(jasmine.objectContaining({ status: 404 }));
56+
57+
await run.stop();
58+
}, 30000);
59+
60+
it('does fallback when request is inside serve path', async () => {
61+
const run = await architect.scheduleTarget(target, { servePath: 'test/' });
62+
63+
await expectAsync(run.result).toBeResolvedTo(
64+
jasmine.objectContaining({ success: true, baseUrl: 'http://localhost:4200/test' }),
65+
);
66+
67+
// fallback processing requires an accept header
68+
await expectAsync(
69+
fetch('http://localhost:4200/test/nothere', { headers: { 'accept': 'text/html' } }),
70+
).toBeResolvedTo(jasmine.objectContaining({ status: 200 }));
71+
72+
await run.stop();
73+
}, 30000);
74+
4075
});

0 commit comments

Comments
 (0)