From 41286344cfbb05bae5dbc5f8b1e1d1cabc0d7296 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 28 Nov 2023 15:17:50 +0000 Subject: [PATCH] fix(@angular-devkit/build-angular): serve assets from the provided `serve-path` When the `serve-path` option is used assets are now correctly servered from this location. Closes #26509 --- .../tests/options/serve-path_spec.ts | 21 ++++++++++++++++++- .../src/builders/dev-server/vite-server.ts | 20 +++++++----------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/options/serve-path_spec.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/options/serve-path_spec.ts index c80aca3670c1..734637c5aead 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/options/serve-path_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/options/serve-path_spec.ts @@ -18,7 +18,9 @@ describeServeBuilder( (harness, setupTarget, isViteRun) => { describe('option: "servePath"', () => { beforeEach(async () => { - setupTarget(harness); + setupTarget(harness, { + assets: ['src/assets'], + }); // Application code is not needed for these tests await harness.writeFile('src/main.ts', 'console.log("foo");'); @@ -96,6 +98,23 @@ describeServeBuilder( expect(result?.success).toBeTrue(); expect(await response?.text()).toContain(''); }); + + it('serves assets at specified path when option is used', async () => { + await harness.writeFile('src/assets/test.txt', 'hello world!'); + + harness.useTarget('serve', { + ...BASE_OPTIONS, + servePath: 'test', + }); + + const { result, response } = await executeOnceAndFetch(harness, '/test/assets/test.txt', { + // fallback processing requires an accept header + request: { headers: { accept: 'text/html' } }, + }); + + expect(result?.success).toBeTrue(); + expect(await response?.text()).toContain('hello world'); + }); }); }, ); 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 13f92d427110..22cb67951481 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 @@ -583,15 +583,16 @@ export async function setupServer( // Parse the incoming request. // The base of the URL is unused but required to parse the URL. - const pathname = pathnameWithoutServePath(req.url, serverOptions); + const pathname = pathnameWithoutBasePath(req.url, server.config.base); const extension = extname(pathname); // Rewrite all build assets to a vite raw fs URL const assetSourcePath = assets.get(pathname); + console.warn({ s: server.config.base }); if (assetSourcePath !== undefined) { // The encoding needs to match what happens in the vite static middleware. // ref: https://github.com/vitejs/vite/blob/d4f13bd81468961c8c926438e815ab6b1c82735e/packages/vite/src/node/server/middlewares/static.ts#L163 - req.url = `/@fs/${encodeURI(assetSourcePath)}`; + req.url = `${server.config.base}@fs/${encodeURI(assetSourcePath)}`; next(); return; @@ -689,7 +690,7 @@ export async function setupServer( // Parse the incoming request. // The base of the URL is unused but required to parse the URL. - const pathname = pathnameWithoutServePath(req.url, serverOptions); + const pathname = pathnameWithoutBasePath(req.url, server.config.base); if (pathname === '/' || pathname === `/index.html`) { const rawHtml = outputFiles.get('/index.html')?.contents; @@ -801,17 +802,12 @@ async function loadViteClientCode(file: string) { return contents; } -function pathnameWithoutServePath(url: string, serverOptions: NormalizedDevServerOptions): string { +function pathnameWithoutBasePath(url: string, basePath: string): string { const parsedUrl = new URL(url, 'http://localhost'); - let pathname = decodeURIComponent(parsedUrl.pathname); - if (serverOptions.servePath && pathname.startsWith(serverOptions.servePath)) { - pathname = pathname.slice(serverOptions.servePath.length); - if (pathname[0] !== '/') { - pathname = '/' + pathname; - } - } + const pathname = decodeURIComponent(parsedUrl.pathname); - return pathname; + // slice(basePath.length - 1) to retain the trailing slash + return basePath && pathname.startsWith(basePath) ? pathname.slice(basePath.length - 1) : pathname; } type ViteEsBuildPlugin = NonNullable<