'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var _Promise = _interopDefault(require('babel-runtime/core-js/promise')); var _asyncToGenerator = _interopDefault(require('babel-runtime/helpers/asyncToGenerator')); var _extends = _interopDefault(require('babel-runtime/helpers/extends')); var _Object$keys = _interopDefault(require('babel-runtime/core-js/object/keys')); var PluginError = _interopDefault(require('plugin-error')); var through = _interopDefault(require('through2')); var Cache = _interopDefault(require('cache-swap')); var File = _interopDefault(require('vinyl')); var pick = _interopDefault(require('object.pick')); var _JSON$stringify = _interopDefault(require('babel-runtime/core-js/json/stringify')); var _Object$assign = _interopDefault(require('babel-runtime/core-js/object/assign')); var _Array$from = _interopDefault(require('babel-runtime/core-js/array/from')); var _Reflect$deleteProperty = _interopDefault(require('babel-runtime/core-js/reflect/delete-property')); var _Reflect$apply = _interopDefault(require('babel-runtime/core-js/reflect/apply')); var _Map = _interopDefault(require('babel-runtime/core-js/map')); var EventEmitter = _interopDefault(require('events')); var crypto = _interopDefault(require('crypto')); const version = "1.0.2"; const whitespaces = 2; const eventListenersCount = 3; function makeHash(key) { return crypto.createHash('md5').update(key).digest('hex'); } class TaskProxy { constructor(task, inputOptions) { this.task = task; this.options = inputOptions; this._cacheQueue = new _Map(); this._removeListeners = []; if (task) { this.patchTask(); } } patchTask() { const task = this.task, _transform = task._transform; task._transform = (chunk, encoding, next) => { _Reflect$apply(_transform, task, [chunk, encoding, (...args) => { next(...args); // eslint-disable-line task.emit('gulp-cache:transformed'); }]); }; } processFile(inputFile, signals = new EventEmitter()) { process.nextTick(() => { this._processFileAsync(inputFile, signals); }); return signals; } _processFileAsync(inputFile, signals = new EventEmitter()) { var _this = this; return _asyncToGenerator(function* () { const cached = yield _this._checkForCachedValue(inputFile); // If we found a cached value // The path of the cache key should also be identical to the original one when the file path changed inside the task const cachedValue = cached.value, cachedValueIsEmpty = !Array.isArray(cachedValue) || !cachedValue.length; const cachedValuesWithNormalPaths = cachedValueIsEmpty ? [] : cachedValue.filter(function (file) { return (!file.gulpCache$filePathChangedInsideTask || file.gulpCache$originalPath === inputFile.path) && (!file.gulpCache$fileBaseChangedInsideTask || file.gulpCache$originalBase === inputFile.base); }); if (cachedValuesWithNormalPaths.length) { cachedValuesWithNormalPaths.forEach(function (cachedFile) { // Extend the cached value onto the file, but don't overwrite original path info const file = new File(_extends({}, cachedFile, pick(inputFile, ['cwd', 'base', 'stat', 'history', 'path']), { // file contents contents: cachedFile.contents })); // Restore the file path if it was set if (cachedFile.path && cachedFile.gulpCache$filePathChangedInsideTask) { file.path = cachedFile.path; } // Restore the file base if it was set if (cachedFile.base && cachedFile.gulpCache$fileBaseChangedInsideTask) { file.base = cachedFile.base; } _Reflect$deleteProperty(file, 'gulpCache$filePathChangedInsideTask'); _Reflect$deleteProperty(file, 'gulpCache$fileBaseChangedInsideTask'); _Reflect$deleteProperty(file, 'gulpCache$originalPath'); _Reflect$deleteProperty(file, 'gulpCache$originalBase'); signals.emit('file', file); }); signals.emit('done'); _this._removeListeners.push(function () { // Remove all listeners from `signals` signals.removeAllListeners(); }); return; } _this._runProxiedTaskAndQueueCache(inputFile, cached.key, signals); })(); } flush(next) { var _this2 = this; return _asyncToGenerator(function* () { const task = _this2.task; try { if (typeof task._flush == 'function') { task._flush((() => { var _ref = _asyncToGenerator(function* (...args) { yield _this2._flush(); next(...args); }); return function () { return _ref.apply(this, arguments); }; })()); } else { yield _this2._flush(); next(); return; } } catch (err) { next(err); return; } })(); } _flush() { var _this3 = this; return _asyncToGenerator(function* () { _this3._removeListeners.forEach(function (remove) { remove(); }); _this3._removeListeners = []; yield _Promise.all(_Array$from(_this3._cacheQueue).map((() => { var _ref2 = _asyncToGenerator(function* ([cachedKey, files]) { return _this3._storeCachedResult(cachedKey, files); }); return function (_x) { return _ref2.apply(this, arguments); }; })())); _this3._cacheQueue = new _Map(); })(); } removeCachedResult(file) { var _this4 = this; return _asyncToGenerator(function* () { const cachedKey = yield _this4._getFileKey(file); return _this4._removeCached(_this4.options.name, cachedKey); })(); } _getFileKey(file) { var _this5 = this; return _asyncToGenerator(function* () { const getKey = _this5.options.key, key = yield getKey(file); return key ? makeHash(key) : key; })(); } _checkForCachedValue(file) { var _this6 = this; return _asyncToGenerator(function* () { const key = yield _this6._getFileKey(file); // If no key returned, bug out early if (!key) { return { value: null, key }; } var _options = _this6.options; const cacheName = _options.name, restore = _options.restore; const cached = yield _this6._getCached(cacheName, key); if (!cached) { return { value: null, key }; } let parsedContents = null; try { parsedContents = JSON.parse(cached.contents); } catch (err) { parsedContents = [{ cached: cached.contents }]; } if (restore) { parsedContents = parsedContents.map(function (parsedFile) { const restoredFile = restore(parsedFile); // Force restore service properties restoredFile.gulpCache$filePathChangedInsideTask = parsedFile.gulpCache$filePathChangedInsideTask; restoredFile.gulpCache$fileBaseChangedInsideTask = parsedFile.gulpCache$fileBaseChangedInsideTask; restoredFile.gulpCache$originalPath = parsedFile.gulpCache$originalPath; restoredFile.gulpCache$originalBase = parsedFile.gulpCache$originalBase; return restoredFile; }); } return { value: parsedContents, key }; })(); } _getValueFromResult(result) { var _this7 = this; return _asyncToGenerator(function* () { const getValue = _this7.options.value; if (typeof getValue !== 'function') { if (typeof getValue === 'string') { return { [getValue]: result[getValue] }; } return getValue; } return getValue(result); })(); } _storeCachedResult(key, result) { var _this8 = this; return _asyncToGenerator(function* () { // If we didn't have a cachedKey, skip caching result if (!key) { return result; } const options = _this8.options; const files = (yield _Promise.all(result.map((() => { var _ref3 = _asyncToGenerator(function* ({ file, meta }) { if (options.success !== true && !(yield options.success(file))) { return null; } return _Object$assign((yield _this8._getValueFromResult(file)), meta); }); return function (_x2) { return _ref3.apply(this, arguments); }; })()))).filter(Boolean); return _this8._addCached(_this8.options.name, key, _JSON$stringify(files, null, whitespaces)); })(); } _queueCache(file, cachedKey, originalBase, originalPath) { var _this9 = this; return _asyncToGenerator(function* () { const _cacheQueue = _this9._cacheQueue; const item = { file: file.clone({ contents: false }), meta: { // Check if the task changed the file path gulpCache$filePathChangedInsideTask: file.path !== originalPath, // Check if the task changed the base path gulpCache$fileBaseChangedInsideTask: file.base !== originalBase, // Keep track of the original path gulpCache$originalPath: originalPath, // Keep track of the original base gulpCache$originalBase: originalBase } }; if (_cacheQueue.has(cachedKey)) { _cacheQueue.get(cachedKey).push(item); } else { _cacheQueue.set(cachedKey, [item]); } })(); } _runProxiedTaskAndQueueCache(file, cachedKey, signals = new EventEmitter()) { const originalBase = file.base, originalPath = file.path; signals.on('cache', file => { this._queueCache(file, cachedKey, originalBase, originalPath); signals.emit('file', file); }); return this._runProxiedTask(file, cachedKey, signals); } _runProxiedTask(file, cachedKey, signals = new EventEmitter()) { const task = this.task, hasCacheListener = Boolean(signals.listenerCount('cache')); function onError(err) { signals.emit('error', err); } function onData(datum) { if (datum._cachedKey !== cachedKey) { return; } _Reflect$deleteProperty(datum, '_cachedKey'); if (hasCacheListener) { signals.emit('cache', datum); } else { signals.emit('file', datum); } } function onTransformed() { signals.emit('done'); } this._removeListeners.push(() => { // Be good citizens and remove our listeners task.removeListener('error', onError); task.removeListener('gulp-cache:transformed', onTransformed); task.removeListener('data', onData); // Reduce the maxListeners back down task.setMaxListeners(task._maxListeners - eventListenersCount); // Remove all listeners from `signals` signals.removeAllListeners(); }); // Bump up max listeners to prevent memory leak warnings const currMaxListeners = task._maxListeners || 0; task.setMaxListeners(currMaxListeners + eventListenersCount); task.on('data', onData); task.once('gulp-cache:transformed', onTransformed); task.once('error', onError); file._cachedKey = cachedKey; // Run through the other task and grab output (or error) task.write(file); return signals; } /** * Cache promise wrappers. */ _addCached(...args) { return new _Promise((resolve, reject) => { this.options.fileCache.addCached(...args, (err, res) => { if (err) { reject(err); return; } resolve(res); }); }); } _getCached(...args) { return new _Promise((resolve, reject) => { this.options.fileCache.getCached(...args, (err, res) => { if (err) { reject(err); return; } resolve(res); }); }); } _removeCached(...args) { return new _Promise((resolve, reject) => { this.options.fileCache.removeCached(...args, err => { if (err) { reject(err); return; } resolve(); }); }); } } module.exports = exports['default']; const fileCache = new Cache({ cacheDirName: 'gulp-cache' }); function defaultKey(file) { return `${version}${file.contents.toString('base64')}`; } function defaultRestore(restored) { if (restored.contents) { // Handle node 0.11 buffer to JSON as object with { type: 'buffer', data: [...] } if (restored && restored.contents && Array.isArray(restored.contents.data)) { restored.contents = new Buffer(restored.contents.data); } else if (Array.isArray(restored.contents)) { restored.contents = new Buffer(restored.contents); } else if (typeof restored.contents === 'string') { restored.contents = new Buffer(restored.contents, 'base64'); } } const restoredFile = new File(restored); // Restore any properties that the original task put on the file; // but omit the normal properties of the file _Object$keys(restored).forEach(key => { if (File.isCustomProp(key)) { restoredFile[key] = restored[key]; } }); return restoredFile; } function defaultValue(file) { const vinylProps = ['cwd', 'base', 'contents', 'stat', 'history', 'path'], customProps = _Object$keys(file).filter(File.isCustomProp); // Convert from a File object (from vinyl) into a plain object return pick(file, [...vinylProps, ...customProps]); } const defaultOptions = { fileCache, name: 'default', success: true, key: defaultKey, restore: defaultRestore, value: defaultValue }; plugin.Cache = Cache; plugin.fileCache = fileCache; plugin.defaultOptions = defaultOptions; function plugin(task, inputOptions) { // Check for required task option if (!task) { throw new PluginError('gulp-cache', 'Must pass a task to cache()'); } const options = _extends({}, plugin.defaultOptions, task.cacheable, inputOptions); const taskProxy = new TaskProxy(task, options); function each(file, enc, next) { if (file.isNull()) { next(null, file); return; } if (file.isStream()) { next(new PluginError('gulp-cache', 'Cannot operate on stream sources')); return; } const signals = taskProxy.processFile(file); signals.on('error', err => { next(new PluginError('gulp-cache', err)); }); signals.on('file', file => { this.push(file); }); signals.on('done', () => { next(null); }); } function flush(next) { taskProxy.flush(next); } return through.obj(each, flush); } plugin.clear = function clear(inputOptions) { let each = (() => { var _ref = _asyncToGenerator(function* (file, enc, next) { if (file.isNull()) { next(null, file); return; } if (file.isStream()) { next(new PluginError('gulp-cache', 'Cannot operate on stream sources')); return; } try { yield taskProxy.removeCachedResult(file); next(null, file); return; } catch (err) { next(new PluginError('gulp-cache', err)); return; } }); return function each(_x, _x2, _x3) { return _ref.apply(this, arguments); }; })(); const options = _extends({}, plugin.defaultOptions, inputOptions); const taskProxy = new TaskProxy(null, options); return through.obj(each); }; plugin.clearAll = function clearAll() { return new _Promise((resolve, reject) => { fileCache.clear(null, err => { if (err) { reject(new PluginError('gulp-cache', `Problem clearing the cache: ${err.message}`)); return; } resolve(); }); }); }; module.exports = exports['default']; module.exports = plugin; //# sourceMappingURL=index.js.map