Skip to content

Commit fb28a15

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 43f22f4 commit fb28a15

File tree

2 files changed

+34
-13
lines changed

2 files changed

+34
-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

+14-12
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ export async function* serveWithVite(
9090
serverOptions.servePath = browserOptions.baseHref;
9191
}
9292

93+
if (serverOptions.servePath) {
94+
serverOptions.servePath = addLeadingSlash(serverOptions.servePath);
95+
}
96+
9397
// The development server currently only supports a single locale when localizing.
9498
// This matches the behavior of the Webpack-based development server but could be expanded in the future.
9599
if (
@@ -583,15 +587,15 @@ export async function setupServer(
583587

584588
// Parse the incoming request.
585589
// The base of the URL is unused but required to parse the URL.
586-
const pathname = pathnameWithoutServePath(req.url, serverOptions);
590+
const pathname = pathnameWithoutServePath(req.url, server.config.base);
587591
const extension = extname(pathname);
588592

589593
// Rewrite all build assets to a vite raw fs URL
590594
const assetSourcePath = assets.get(pathname);
591595
if (assetSourcePath !== undefined) {
592596
// The encoding needs to match what happens in the vite static middleware.
593597
// ref: https://github.com/vitejs/vite/blob/d4f13bd81468961c8c926438e815ab6b1c82735e/packages/vite/src/node/server/middlewares/static.ts#L163
594-
req.url = `/@fs/${encodeURI(assetSourcePath)}`;
598+
req.url = `${server.config.base}/@fs/${encodeURI(assetSourcePath)}`;
595599
next();
596600

597601
return;
@@ -689,7 +693,7 @@ export async function setupServer(
689693

690694
// Parse the incoming request.
691695
// The base of the URL is unused but required to parse the URL.
692-
const pathname = pathnameWithoutServePath(req.url, serverOptions);
696+
const pathname = pathnameWithoutServePath(req.url, server.config.base);
693697

694698
if (pathname === '/' || pathname === `/index.html`) {
695699
const rawHtml = outputFiles.get('/index.html')?.contents;
@@ -801,17 +805,15 @@ async function loadViteClientCode(file: string) {
801805
return contents;
802806
}
803807

804-
function pathnameWithoutServePath(url: string, serverOptions: NormalizedDevServerOptions): string {
808+
function pathnameWithoutServePath(url: string, basePath: string): string {
805809
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-
}
810+
const pathname = decodeURIComponent(parsedUrl.pathname);
811+
812+
return basePath && pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
813+
}
813814

814-
return pathname;
815+
function addLeadingSlash(value: string): string {
816+
return value[0] === '/' ? value : '/' + value;
815817
}
816818

817819
type ViteEsBuildPlugin = NonNullable<

0 commit comments

Comments
 (0)