Skip to content

Commit 150a2e7

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 angular#26509
1 parent f679585 commit 150a2e7

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
@@ -448,6 +448,7 @@ export async function setupServer(
448448
css: {
449449
devSourcemap: true,
450450
},
451+
// Vite will normalize the `base` option by adding a leading and trailing forward slash.
451452
base: serverOptions.servePath,
452453
resolve: {
453454
mainFields: ['es2020', 'browser', 'module', 'main'],
@@ -583,15 +584,15 @@ export async function setupServer(
583584

584585
// Parse the incoming request.
585586
// The base of the URL is unused but required to parse the URL.
586-
const pathname = pathnameWithoutServePath(req.url, serverOptions);
587+
const pathname = pathnameWithoutBasePath(req.url, server.config.base);
587588
const extension = extname(pathname);
588589

589590
// Rewrite all build assets to a vite raw fs URL
590591
const assetSourcePath = assets.get(pathname);
591592
if (assetSourcePath !== undefined) {
592593
// The encoding needs to match what happens in the vite static middleware.
593594
// ref: https://github.com/vitejs/vite/blob/d4f13bd81468961c8c926438e815ab6b1c82735e/packages/vite/src/node/server/middlewares/static.ts#L163
594-
req.url = `/@fs/${encodeURI(assetSourcePath)}`;
595+
req.url = `${server.config.base}@fs/${encodeURI(assetSourcePath)}`;
595596
next();
596597

597598
return;
@@ -689,7 +690,7 @@ export async function setupServer(
689690

690691
// Parse the incoming request.
691692
// The base of the URL is unused but required to parse the URL.
692-
const pathname = pathnameWithoutServePath(req.url, serverOptions);
693+
const pathname = pathnameWithoutBasePath(req.url, server.config.base);
693694

694695
if (pathname === '/' || pathname === `/index.html`) {
695696
const rawHtml = outputFiles.get('/index.html')?.contents;
@@ -801,17 +802,14 @@ async function loadViteClientCode(file: string) {
801802
return contents;
802803
}
803804

804-
function pathnameWithoutServePath(url: string, serverOptions: NormalizedDevServerOptions): string {
805+
function pathnameWithoutBasePath(url: string, basePath: string): string {
805806
const parsedUrl = new URL(url, 'http://localhost');
806-
let pathname = decodeURIComponent(parsedUrl.pathname);
807-
if (serverOptions.servePath && pathname.startsWith(serverOptions.servePath)) {
808-
pathname = pathname.slice(serverOptions.servePath.length);
809-
if (pathname[0] !== '/') {
810-
pathname = '/' + pathname;
811-
}
812-
}
807+
const pathname = decodeURIComponent(parsedUrl.pathname);
813808

814-
return pathname;
809+
// slice(basePath.length - 1) to retain the trailing slash
810+
return basePath !== '/' && pathname.startsWith(basePath)
811+
? pathname.slice(basePath.length - 1)
812+
: pathname;
815813
}
816814

817815
type ViteEsBuildPlugin = NonNullable<

0 commit comments

Comments
 (0)