Skip to content

Commit 54d712f

Browse files
committed
fix(@angular/build): ensure parent directory exists in SQLite cache store
Previously, initializing DatabaseSync directly with a path in a nonexistent directory failed with an unable to open database file error. Unlike other storage backends, node:sqlite does not recursively create parent directory structures. Parent directories for the cache database are now created recursively before opening the database file, preventing initialization errors when the cache directory does not yet exist.
1 parent 55583c4 commit 54d712f

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

packages/angular/build/src/tools/esbuild/sqlite-cache-store.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import { mkdirSync } from 'node:fs';
10+
import { dirname } from 'node:path';
911
import { DatabaseSync, StatementSync } from 'node:sqlite';
1012
import { deserialize, serialize } from 'node:v8';
1113
import { Cache, PersistentCacheStore } from './cache';
@@ -35,7 +37,19 @@ export class SqliteCacheStore implements PersistentCacheStore<unknown> {
3537

3638
#ensureDb(): DatabaseSync {
3739
if (!this.#db) {
38-
this.#db = new DatabaseSync(this.cachePath);
40+
if (this.cachePath === ':memory:') {
41+
this.#db = new DatabaseSync(this.cachePath);
42+
} else {
43+
// Optimistically attempt to open the database file first to avoid directory creation
44+
// syscalls on warm builds where the parent directory already exists.
45+
try {
46+
this.#db = new DatabaseSync(this.cachePath);
47+
} catch {
48+
mkdirSync(dirname(this.cachePath), { recursive: true });
49+
this.#db = new DatabaseSync(this.cachePath);
50+
}
51+
}
52+
3953
// Optimize SQLite for cache usage
4054
this.#db.exec('PRAGMA auto_vacuum = FULL;');
4155
this.#db.exec('PRAGMA journal_mode = WAL;');

packages/angular/build/src/tools/esbuild/sqlite-cache-store_spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,31 @@ describe('SqliteCacheStore', () => {
220220
}
221221
});
222222

223+
it('should create parent directories if they do not exist', async () => {
224+
const nestedDir = join(tempDir, 'nested', 'deeply', 'cache');
225+
const nestedCachePath = join(nestedDir, 'nested-cache.db');
226+
const nestedStore = new SqliteCacheStore(nestedCachePath);
227+
228+
try {
229+
await nestedStore.set('nested-key', 'nested-value');
230+
const result = await nestedStore.get('nested-key');
231+
expect(result).toBe('nested-value');
232+
} finally {
233+
nestedStore.close();
234+
}
235+
});
236+
237+
it('should support in-memory databases', async () => {
238+
const memoryStore = new SqliteCacheStore(':memory:');
239+
try {
240+
await memoryStore.set('mem-key', 'mem-value');
241+
const result = await memoryStore.get('mem-key');
242+
expect(result).toBe('mem-value');
243+
} finally {
244+
memoryStore.close();
245+
}
246+
});
247+
223248
describe('NG_BUILD_CACHE_STORE env variable option', () => {
224249
it('should force SQLite when NG_BUILD_CACHE_STORE=sqlite', () => {
225250
const code = `

0 commit comments

Comments
 (0)