103 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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;
 | 
