Skip to content

Commit 3deb0d4

Browse files
committed
fix(@angular-devkit/build-angular): return 404 for assets that are not found
This commit updates the vite dev-server to return 404 for assets and files that are not found. Closes #26917 (cherry picked from commit 8216b11)
1 parent e880531 commit 3deb0d4

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed

packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/build-assets_spec.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
4141

4242
setupTarget(harness, {
4343
assets: ['src/extra.ts'],
44+
});
45+
46+
harness.useTarget('serve', {
47+
...BASE_OPTIONS,
48+
});
49+
50+
const { result, response } = await executeOnceAndFetch(harness, 'extra.ts');
51+
52+
expect(result?.success).toBeTrue();
53+
expect(await response?.text()).toContain(javascriptFileContent);
54+
});
55+
56+
it('should return 404 for non existing assets', async () => {
57+
setupTarget(harness, {
58+
assets: ['src/extra.js'],
4459
optimization: {
4560
scripts: true,
4661
},
@@ -50,10 +65,10 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
5065
...BASE_OPTIONS,
5166
});
5267

53-
const { result, response } = await executeOnceAndFetch(harness, 'extra.ts');
68+
const { result, response } = await executeOnceAndFetch(harness, 'extra.js');
5469

5570
expect(result?.success).toBeTrue();
56-
expect(await response?.text()).toContain(javascriptFileContent);
71+
expect(await response?.status).toBe(404);
5772
});
5873
});
5974
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ export async function setupServer(
462462
publicDir: false,
463463
esbuild: false,
464464
mode: 'development',
465-
appType: 'spa',
465+
appType: 'mpa',
466466
css: {
467467
devSourcemap: true,
468468
},

packages/angular_devkit/build_angular/src/tools/vite/angular-memory-plugin.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions):
9090
map: mapContents && Buffer.from(mapContents).toString('utf-8'),
9191
};
9292
},
93+
// eslint-disable-next-line max-lines-per-function
9394
configureServer(server) {
9495
const originalssrTransform = server.ssrTransform;
9596
server.ssrTransform = async (code, map, url, originalCode) => {
@@ -169,6 +170,8 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions):
169170
// Returning a function, installs middleware after the main transform middleware but
170171
// before the built-in HTML middleware
171172
return () => {
173+
server.middlewares.use(angularHtmlFallbackMiddleware);
174+
172175
function angularSSRMiddleware(
173176
req: Connect.IncomingMessage,
174177
res: ServerResponse,
@@ -180,8 +183,8 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions):
180183
// Skip if path is not defined.
181184
!url ||
182185
// Skip if path is like a file.
183-
// NOTE: We use a regexp to mitigate against matching requests like: /browse/pl.0ef59752c0cd457dbf1391f08cbd936f
184-
/^\.[a-z]{2,4}$/i.test(extname(url.split('?')[0]))
186+
// NOTE: We use a mime type lookup to mitigate against matching requests like: /browse/pl.0ef59752c0cd457dbf1391f08cbd936f
187+
lookupMimeTypeFromRequest(url)
185188
) {
186189
next();
187190

@@ -306,3 +309,34 @@ function pathnameWithoutBasePath(url: string, basePath: string): string {
306309
? pathname.slice(basePath.length - 1)
307310
: pathname;
308311
}
312+
313+
function angularHtmlFallbackMiddleware(
314+
req: Connect.IncomingMessage,
315+
res: ServerResponse,
316+
next: Connect.NextFunction,
317+
): void {
318+
// Similar to how it is handled in vite
319+
// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/middlewares/htmlFallback.ts#L15C19-L15C45
320+
if (
321+
(req.method === 'GET' || req.method === 'HEAD') &&
322+
(!req.url || !lookupMimeTypeFromRequest(req.url)) &&
323+
(!req.headers.accept ||
324+
req.headers.accept.includes('text/html') ||
325+
req.headers.accept.includes('text/*') ||
326+
req.headers.accept.includes('*/*'))
327+
) {
328+
req.url = '/index.html';
329+
}
330+
331+
next();
332+
}
333+
334+
function lookupMimeTypeFromRequest(url: string): string | undefined {
335+
const extension = extname(url.split('?')[0]);
336+
337+
if (extension === '.ico') {
338+
return 'image/x-icon';
339+
}
340+
341+
return extension && lookupMimeType(extension);
342+
}

0 commit comments

Comments
 (0)