diff --git a/packages/angular/build/src/tools/esbuild/sqlite-cache-store.ts b/packages/angular/build/src/tools/esbuild/sqlite-cache-store.ts index a46251ce4d31..9de037800844 100644 --- a/packages/angular/build/src/tools/esbuild/sqlite-cache-store.ts +++ b/packages/angular/build/src/tools/esbuild/sqlite-cache-store.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ +import { mkdirSync } from 'node:fs'; +import { dirname } from 'node:path'; import { DatabaseSync, StatementSync } from 'node:sqlite'; import { deserialize, serialize } from 'node:v8'; import { Cache, PersistentCacheStore } from './cache'; @@ -35,7 +37,19 @@ export class SqliteCacheStore implements PersistentCacheStore { #ensureDb(): DatabaseSync { if (!this.#db) { - this.#db = new DatabaseSync(this.cachePath); + if (this.cachePath === ':memory:') { + this.#db = new DatabaseSync(this.cachePath); + } else { + // Optimistically attempt to open the database file first to avoid directory creation + // syscalls on warm builds where the parent directory already exists. + try { + this.#db = new DatabaseSync(this.cachePath); + } catch { + mkdirSync(dirname(this.cachePath), { recursive: true }); + this.#db = new DatabaseSync(this.cachePath); + } + } + // Optimize SQLite for cache usage this.#db.exec('PRAGMA auto_vacuum = FULL;'); this.#db.exec('PRAGMA journal_mode = WAL;'); 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 c76394cb7ee3..15eec7cb1298 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 @@ -220,6 +220,31 @@ describe('SqliteCacheStore', () => { } }); + it('should create parent directories if they do not exist', async () => { + const nestedDir = join(tempDir, 'nested', 'deeply', 'cache'); + const nestedCachePath = join(nestedDir, 'nested-cache.db'); + const nestedStore = new SqliteCacheStore(nestedCachePath); + + try { + await nestedStore.set('nested-key', 'nested-value'); + const result = await nestedStore.get('nested-key'); + expect(result).toBe('nested-value'); + } finally { + nestedStore.close(); + } + }); + + it('should support in-memory databases', async () => { + const memoryStore = new SqliteCacheStore(':memory:'); + try { + await memoryStore.set('mem-key', 'mem-value'); + const result = await memoryStore.get('mem-key'); + expect(result).toBe('mem-value'); + } finally { + memoryStore.close(); + } + }); + describe('NG_BUILD_CACHE_STORE env variable option', () => { it('should force SQLite when NG_BUILD_CACHE_STORE=sqlite', () => { const code = `