diff --git a/packages/angular/build/src/tools/esbuild/cache.ts b/packages/angular/build/src/tools/esbuild/cache.ts index 9993023893fb..2bb6afcb725f 100644 --- a/packages/angular/build/src/tools/esbuild/cache.ts +++ b/packages/angular/build/src/tools/esbuild/cache.ts @@ -11,6 +11,7 @@ * Provides infrastructure for common caching functionality within the build system. */ +import { persistentCacheStoreSetting } from '../../utils/environment-options'; import { assertIsError } from '../../utils/error'; /** @@ -248,6 +249,36 @@ export class MemoryCache extends Cache> { export async function createPersistentCacheStore( baseCachePath: string, ): Promise { + if (persistentCacheStoreSetting === 'sqlite') { + try { + const { SqliteCacheStore } = await import('./sqlite-cache-store'); + + return new SqliteCacheStore(baseCachePath + '-sqlite.db'); + } catch (err) { + assertIsError(err); + + throw new Error( + 'Unable to initialize JavaScript cache storage.\n' + `SQLite error: ${err.message}`, + { cause: err }, + ); + } + } + + if (persistentCacheStoreSetting === 'lmdb') { + try { + const { LmdbCacheStore } = await import('./lmdb-cache-store'); + + return new LmdbCacheStore(baseCachePath + '.db'); + } catch (err) { + assertIsError(err); + + throw new Error( + 'Unable to initialize JavaScript cache storage.\n' + `LMDB error: ${err.message}`, + { cause: err }, + ); + } + } + try { const { LmdbCacheStore } = await import('./lmdb-cache-store'); diff --git a/packages/angular/build/src/tools/esbuild/sqlite-cache-store_spec.ts b/packages/angular/build/src/tools/esbuild/sqlite-cache-store_spec.ts index bd834aca5f12..679bff21de20 100644 --- a/packages/angular/build/src/tools/esbuild/sqlite-cache-store_spec.ts +++ b/packages/angular/build/src/tools/esbuild/sqlite-cache-store_spec.ts @@ -111,4 +111,68 @@ describe('SqliteCacheStore', () => { expect(checkStore.has('k1')).toBeFalse(); checkStore.close(); }); + + describe('NG_BUILD_CACHE_STORE env variable option', () => { + it('should force SQLite when NG_BUILD_CACHE_STORE=sqlite', () => { + const code = ` + (async () => { + const { createPersistentCacheStore } = await import('./cache.js'); + const { SqliteCacheStore } = await import('./sqlite-cache-store.js'); + const store = await createPersistentCacheStore('dummy-sqlite-env'); + if (!(store instanceof SqliteCacheStore)) { + console.error('Expected SqliteCacheStore, got:', store.constructor.name); + process.exit(1); + } + })().catch(err => { + console.error(err); + process.exit(2); + }); + `; + const { execFileSync } = require('node:child_process'); + execFileSync(process.execPath, ['--input-type=module', '-e', code], { + cwd: __dirname, + env: { + ...process.env, + NG_BUILD_CACHE_STORE: 'sqlite', + }, + }); + }); + + it('should force LMDB when NG_BUILD_CACHE_STORE=lmdb', () => { + const code = ` + (async () => { + const { createPersistentCacheStore } = await import('./cache.js'); + const { LmdbCacheStore } = await import('./lmdb-cache-store.js'); + const store = await createPersistentCacheStore('dummy-lmdb-env'); + if (!(store instanceof LmdbCacheStore)) { + console.error('Expected LmdbCacheStore, got:', store.constructor.name); + process.exit(1); + } + })().catch(err => { + console.error(err); + process.exit(2); + }); + `; + const { execFileSync } = require('node:child_process'); + try { + execFileSync(process.execPath, ['--input-type=module', '-e', code], { + cwd: __dirname, + env: { + ...process.env, + NG_BUILD_CACHE_STORE: 'lmdb', + }, + }); + } catch (e) { + if (e && typeof e === 'object' && 'message' in e) { + const error = e as { message: string; stderr?: Buffer }; + const output = error.stderr?.toString() || error.message; + if (!output.includes('Unable to initialize JavaScript cache storage')) { + throw e; + } + } else { + throw e; + } + } + }); + }); }); diff --git a/packages/angular/build/src/utils/environment-options.ts b/packages/angular/build/src/utils/environment-options.ts index 1177563c04b5..075865345a6c 100644 --- a/packages/angular/build/src/utils/environment-options.ts +++ b/packages/angular/build/src/utils/environment-options.ts @@ -201,3 +201,19 @@ export const bazelEsbuildPluginPath = bazelBinDirectory && bazelExecRoot ? process.env['NG_INTERNAL_ESBUILD_PLUGINS_DO_NOT_USE'] : undefined; + +/** + * The persistent cache store configuration to use. + * Managed by the `NG_BUILD_CACHE_STORE` environment variable. + * - 'lmdb': Forces the use of LMDB. + * - 'sqlite': Forces the use of SQLite. + * - undefined / 'auto' / other: Automatically uses LMDB and falls back to SQLite. + */ +export const persistentCacheStoreSetting = (() => { + const env = process.env['NG_BUILD_CACHE_STORE']?.trim().toLowerCase(); + if (env === 'lmdb' || env === 'sqlite') { + return env; + } + + return undefined; +})();