You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
5.4 KiB
144 lines
5.4 KiB
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.Cacher = void 0;
|
|
exports.getCacheDir = getCacheDir;
|
|
function _fsExtra() {
|
|
const data = _interopRequireDefault(require("fs-extra"));
|
|
_fsExtra = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _os() {
|
|
const data = _interopRequireDefault(require("os"));
|
|
_os = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _path() {
|
|
const data = _interopRequireDefault(require("path"));
|
|
_path = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
/*
|
|
A Cacher is used to wrap a fallible or expensive function and to memoize its results on disk
|
|
in case it either fails or we don't need fresh results very often. It stores objects in JSON, and
|
|
parses JSON from disk when returning an object.
|
|
|
|
It's constructed with a "refresher" callback which will be called for the results, a filename to use
|
|
for the cache, and an optional TTL and boostrap file. The TTL (in milliseconds) can be used to speed
|
|
up slow calls from the cache (for example checking npm published versions can be very slow). The
|
|
bootstrap file can be used to "seed" the cache with a particular value stored in a file.
|
|
|
|
If there is a problem calling the refresher function or in performing the cache's disk I/O, errors
|
|
will be stored in variables on the class. The only times Cacher will throw an exception are if it's
|
|
not possible to create the cache directory (usually weird home directory permissions), or if getAsync()
|
|
is called but no value can be provided. The latter will only occur if the refresher fails, no cache
|
|
is available on disk (i.e. this is the first call or it has been recently cleared), and bootstrapping
|
|
was not available (either a bootstrap file wasn't provided or reading/writing failed).
|
|
|
|
See src/__tests__/tools/FsCache-test.js for usage examples.
|
|
*/
|
|
class Cacher {
|
|
constructor(refresher, filename, ttlMilliseconds, bootstrapFile) {
|
|
_defineProperty(this, "refresher", void 0);
|
|
_defineProperty(this, "filename", void 0);
|
|
_defineProperty(this, "bootstrapFile", void 0);
|
|
_defineProperty(this, "ttlMilliseconds", void 0);
|
|
_defineProperty(this, "readError", void 0);
|
|
_defineProperty(this, "writeError", void 0);
|
|
this.refresher = refresher;
|
|
this.filename = _path().default.join(getCacheDir(), filename);
|
|
this.ttlMilliseconds = ttlMilliseconds || 0;
|
|
this.bootstrapFile = bootstrapFile;
|
|
}
|
|
async getAsync() {
|
|
// Let user opt out of cache for debugging purposes
|
|
if (process.env.SKIP_CACHE) {
|
|
return await this.refresher();
|
|
}
|
|
let mtime;
|
|
try {
|
|
const stats = await _fsExtra().default.stat(this.filename);
|
|
mtime = stats.mtime;
|
|
} catch {
|
|
try {
|
|
await _fsExtra().default.mkdirp(getCacheDir());
|
|
if (this.bootstrapFile) {
|
|
const bootstrapContents = (await _fsExtra().default.readFile(this.bootstrapFile)).toString();
|
|
await _fsExtra().default.writeFile(this.filename, bootstrapContents, 'utf8');
|
|
}
|
|
} catch {
|
|
// intentional no-op
|
|
}
|
|
mtime = new Date(1989, 10, 19);
|
|
}
|
|
let fromCache = null;
|
|
let failedRefresh = null;
|
|
|
|
// if mtime + ttl >= now, attempt to fetch the value, otherwise read from disk
|
|
// alternatively, if ttlMilliseconds is 0 we also update every time, regardless of the times.
|
|
// this is a workaround for the issue described in https://github.com/expo/expo-cli/issues/1683
|
|
if (this.ttlMilliseconds === 0 || new Date().getTime() - mtime.getTime() > this.ttlMilliseconds) {
|
|
try {
|
|
fromCache = await this.refresher();
|
|
try {
|
|
await _fsExtra().default.writeFile(this.filename, JSON.stringify(fromCache), 'utf8');
|
|
} catch (e) {
|
|
this.writeError = e;
|
|
// do nothing, if the refresh succeeded it'll be returned, if the persist failed we don't care
|
|
}
|
|
} catch (e) {
|
|
failedRefresh = e;
|
|
}
|
|
}
|
|
if (!fromCache) {
|
|
try {
|
|
fromCache = JSON.parse(await _fsExtra().default.readFile(this.filename, 'utf8'));
|
|
} catch (e) {
|
|
this.readError = e;
|
|
// if this fails then we've exhausted our options and it should remain null
|
|
}
|
|
}
|
|
|
|
if (fromCache) {
|
|
return fromCache;
|
|
} else {
|
|
if (failedRefresh) {
|
|
throw new Error(`Unable to perform cache refresh for ${this.filename}: ${failedRefresh}`);
|
|
} else {
|
|
throw new Error(`Unable to read ${this.filename}. ${this.readError || ''}`);
|
|
}
|
|
}
|
|
}
|
|
async clearAsync() {
|
|
try {
|
|
await _fsExtra().default.unlink(this.filename);
|
|
} catch (e) {
|
|
this.writeError = e;
|
|
}
|
|
}
|
|
}
|
|
exports.Cacher = Cacher;
|
|
function getCacheDir() {
|
|
const homeDir = _os().default.homedir();
|
|
if (process.env.XDG_CACHE_HOME) {
|
|
return process.env.XDG_CACHE_HOME;
|
|
} else if (process.platform === 'win32') {
|
|
return _path().default.join(homeDir, 'AppData', 'Local', 'Expo');
|
|
} else if (process.platform === 'darwin') {
|
|
// too many mac users have broken permissions on their ~/.cache directory
|
|
return _path().default.join(homeDir, '.expo', 'cache');
|
|
} else {
|
|
return _path().default.join(homeDir, '.cache', 'expo');
|
|
}
|
|
}
|
|
//# sourceMappingURL=FsCache.js.map
|