"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