2019-01-26 02:21:45 +08:00
|
|
|
/*
|
|
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
|
|
Author Tobias Koppers @sokra
|
|
|
|
*/
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/** @typedef {import("../Compiler")} Compiler */
|
|
|
|
|
|
|
|
class IdleFileCachePlugin {
|
|
|
|
constructor(strategy, idleTimeout, idleTimeoutForInitialStore) {
|
|
|
|
this.strategy = strategy;
|
|
|
|
this.idleTimeout = idleTimeout;
|
|
|
|
this.idleTimeoutForInitialStore = idleTimeoutForInitialStore;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {Compiler} compiler Webpack compiler
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
apply(compiler) {
|
|
|
|
const strategy = this.strategy;
|
|
|
|
const idleTimeout = this.idleTimeout;
|
|
|
|
const idleTimeoutForInitialStore = Math.min(
|
|
|
|
idleTimeout,
|
|
|
|
this.idleTimeoutForInitialStore
|
|
|
|
);
|
|
|
|
|
|
|
|
const resolvedPromise = Promise.resolve();
|
|
|
|
|
|
|
|
/** @type {Map<string, () => Promise>} */
|
|
|
|
const pendingIdleTasks = new Map();
|
|
|
|
|
|
|
|
compiler.cache.hooks.store.tap(
|
|
|
|
"IdleFileCachePlugin",
|
|
|
|
(identifier, etag, data) => {
|
|
|
|
pendingIdleTasks.set(identifier, () =>
|
|
|
|
strategy.store(identifier, etag, data)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
compiler.cache.hooks.get.tapPromise(
|
|
|
|
"IdleFileCachePlugin",
|
|
|
|
(identifier, etag, gotHandlers) => {
|
|
|
|
return strategy.restore(identifier, etag).then(cacheEntry => {
|
|
|
|
if (cacheEntry === undefined) {
|
|
|
|
gotHandlers.push((result, callback) => {
|
|
|
|
if (result !== undefined) {
|
|
|
|
pendingIdleTasks.set(identifier, () =>
|
|
|
|
strategy.store(identifier, etag, result)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return cacheEntry;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
compiler.cache.hooks.shutdown.tapPromise("IdleFileCachePlugin", () => {
|
|
|
|
if (idleTimer) {
|
|
|
|
clearTimeout(idleTimer);
|
|
|
|
idleTimer = undefined;
|
|
|
|
}
|
|
|
|
isIdle = false;
|
|
|
|
const promises = Array.from(pendingIdleTasks.values()).map(fn => fn());
|
|
|
|
pendingIdleTasks.clear();
|
|
|
|
if (currentIdlePromise !== undefined) promises.push(currentIdlePromise);
|
|
|
|
let promise = Promise.all(promises);
|
|
|
|
return promise.then(() => strategy.afterAllStored());
|
|
|
|
});
|
|
|
|
|
|
|
|
let currentIdlePromise;
|
|
|
|
let isIdle = false;
|
|
|
|
let isInitialStore = true;
|
|
|
|
const processIdleTasks = () => {
|
|
|
|
if (isIdle) {
|
|
|
|
if (pendingIdleTasks.size > 0) {
|
|
|
|
const promises = [];
|
|
|
|
const maxTime = Date.now() + 100;
|
|
|
|
let maxCount = 100;
|
|
|
|
for (const [filename, factory] of pendingIdleTasks) {
|
|
|
|
pendingIdleTasks.delete(filename);
|
|
|
|
promises.push(factory());
|
|
|
|
if (maxCount-- <= 0 || Date.now() > maxTime) break;
|
|
|
|
}
|
|
|
|
currentIdlePromise = Promise.all(promises).then(() => {
|
|
|
|
currentIdlePromise = undefined;
|
|
|
|
});
|
|
|
|
currentIdlePromise.then(() => {
|
|
|
|
// Allow to exit the process inbetween
|
|
|
|
setTimeout(processIdleTasks, 0).unref();
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
currentIdlePromise = strategy.afterAllStored();
|
|
|
|
isInitialStore = false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let idleTimer = undefined;
|
|
|
|
compiler.cache.hooks.beginIdle.tap("IdleFileCachePlugin", () => {
|
2019-02-06 22:37:11 +08:00
|
|
|
idleTimer = setTimeout(
|
|
|
|
() => {
|
|
|
|
idleTimer = undefined;
|
|
|
|
isIdle = true;
|
|
|
|
resolvedPromise.then(processIdleTasks);
|
|
|
|
},
|
|
|
|
isInitialStore ? idleTimeoutForInitialStore : idleTimeout
|
|
|
|
);
|
2019-01-26 02:21:45 +08:00
|
|
|
idleTimer.unref();
|
|
|
|
});
|
|
|
|
compiler.cache.hooks.endIdle.tap("IdleFileCachePlugin", () => {
|
|
|
|
if (idleTimer) {
|
|
|
|
clearTimeout(idleTimer);
|
|
|
|
idleTimer = undefined;
|
|
|
|
}
|
|
|
|
isIdle = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = IdleFileCachePlugin;
|