This repository has been archived by the owner on Mar 10, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 73
/
cache.ts
117 lines (94 loc) · 2.81 KB
/
cache.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import fileEntryCache, {
FileDescriptor,
FileEntryCache,
} from 'file-entry-cache';
import { Cache } from 'flat-cache';
import { log } from './log';
import path from 'path';
import { rmSync } from 'fs';
import { EntryConfig } from './config';
type CacheMeta<T> = FileDescriptor['meta'] & { data: T };
// we keep cache groups per entry file, to keep the cache free from override conflicts
const caches: Record<string, FileEntryCache> = {};
export function getCacheIdentity(entry: EntryConfig): string {
// don't use just the file name, the entry file can be the same, while the
// overrides make it build target specific.
const value = JSON.stringify({
...entry,
filepath: path.resolve(entry.file),
});
return hash(value);
}
function getCache(identity: string) {
if (caches[identity]) {
return caches[identity];
}
caches[identity] = fileEntryCache.create(
identity,
path.resolve(process.cwd(), './node_modules/.cache/unimported'),
);
return caches[identity];
}
// Create short hashes for file names
function hash(path: string): string {
let h;
let i;
for (h = 0, i = 0; i < path.length; h &= h) {
h = 31 * h + path.charCodeAt(i++);
}
return Math.abs(h).toString(16);
}
export class InvalidCacheError extends Error {
path: string;
constructor(message: string, path: string) {
super(message);
this.name = 'InvalidCacheError';
this.path = path;
}
static wrap(e: Error, path: string): InvalidCacheError {
return new InvalidCacheError(e.message, path);
}
}
export async function resolveEntry<T>(
path: string,
generator: () => Promise<T>,
cacheIdentity = '*',
): Promise<T> {
const cacheEntry = getCache(cacheIdentity).getFileDescriptor(path);
const meta: CacheMeta<T> = cacheEntry.meta as CacheMeta<T>;
if (!meta) {
// Something else referenced a now deleted file. Force error and let upstream handle
throw new InvalidCacheError(`${path} was deleted`, path);
}
if (cacheEntry.changed || !meta.data) {
meta.data = await generator();
}
return meta.data;
}
export function invalidateEntry(path: string): void {
for (const cache of Object.values(caches)) {
cache.removeEntry(path);
}
}
export function invalidateEntries<T>(shouldRemove: (meta: T) => boolean): void {
for (const cache of Object.values(caches)) {
Object.values((cache.cache as Cache).all()).forEach((cacheEntry) => {
if (shouldRemove(cacheEntry.data as T)) {
cache.removeEntry(cacheEntry.data.path);
}
});
}
}
export function storeCache(): void {
log.info('store cache');
for (const key of Object.keys(caches)) {
caches[key].reconcile();
}
}
export function purgeCache(): void {
log.info('purge cache');
rmSync(path.resolve(process.cwd(), './node_modules/.cache/unimported'), {
recursive: true,
force: true,
});
}