Skip to content

Commit 9906ab7

Browse files
clydinalan-agius4
authored andcommitted
fix(@angular-devkit/build-angular): normalize asset source locations in Vite-based development server
The Vite-based development server uses an allow list to permit access to configured assets. This list is checked internally to Vite by using its normalized path form. To ensure that assets provided by the build are checked correctly on all platforms, the asset list is now normalized with Vite's path normalization prior to being used.
1 parent f4e819a commit 9906ab7

File tree

2 files changed

+60
-1
lines changed
  • packages/angular_devkit/build_angular/src/builders/dev-server
  • tests/legacy-cli/e2e/tests/commands/serve

2 files changed

+60
-1
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export async function* serveWithVite(
191191
assetFiles.clear();
192192
if (result.assetFiles) {
193193
for (const asset of result.assetFiles) {
194-
assetFiles.set('/' + normalizePath(asset.destination), asset.source);
194+
assetFiles.set('/' + normalizePath(asset.destination), normalizePath(asset.source));
195195
}
196196
}
197197

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import assert from 'node:assert';
2+
import { randomUUID } from 'node:crypto';
3+
import { mkdir, rm, writeFile } from 'node:fs/promises';
4+
import { ngServe, updateJsonFile } from '../../../utils/project';
5+
import { getGlobalVariable } from '../../../utils/env';
6+
7+
export default async function () {
8+
const outsideDirectoryName = `../outside-${randomUUID()}`;
9+
10+
await updateJsonFile('angular.json', (json) => {
11+
json.projects['test-project'].architect.build.options.assets = [
12+
'src/favicon.ico',
13+
'src/assets',
14+
// Ensure assets located outside the workspace root work with the dev server
15+
{ 'input': outsideDirectoryName, 'glob': '**/*', 'output': './outside' },
16+
];
17+
});
18+
19+
await mkdir(outsideDirectoryName);
20+
try {
21+
await writeFile(`${outsideDirectoryName}/some-asset.xyz`, 'XYZ');
22+
23+
const port = await ngServe();
24+
25+
let response = await fetch(`http://localhost:${port}/favicon.ico`);
26+
assert.strictEqual(response.status, 200, 'favicon.ico response should be ok');
27+
28+
response = await fetch(`http://localhost:${port}/outside/some-asset.xyz`);
29+
assert.strictEqual(response.status, 200, 'outside/some-asset.xyz response should be ok');
30+
assert.strictEqual(await response.text(), 'XYZ', 'outside/some-asset.xyz content is wrong');
31+
32+
// A non-existent HTML file request with accept header should fallback to the index HTML
33+
response = await fetch(`http://localhost:${port}/does-not-exist.html`, {
34+
headers: { accept: 'text/html' },
35+
});
36+
assert.strictEqual(
37+
response.status,
38+
200,
39+
'non-existent file response should fallback and be ok',
40+
);
41+
assert.match(
42+
await response.text(),
43+
/<app-root/,
44+
'non-existent file response should fallback and contain html',
45+
);
46+
47+
// Vite will incorrectly fallback in all non-existent cases so skip last test case
48+
// TODO: Remove conditional when Vite handles this case
49+
if (getGlobalVariable('argv')['esbuild']) {
50+
return;
51+
}
52+
53+
// A non-existent file without an html accept header should not be found.
54+
response = await fetch(`http://localhost:${port}/does-not-exist.png`);
55+
assert.strictEqual(response.status, 404, 'non-existent file response should be not found');
56+
} finally {
57+
await rm(outsideDirectoryName, { force: true, recursive: true });
58+
}
59+
}

0 commit comments

Comments
 (0)