diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts index 166a40ded6b3..df7d2e06449f 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts @@ -10,6 +10,7 @@ import assert from 'node:assert'; import { readFile } from 'node:fs/promises'; import path from 'node:path'; import type { VitestPlugin } from 'vitest/node'; +import { createBuildAssetsMiddleware } from '../../../../tools/vite/middlewares/assets-middleware'; import { toPosixPath } from '../../../../utils/path'; import type { ResultFile } from '../../../application/results'; import type { NormalizedUnitTestBuilderOptions } from '../../options'; @@ -129,6 +130,11 @@ export function createVitestPlugins( }; } }, + configureServer: (server) => { + server.middlewares.use( + createBuildAssetsMiddleware(server.config.base, buildResultFiles), + ); + }, }, { name: 'angular:html-index', diff --git a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts index a9fe69ffca15..414ec5ca13d1 100644 --- a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts +++ b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts @@ -12,6 +12,7 @@ import { readFileSync } from 'node:fs'; import type { ServerResponse } from 'node:http'; import { extname } from 'node:path'; import type { Connect, ViteDevServer } from 'vite'; +import { ResultFile } from '../../../builders/application/results'; import { AngularMemoryOutputFiles, AngularOutputAssets, pathnameWithoutBasePath } from '../utils'; export interface ComponentStyleRecord { @@ -214,3 +215,45 @@ function checkAndHandleEtag( return false; } + +export function createBuildAssetsMiddleware( + basePath: string, + buildResultFiles: ReadonlyMap, + readHandler: (path: string) => Buffer = readFileSync, +): Connect.NextHandleFunction { + return function buildAssetsMiddleware(req, res, next) { + if (req.url === undefined || res.writableEnded) { + return; + } + + // Parse the incoming request. + // The base of the URL is unused but required to parse the URL. + const pathname = pathnameWithoutBasePath(req.url, basePath); + const extension = extname(pathname); + if (extension && !/\.[mc]?[jt]s(?:\.map)?$/.test(extension)) { + const outputFile = buildResultFiles.get(pathname.slice(1)); + if (outputFile) { + const contents = + outputFile.origin === 'memory' ? outputFile.contents : readHandler(outputFile.inputPath); + + const etag = `W/${createHash('sha256').update(contents).digest('hex')}`; + if (checkAndHandleEtag(req, res, etag)) { + return; + } + + const mimeType = lookupMimeType(extension); + if (mimeType) { + res.setHeader('Content-Type', mimeType); + } + + res.setHeader('ETag', etag); + res.setHeader('Cache-Control', 'no-cache'); + res.end(contents); + + return; + } + } + + next(); + }; +}