Skip to content

Commit 085c0f7

Browse files
authored
fix: Support async cache (#7631)
1 parent 74092a7 commit 085c0f7

File tree

11 files changed

+149
-110
lines changed

11 files changed

+149
-110
lines changed

packages/cspell/src/application.test.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -308,26 +308,34 @@ describe('Linter File Caching', () => {
308308

309309
const NoCache: LinterOptions = { cache: false };
310310
const Config: LinterOptions = { cacheFormat: 'legacy' };
311-
const WithCache: LinterOptions = { cache: true, cacheStrategy: 'content', cacheFormat: 'legacy' };
311+
const WithCacheL: LinterOptions = { cache: true, cacheStrategy: 'content', cacheFormat: 'legacy' };
312+
const WithCache: LinterOptions = { cache: true, cacheStrategy: 'content', cacheFormat: 'universal' };
312313
// const WithCacheUniversal: LinterOptions = { cache: true, cacheStrategy: 'metadata' };
313-
const WithCacheReset: LinterOptions = {
314+
const WithCacheResetL: LinterOptions = {
314315
cache: true,
315316
cacheStrategy: 'content',
316317
cacheReset: true,
317318
cacheFormat: 'legacy',
318319
};
320+
const WithCacheReset: LinterOptions = {
321+
...WithCacheResetL,
322+
cacheFormat: 'universal',
323+
};
319324
const CacheMetadata: LinterOptions = { cache: true, cacheStrategy: 'metadata', cacheFormat: 'legacy' };
320325

321326
test.each`
322-
runs | root | comment
323-
${[run([], Config, fc(0, 0)), run([], Config, fc(0, 0))]} | ${packageRoot} | ${'No files'}
324-
${[run(['*.md'], Config, fc(1, 0)), run(['*.md'], Config, fc(1, 1))]} | ${fr('cached')} | ${'Config based caching'}
325-
${[run(['*.md'], NoCache, fc(1, 0)), run(['*.md'], WithCache, fc(1, 0)), run(['*.md'], WithCache, fc(1, 1))]} | ${fr('cached')} | ${'Single .md file not cached then cached, result is not cached.'}
326-
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.md'], WithCache, fc(1, 1)), run(['*.md'], WithCache, fc(1, 1))]} | ${fr('cached')} | ${'Single .md file cached three runs'}
327-
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.{md,ts}'], WithCache, fc(2, 1)), run(['*.{md,ts}'], WithCache, fc(2, 2))]} | ${fr('cached')} | ${'cached changing glob three runs'}
328-
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.{md,ts}'], WithCache, fc(2, 1)), run(['*.{md,ts}'], WithCacheReset, fc(2, 0))]} | ${fr('cached')} | ${'cached changing glob three runs'}
329-
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.{md,ts}'], WithCache, fc(2, 1)), run(['*.{md,ts}'], CacheMetadata, fc(2, 0))]} | ${fr('cached')} | ${'with cache rebuild'}
330-
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.{md,ts}'], WithCacheReset, fc(2, 0)), run(['*.{md,ts}'], WithCache, fc(2, 2))]} | ${fr('cached')} | ${'cached changing glob three runs'}
327+
runs | root | comment
328+
${[run([], Config, fc(0, 0)), run([], Config, fc(0, 0))]} | ${packageRoot} | ${'No files'}
329+
${[run(['*.md'], Config, fc(1, 0)), run(['*.md'], Config, fc(1, 1))]} | ${fr('cached')} | ${'Config based caching'}
330+
${[run(['*.md'], NoCache, fc(1, 0)), run(['*.md'], WithCacheL, fc(1, 0)), run(['*.md'], WithCacheL, fc(1, 1))]} | ${fr('cached')} | ${'Single .md file not cached then cached, result is not cached.'}
331+
${[run(['*.md'], WithCacheL, fc(1, 0)), run(['*.md'], WithCacheL, fc(1, 1)), run(['*.md'], WithCacheL, fc(1, 1))]} | ${fr('cached')} | ${'Single .md file cached three runs'}
332+
${[run(['*.md'], WithCacheL, fc(1, 0)), run(['*.{md,ts}'], WithCacheL, fc(2, 1)), run(['*.{md,ts}'], CacheMetadata, fc(2, 0))]} | ${fr('cached')} | ${'with cache rebuild'}
333+
${[run(['*.md'], WithCacheL, fc(1, 0)), run(['*.{md,ts}'], WithCacheL, fc(2, 1)), run(['*.{md,ts}'], WithCacheL, fc(2, 2))]} | ${fr('cached')} | ${'cached changing glob three runs L WWW'}
334+
${[run(['*.md'], WithCacheL, fc(1, 0)), run(['*.{md,ts}'], WithCacheL, fc(2, 1)), run(['*.{md,ts}'], WithCacheResetL, fc(2, 0))]} | ${fr('cached')} | ${'cached changing glob three runs L WWR'}
335+
${[run(['*.md'], WithCacheL, fc(1, 0)), run(['*.{md,ts}'], WithCacheResetL, fc(2, 0)), run(['*.{md,ts}'], WithCacheL, fc(2, 2))]} | ${fr('cached')} | ${'cached changing glob three runs L WRW'}
336+
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.{md,ts}'], WithCache, fc(2, 1)), run(['*.{md,ts}'], WithCache, fc(2, 2))]} | ${fr('cached')} | ${'cached changing glob three runs U WWW'}
337+
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.{md,ts}'], WithCache, fc(2, 1)), run(['*.{md,ts}'], WithCacheReset, fc(2, 0))]} | ${fr('cached')} | ${'cached changing glob three runs U WWR'}
338+
${[run(['*.md'], WithCache, fc(1, 0)), run(['*.{md,ts}'], WithCacheReset, fc(2, 0)), run(['*.{md,ts}'], WithCache, fc(2, 2))]} | ${fr('cached')} | ${'cached changing glob three runs U WRW'}
331339
`('lint caching with $root $comment', async ({ runs, root }: TestCase) => {
332340
const reporter = new InMemoryReporter();
333341
const cacheLocation = tempLocation('.cspellcache');

packages/cspell/src/lint/lint.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
264264

265265
const dep = calcDependencies(config);
266266

267-
cache.setCachedLintResults(result, dep.files);
267+
await cache.setCachedLintResults(result, dep.files);
268268
return result;
269269
}
270270

@@ -280,7 +280,7 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
280280
): Promise<RunResult> {
281281
const fileCount = Array.isArray(files) ? files.length : undefined;
282282
const status: RunResult = runResult();
283-
const cache = createCache(cacheSettings);
283+
const cache = await createCache(cacheSettings);
284284
const failFast = cfg.options.failFast ?? configInfo.config.failFast ?? false;
285285

286286
function* prefetchFiles(files: string[]) {
@@ -369,7 +369,7 @@ export async function runLint(cfg: LintRequest): Promise<RunResult> {
369369
status.errors += result.configErrors;
370370
}
371371

372-
cache.reconcile();
372+
await cache.reconcile();
373373
return status;
374374
}
375375

packages/cspell/src/util/cache/CSpellLintResultCache.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ export interface CSpellLintResultCache {
88
/**
99
* Set the cached lint results.
1010
*/
11-
setCachedLintResults(result: LintFileResult, dependsUponFiles: string[]): void;
11+
setCachedLintResults(result: LintFileResult, dependsUponFiles: string[]): Promise<void>;
1212
/**
1313
* Persists the in-memory cache to disk.
1414
*/
15-
reconcile(): void;
15+
reconcile(): Promise<void>;
1616
/**
1717
* Resets the cache.
1818
*/
19-
reset(): void;
19+
reset(): Promise<void>;
2020
}

packages/cspell/src/util/cache/DiskCache.test.ts

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,24 @@ import { afterEach, beforeEach, describe, expect, type Mock, test, vi } from 'vi
44

55
import * as fileHelper from '../../util/fileHelper.js';
66
import type { CachedFileResult, CSpellCacheMeta } from './DiskCache.js';
7-
import { __testing__, DiskCache } from './DiskCache.js';
8-
import { createFromFile } from './file-entry-cache.mjs';
7+
import { __testing__, createDiskCache, DiskCache } from './DiskCache.js';
8+
import { createFromFile } from './file-entry-cache/index.js';
99

1010
const { calcVersion } = __testing__;
1111

12-
vi.mock('./file-entry-cache.mjs', () => ({
13-
createFromFile: vi.fn().mockReturnValue({
14-
getFileDescriptor: vi.fn(),
15-
reconcile: vi.fn(),
16-
analyzeFiles: vi.fn().mockReturnValue({
17-
changedFiles: [],
18-
notFoundFiles: [],
19-
notChangedFiles: [],
12+
vi.mock('./file-entry-cache/index.js', () => ({
13+
createFromFile: vi.fn().mockReturnValue(
14+
Promise.resolve({
15+
getFileDescriptor: vi.fn(),
16+
reconcile: vi.fn(),
17+
analyzeFiles: vi.fn().mockReturnValue({
18+
changedFiles: [],
19+
notFoundFiles: [],
20+
notChangedFiles: [],
21+
}),
22+
destroy: vi.fn(() => Promise.resolve()),
2023
}),
21-
destroy: vi.fn(),
22-
}),
24+
),
2325
}));
2426

2527
const mockCreateFileEntryCache = vi.mocked(createFromFile);
@@ -37,16 +39,25 @@ const RESULT_NO_ISSUES: CachedFileResult = {
3739

3840
describe('DiskCache', () => {
3941
let diskCache: DiskCache;
40-
let fileEntryCache: {
42+
let _fileEntryCache: Promise<{
4143
getFileDescriptor: Mock;
4244
reconcile: Mock;
4345
analyzeFiles: Mock;
4446
destroy: Mock;
45-
};
47+
}>;
48+
49+
function getFileEntryCache(): Promise<{
50+
getFileDescriptor: Mock;
51+
reconcile: Mock;
52+
analyzeFiles: Mock;
53+
destroy: Mock;
54+
}> {
55+
return _fileEntryCache;
56+
}
4657

47-
beforeEach(() => {
48-
diskCache = new DiskCache('.foobar', false, 'version', false);
49-
fileEntryCache = mockCreateFileEntryCache.mock.results[0].value;
58+
beforeEach(async () => {
59+
diskCache = await createDiskCache('.foobar', false, 'version', false);
60+
_fileEntryCache = mockCreateFileEntryCache.mock.results[0].value;
5061
});
5162

5263
describe('constructor', () => {
@@ -58,16 +69,19 @@ describe('DiskCache', () => {
5869

5970
describe('getCachedLintResults', () => {
6071
test('returns undefined for not found files', async () => {
72+
const fileEntryCache = await getFileEntryCache();
6173
fileEntryCache.getFileDescriptor.mockReturnValue({ notFound: true });
6274
expect(await diskCache.getCachedLintResults('file')).toEqual(undefined);
6375
});
6476

6577
test('returns undefined for changed files', async () => {
78+
const fileEntryCache = await getFileEntryCache();
6679
fileEntryCache.getFileDescriptor.mockReturnValue({ changed: true });
6780
expect(await diskCache.getCachedLintResults('file')).toEqual(undefined);
6881
});
6982

7083
test('returns cached result', async () => {
84+
const fileEntryCache = await getFileEntryCache();
7185
fileEntryCache.getFileDescriptor.mockReturnValue(entry(RESULT_NO_ISSUES));
7286

7387
const cachedResult = await diskCache.getCachedLintResults('file');
@@ -81,6 +95,7 @@ describe('DiskCache', () => {
8195
});
8296

8397
test('returns cached result for empty files', async () => {
98+
const fileEntryCache = await getFileEntryCache();
8499
fileEntryCache.getFileDescriptor.mockReturnValue(entry(RESULT_NO_ISSUES));
85100

86101
const cachedResult = await diskCache.getCachedLintResults('file');
@@ -95,6 +110,7 @@ describe('DiskCache', () => {
95110

96111
test('returns cached result for files with errors', async () => {
97112
const result = { ...RESULT_NO_ISSUES, errors: 10 };
113+
const fileEntryCache = await getFileEntryCache();
98114
fileEntryCache.getFileDescriptor.mockReturnValue(entry(result));
99115

100116
const fileInfo = { filename: 'file', text: 'file content' };
@@ -111,6 +127,7 @@ describe('DiskCache', () => {
111127
});
112128

113129
test('with failed dependencies', async () => {
130+
const fileEntryCache = await getFileEntryCache();
114131
fileEntryCache.getFileDescriptor.mockReturnValue(entry(RESULT_NO_ISSUES, ['fileA', 'fileB']));
115132

116133
fileEntryCache.analyzeFiles.mockReturnValue({
@@ -125,10 +142,11 @@ describe('DiskCache', () => {
125142
});
126143

127144
describe('setCachedLintResults', () => {
128-
test('skips not found files', () => {
145+
test('skips not found files', async () => {
129146
const descriptor = { notFound: true, meta: { result: undefined } };
147+
const fileEntryCache = await getFileEntryCache();
130148
fileEntryCache.getFileDescriptor.mockReturnValue(descriptor);
131-
diskCache.setCachedLintResults(
149+
await diskCache.setCachedLintResults(
132150
{
133151
fileInfo: { filename: 'some-file' },
134152
processed: true,
@@ -143,8 +161,9 @@ describe('DiskCache', () => {
143161
expect(descriptor.meta.result).toBeUndefined();
144162
});
145163

146-
test('writes result and config hash to cache', () => {
164+
test('writes result and config hash to cache', async () => {
147165
const descriptor = { meta: { data: { r: undefined } } };
166+
const fileEntryCache = await getFileEntryCache();
148167
fileEntryCache.getFileDescriptor.mockReturnValue(descriptor);
149168

150169
const result = {
@@ -153,23 +172,28 @@ describe('DiskCache', () => {
153172
errors: 0,
154173
configErrors: 0,
155174
};
156-
diskCache.setCachedLintResults({ ...result, fileInfo: { filename: 'some-file' }, elapsedTimeMs: 100 }, []);
175+
await diskCache.setCachedLintResults(
176+
{ ...result, fileInfo: { filename: 'some-file' }, elapsedTimeMs: 100 },
177+
[],
178+
);
157179

158180
expect(fileEntryCache.getFileDescriptor).toHaveBeenCalledWith('some-file');
159181
expect(descriptor.meta.data.r).toEqual(result);
160182
});
161183
});
162184

163185
describe('reconcile', () => {
164-
test('call cache.reconcile()', () => {
186+
test('call cache.reconcile()', async () => {
165187
diskCache.reconcile();
188+
const fileEntryCache = await getFileEntryCache();
166189
expect(fileEntryCache.reconcile).toHaveBeenCalledTimes(1);
167190
});
168191
});
169192

170193
describe('reset', () => {
171-
test('resets', () => {
172-
diskCache.reset();
194+
test('resets', async () => {
195+
await diskCache.reset();
196+
const fileEntryCache = await getFileEntryCache();
173197
expect(fileEntryCache.destroy).toHaveBeenCalledTimes(1);
174198
});
175199
});

packages/cspell/src/util/cache/DiskCache.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ interface DependencyCacheTree {
6868
export class DiskCache implements CSpellLintResultCache {
6969
public readonly cacheFileLocation: string;
7070
private cacheDir: string;
71-
private fileEntryCache: FileEntryCache;
7271
private dependencyCache: Map<string, Dependency> = new Map();
7372
private dependencyCacheTree: DependencyCacheTree = {};
7473
private objectCollection = new ShallowObjectCollection<CachedData>();
@@ -80,16 +79,16 @@ export class DiskCache implements CSpellLintResultCache {
8079
readonly useCheckSum: boolean,
8180
readonly cspellVersion: string,
8281
readonly useUniversalCache: boolean,
82+
private fileEntryCache: FileEntryCache,
8383
) {
8484
this.cacheFileLocation = resolvePath(cacheFileLocation);
8585
this.cacheDir = dirname(this.cacheFileLocation);
86-
this.fileEntryCache = createFromFile(this.cacheFileLocation, useCheckSum, useUniversalCache);
8786
this.version = calcVersion(cspellVersion);
8887
}
8988

9089
public async getCachedLintResults(filename: string): Promise<LintFileResult | undefined> {
9190
filename = normalizePath(filename);
92-
const fileDescriptor = this.fileEntryCache.getFileDescriptor(filename);
91+
const fileDescriptor = await this.fileEntryCache.getFileDescriptor(filename);
9392
const meta = fileDescriptor.meta as CSpellCacheMeta;
9493
const data = meta?.data;
9594
const result = data?.r;
@@ -132,11 +131,11 @@ export class DiskCache implements CSpellLintResultCache {
132131
};
133132
}
134133

135-
public setCachedLintResults(
134+
public async setCachedLintResults(
136135
{ fileInfo, elapsedTimeMs: _, cached: __, ...result }: LintFileResult,
137136
dependsUponFiles: string[],
138-
): void {
139-
const fileDescriptor = this.fileEntryCache.getFileDescriptor(fileInfo.filename);
137+
): Promise<void> {
138+
const fileDescriptor = await this.fileEntryCache.getFileDescriptor(fileInfo.filename);
140139
const meta = fileDescriptor.meta as CSpellCacheMeta;
141140
if (fileDescriptor.notFound || !meta) {
142141
return;
@@ -151,12 +150,12 @@ export class DiskCache implements CSpellLintResultCache {
151150
meta.data = data;
152151
}
153152

154-
public reconcile(): void {
155-
this.fileEntryCache.reconcile();
153+
public async reconcile(): Promise<void> {
154+
await this.fileEntryCache.reconcile();
156155
}
157156

158-
public reset(): void {
159-
this.fileEntryCache.destroy();
157+
public async reset(): Promise<void> {
158+
await this.fileEntryCache.destroy();
160159
this.dependencyCache.clear();
161160
this.dependencyCacheTree = {};
162161
this.objectCollection = new ShallowObjectCollection<CachedData>();
@@ -244,6 +243,17 @@ export class DiskCache implements CSpellLintResultCache {
244243
}
245244
}
246245

246+
export async function createDiskCache(
247+
cacheFileLocation: string,
248+
useCheckSum: boolean,
249+
cspellVersion: string,
250+
useUniversalCache: boolean,
251+
): Promise<DiskCache> {
252+
const fileEntryCache = await createFromFile(cacheFileLocation, useCheckSum, useUniversalCache);
253+
const cache = new DiskCache(cacheFileLocation, useCheckSum, cspellVersion, useUniversalCache, fileEntryCache);
254+
return cache;
255+
}
256+
247257
function getTreeEntry(tree: DependencyCacheTree, keys: string[]): DependencyCacheTree | undefined {
248258
let r: DependencyCacheTree | undefined = tree;
249259
for (const k of keys) {

packages/cspell/src/util/cache/DummyCache.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ export class DummyCache implements CSpellLintResultCache {
77
getCachedLintResults(): Promise<undefined> {
88
return Promise.resolve(undefined);
99
}
10-
setCachedLintResults(): void {
11-
return;
10+
setCachedLintResults(): Promise<void> {
11+
return Promise.resolve();
1212
}
13-
reconcile(): void {
14-
return;
13+
reconcile(): Promise<void> {
14+
return Promise.resolve();
1515
}
16-
reset(): void {
17-
return;
16+
reset(): Promise<void> {
17+
return Promise.resolve();
1818
}
1919
}

0 commit comments

Comments
 (0)