From 1f0369214a4b4a8035afd2c6fb0bd6dd9f546681 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:43:22 -0500 Subject: [PATCH] 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. --- .../src/builders/dev-server/vite-server.ts | 2 +- .../e2e/tests/commands/serve/assets.ts | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/legacy-cli/e2e/tests/commands/serve/assets.ts diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/vite-server.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/vite-server.ts index b77c9f632677..facb5bbee708 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/vite-server.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/vite-server.ts @@ -191,7 +191,7 @@ export async function* serveWithVite( assetFiles.clear(); if (result.assetFiles) { for (const asset of result.assetFiles) { - assetFiles.set('/' + normalizePath(asset.destination), asset.source); + assetFiles.set('/' + normalizePath(asset.destination), normalizePath(asset.source)); } } diff --git a/tests/legacy-cli/e2e/tests/commands/serve/assets.ts b/tests/legacy-cli/e2e/tests/commands/serve/assets.ts new file mode 100644 index 000000000000..53853144f985 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/commands/serve/assets.ts @@ -0,0 +1,59 @@ +import assert from 'node:assert'; +import { randomUUID } from 'node:crypto'; +import { mkdir, rm, writeFile } from 'node:fs/promises'; +import { ngServe, updateJsonFile } from '../../../utils/project'; +import { getGlobalVariable } from '../../../utils/env'; + +export default async function () { + const outsideDirectoryName = `../outside-${randomUUID()}`; + + await updateJsonFile('angular.json', (json) => { + json.projects['test-project'].architect.build.options.assets = [ + 'src/favicon.ico', + 'src/assets', + // Ensure assets located outside the workspace root work with the dev server + { 'input': outsideDirectoryName, 'glob': '**/*', 'output': './outside' }, + ]; + }); + + await mkdir(outsideDirectoryName); + try { + await writeFile(`${outsideDirectoryName}/some-asset.xyz`, 'XYZ'); + + const port = await ngServe(); + + let response = await fetch(`http://localhost:${port}/favicon.ico`); + assert.strictEqual(response.status, 200, 'favicon.ico response should be ok'); + + response = await fetch(`http://localhost:${port}/outside/some-asset.xyz`); + assert.strictEqual(response.status, 200, 'outside/some-asset.xyz response should be ok'); + assert.strictEqual(await response.text(), 'XYZ', 'outside/some-asset.xyz content is wrong'); + + // A non-existent HTML file request with accept header should fallback to the index HTML + response = await fetch(`http://localhost:${port}/does-not-exist.html`, { + headers: { accept: 'text/html' }, + }); + assert.strictEqual( + response.status, + 200, + 'non-existent file response should fallback and be ok', + ); + assert.match( + await response.text(), + /