This repository was archived by the owner on Feb 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcache.js
86 lines (76 loc) · 2.4 KB
/
cache.js
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
const {existsSync, mkdirSync, readJson, writeJson} = require('fs-extra')
const path = require('path')
const {forEach} = require('./iterate')
const CACHE_DIR = path.join(__dirname, '.cache')
const TODAY = new Date().toISOString().substr(0, 10)
const MEM = {}
async function getCache(file) {
if (MEM[file]) return MEM[file]
let cache = {}
try {
cache = await readJson(file)
} catch {}
MEM[file] = cache
return cache
}
const toFlush = {}
function flushLater (file) {
if (toFlush[file]) clearTimeout(toFlush[file])
toFlush[file] = setTimeout(flush, 500, file)
}
async function flush (file) {
await writeJson(file, MEM[file]).catch(console.error)
}
async function flushAll () {
return forEach(flush, Object.keys(MEM)).catch(console.error)
}
/**
* @param {Function} fun
*/
function hashFunction (fun) {
return fun.name || fun.toString().replace(/\s+/g, '_')
}
function asFileName (main, ...keys) {
if (path.isAbsolute(main)) main = path.relative(CACHE_DIR, main)
return [main, ...keys].join('.').replace(/[/\\\s]+/g, '--')
}
/**
* Stores the values returned by `resolve` inside `./.cache` as JSON files.
*
* @param {any[]} keys the first part of the cache file name
* @param {Function} resolve The method to invoke (with `keys`) to get the data.
* Will not be invoked if the data is in the cache.
* It's name (or the code for anonymous functions) is also part of the cache file name.
* @param {any} args the values to pass to `resolver`, the form the first part of the key
* arguments with `typeof === 'object'` will be ignored
* @returns {Promise<ReturnType<resolve>>}
*/
function cached (keys, resolve) {
if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR)
const hash = asFileName(...keys, hashFunction(resolve)) + '.json'
const file = path.join(CACHE_DIR, hash)
const fun = async (...args) => {
let cache = await getCache(file)
const cacheKey = args
.map(it => {
switch (typeof it) {
case 'object':
return 'object'
case 'function':
return hashFunction(it)
default:
return it.toString()
}
}).join(':')
if (cacheKey in cache) {
return cache[cacheKey]
}
const value = await resolve(...args)
cache[cacheKey] = value
flushLater(file)
return value
}
fun.name = `cached(${hashFunction(resolve)})`
return fun;
}
module.exports = {cached, flushAll, TODAY}