From ec733f1447ab5a95535b5d3bcd4f1494fa04f4d3 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 16 Jun 2023 12:18:00 -0400 Subject: [PATCH] refactor(@angular-devkit/build-angular): use direct fs caching for font inlining The `cacache` package was only minimally used within the font inlining post-build processing. The usage has now been replaced with direct filesystem access and key hashing to cache any font files. This not only lowers the overall dependency count but also provides a small performance improvement by removing the need to resolve, load, and evaluate additional JavaScript at build time. --- package.json | 2 - .../angular_devkit/build_angular/BUILD.bazel | 2 - .../angular_devkit/build_angular/package.json | 1 - .../src/utils/index-file/inline-fonts.ts | 55 ++++++++++++++----- yarn.lock | 7 --- 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 1419a6cefc10..5c9eae38fe41 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,6 @@ "@types/babel__core": "7.20.1", "@types/babel__template": "7.4.1", "@types/browserslist": "^4.15.0", - "@types/cacache": "^15.0.0", "@types/express": "^4.16.0", "@types/http-proxy": "^1.17.4", "@types/ini": "^1.3.31", @@ -129,7 +128,6 @@ "bootstrap": "^4.0.0", "browserslist": "^4.21.5", "buffer": "6.0.3", - "cacache": "17.1.3", "chokidar": "3.5.3", "copy-webpack-plugin": "11.0.0", "critters": "0.0.18", diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index 6c9d13598cf9..50f3decc08b2 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -129,7 +129,6 @@ ts_library( "@npm//@types/babel__core", "@npm//@types/babel__template", "@npm//@types/browserslist", - "@npm//@types/cacache", "@npm//@types/inquirer", "@npm//@types/karma", "@npm//@types/less", @@ -145,7 +144,6 @@ ts_library( "@npm//babel-loader", "@npm//babel-plugin-istanbul", "@npm//browserslist", - "@npm//cacache", "@npm//chokidar", "@npm//copy-webpack-plugin", "@npm//critters", diff --git a/packages/angular_devkit/build_angular/package.json b/packages/angular_devkit/build_angular/package.json index 394c8c5beff8..85392620c50c 100644 --- a/packages/angular_devkit/build_angular/package.json +++ b/packages/angular_devkit/build_angular/package.json @@ -28,7 +28,6 @@ "babel-loader": "9.1.2", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", - "cacache": "17.1.3", "chokidar": "3.5.3", "copy-webpack-plugin": "11.0.0", "critters": "0.0.18", diff --git a/packages/angular_devkit/build_angular/src/utils/index-file/inline-fonts.ts b/packages/angular_devkit/build_angular/src/utils/index-file/inline-fonts.ts index 2f358cbad7ff..4746d589d22a 100644 --- a/packages/angular_devkit/build_angular/src/utils/index-file/inline-fonts.ts +++ b/packages/angular_devkit/build_angular/src/utils/index-file/inline-fonts.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import * as cacache from 'cacache'; -import * as fs from 'fs'; -import * as https from 'https'; import proxyAgent from 'https-proxy-agent'; -import { join } from 'path'; -import { URL } from 'url'; +import { createHash } from 'node:crypto'; +import { readFile, rm, writeFile } from 'node:fs/promises'; +import * as https from 'node:https'; +import { join } from 'node:path'; +import { URL } from 'node:url'; import { NormalizedCachedOptions } from '../normalize-cache'; import { VERSION } from '../package-version'; import { htmlRewritingStream } from './html-rewriting-stream'; @@ -34,6 +34,16 @@ const SUPPORTED_PROVIDERS: Record = { }, }; +/** + * Hash algorithm used for cached files. + */ +const CONTENT_HASH_ALGORITHM = 'sha256'; + +/** + * String length of the SHA-256 content hash stored in cached files. + */ +const CONTENT_HASH_LENGTH = 64; + export class InlineFontsProcessor { private readonly cachePath: string | undefined; constructor(private options: InlineFontsOptions) { @@ -161,13 +171,29 @@ export class InlineFontsProcessor { } private async getResponse(url: URL): Promise { - const key = `${VERSION}|${url}`; - + let cacheFile; if (this.cachePath) { - const entry = await cacache.get.info(this.cachePath, key); - if (entry) { - return fs.promises.readFile(entry.path, 'utf8'); - } + const key = createHash(CONTENT_HASH_ALGORITHM).update(`${VERSION}|${url}`).digest('hex'); + cacheFile = join(this.cachePath, key); + } + + if (cacheFile) { + try { + const data = await readFile(cacheFile, 'utf8'); + // Check for valid content via stored hash + if (data.length > CONTENT_HASH_LENGTH) { + const storedHash = data.slice(0, CONTENT_HASH_LENGTH); + const content = data.slice(CONTENT_HASH_LENGTH); + const contentHash = createHash(CONTENT_HASH_ALGORITHM).update(content).digest('base64'); + if (storedHash === contentHash) { + // Return valid content + return content; + } else { + // Delete corrupted cache content + await rm(cacheFile); + } + } + } catch {} } let agent: proxyAgent.HttpsProxyAgent | undefined; @@ -214,8 +240,11 @@ export class InlineFontsProcessor { ); }); - if (this.cachePath) { - await cacache.put(this.cachePath, key, data); + if (cacheFile) { + try { + const dataHash = createHash(CONTENT_HASH_ALGORITHM).update(data).digest('hex'); + await writeFile(cacheFile, dataHash + data); + } catch {} } return data; diff --git a/yarn.lock b/yarn.lock index 8cc1ea206a69..a38ad2cb89d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3305,13 +3305,6 @@ dependencies: browserslist "*" -"@types/cacache@^15.0.0": - version "15.0.1" - resolved "https://registry.yarnpkg.com/@types/cacache/-/cacache-15.0.1.tgz#3d1943cc80ade160c9ae98bd5c1ebcc538f9cd57" - integrity sha512-JhL2GFJuHMx4RMg4z0XfXB4ZkKdyiOaOLpjoYMXcyKfrkF3IBXNZBj6/Peo9zX/7PPHyfI63NWVD589cI2YTzg== - dependencies: - "@types/node" "*" - "@types/connect-history-api-fallback@^1.3.5": version "1.5.0" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#9fd20b3974bdc2bcd4ac6567e2e0f6885cb2cf41"