'use strict'; const fs = require('fs'); const path = require('path'); const hasha = require('hasha'); const makeDir = require('make-dir'); const writeFileAtomic = require('write-file-atomic'); const packageHash = require('package-hash'); let ownHash = ''; function getOwnHash() { ownHash = packageHash.sync(path.join(__dirname, 'package.json')); return ownHash; } function wrap(opts) { if (!(opts.factory || opts.transform) || (opts.factory && opts.transform)) { throw new Error('Specify factory or transform but not both'); } if (typeof opts.cacheDir !== 'string' && !opts.disableCache) { throw new Error('cacheDir must be a string'); } opts = { ext: '', salt: '', hashData: () => [], filenamePrefix: () => '', onHash: () => {}, ...opts }; let transformFn = opts.transform; const {factory, cacheDir, shouldTransform, disableCache, hashData, onHash, filenamePrefix, ext, salt} = opts; const cacheDirCreated = opts.createCacheDir === false; let created = transformFn && cacheDirCreated; const encoding = opts.encoding === 'buffer' ? undefined : opts.encoding || 'utf8'; function transform(input, metadata, hash) { if (!created) { if (!cacheDirCreated && !disableCache) { makeDir.sync(cacheDir); } if (!transformFn) { transformFn = factory(cacheDir); } created = true; } return transformFn(input, metadata, hash); } return function (input, metadata) { if (shouldTransform && !shouldTransform(input, metadata)) { return input; } if (disableCache) { return transform(input, metadata); } const data = [ ownHash || getOwnHash(), input, salt, ...[].concat(hashData(input, metadata)) ]; const hash = hasha(data, {algorithm: 'sha256'}); const cachedPath = path.join(cacheDir, filenamePrefix(metadata) + hash + ext); onHash(input, metadata, hash); let result; let retry = 0; /* eslint-disable-next-line no-constant-condition */ while (true) { try { return fs.readFileSync(cachedPath, encoding); } catch (readError) { if (!result) { result = transform(input, metadata, hash); } try { writeFileAtomic.sync(cachedPath, result, {encoding}); return result; } catch (error) { /* Likely https://github.com/npm/write-file-atomic/issues/28 * Make up to 3 attempts to read or write the cache. */ retry++; if (retry > 3) { throw error; } } } } }; } module.exports = wrap;