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.
618 lines
14 KiB
618 lines
14 KiB
6 years ago
|
'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
|