Skip to content

Commit 7455fdc

Browse files
committed
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 (cherry picked from commit 150a2e7)
1 parent 27e7c2e commit 7455fdc

File tree

2 files changed

+30
-13
lines changed

2 files changed

+30
-13
lines changed

packages/angular_devkit/build_angular/src/builders/dev-server/tests/options/serve-path_spec.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ describeServeBuilder(
1818
(harness, setupTarget, isViteRun) => {
1919
describe('option: "servePath"', () => {
2020
beforeEach(async () => {
21-
setupTarget(harness);
21+
setupTarget(harness, {
22+
assets: ['src/assets'],
23+
});
2224

2325
// Application code is not needed for these tests
2426
await harness.writeFile('src/main.ts', 'console.log("foo");');
@@ -96,6 +98,23 @@ describeServeBuilder(
9698
expect(result?.success).toBeTrue();
9799
expect(await response?.text()).toContain('<title>');
98100
});
101+
102+
it('serves assets at specified path when option is used', async () => {
103+
await harness.writeFile('src/assets/test.txt', 'hello world!');
104+
105+
harness.useTarget('serve', {
106+
...BASE_OPTIONS,
107+
servePath: 'test',
108+
});
109+
110+
const { result, response } = await executeOnceAndFetch(harness, '/test/assets/test.txt', {
111+
// fallback processing requires an accept header
112+
request: { headers: { accept: 'text/html' } },
113+
});
114+
115+
expect(result?.success).toBeTrue();
116+
expect(await response?.text()).toContain('hello world');
117+
});
99118
});
100119
},
101120
);

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

+10-12
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ export async function setupServer(
439439
css: {
440440
devSourcemap: true,
441441
},
442+
// Vite will normalize the `base` option by adding a leading and trailing forward slash.
442443
base: serverOptions.servePath,
443444
resolve: {
444445
mainFields: ['es2020', 'browser', 'module', 'main'],
@@ -568,15 +569,15 @@ export async function setupServer(
568569

569570
// Parse the incoming request.
570571
// The base of the URL is unused but required to parse the URL.
571-
const pathname = pathnameWithoutServePath(req.url, serverOptions);
572+
const pathname = pathnameWithoutBasePath(req.url, server.config.base);
572573
const extension = extname(pathname);
573574

574575
// Rewrite all build assets to a vite raw fs URL
575576
const assetSourcePath = assets.get(pathname);
576577
if (assetSourcePath !== undefined) {
577578
// The encoding needs to match what happens in the vite static middleware.
578579
// ref: https://github.com/vitejs/vite/blob/d4f13bd81468961c8c926438e815ab6b1c82735e/packages/vite/src/node/server/middlewares/static.ts#L163
579-
req.url = `/@fs/${encodeURI(assetSourcePath)}`;
580+
req.url = `${server.config.base}@fs/${encodeURI(assetSourcePath)}`;
580581
next();
581582

582583
return;
@@ -674,7 +675,7 @@ export async function setupServer(
674675

675676
// Parse the incoming request.
676677
// The base of the URL is unused but required to parse the URL.
677-
const pathname = pathnameWithoutServePath(req.url, serverOptions);
678+
const pathname = pathnameWithoutBasePath(req.url, server.config.base);
678679

679680
if (pathname === '/' || pathname === `/index.html`) {
680681
const rawHtml = outputFiles.get('/index.html')?.contents;
@@ -785,17 +786,14 @@ async function loadViteClientCode(file: string) {
785786
return contents;
786787
}
787788

788-
function pathnameWithoutServePath(url: string, serverOptions: NormalizedDevServerOptions): string {
789+
function pathnameWithoutBasePath(url: string, basePath: string): string {
789790
const parsedUrl = new URL(url, 'http://localhost');
790-
let pathname = decodeURIComponent(parsedUrl.pathname);
791-
if (serverOptions.servePath && pathname.startsWith(serverOptions.servePath)) {
792-
pathname = pathname.slice(serverOptions.servePath.length);
793-
if (pathname[0] !== '/') {
794-
pathname = '/' + pathname;
795-
}
796-
}
791+
const pathname = decodeURIComponent(parsedUrl.pathname);
797792

798-
return pathname;
793+
// slice(basePath.length - 1) to retain the trailing slash
794+
return basePath !== '/' && pathname.startsWith(basePath)
795+
? pathname.slice(basePath.length - 1)
796+
: pathname;
799797
}
800798

801799
type ViteEsBuildPlugin = NonNullable<

0 commit comments

Comments
 (0)