123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const Cache = require("../Cache");
- /** @typedef {import("webpack-sources").Source} Source */
- /** @typedef {import("../Cache").Etag} Etag */
- /** @typedef {import("../Compiler")} Compiler */
- /** @typedef {import("../Module")} Module */
- class MemoryWithGcCachePlugin {
- constructor({ maxGenerations }) {
- this._maxGenerations = maxGenerations;
- }
- /**
- * Apply the plugin
- * @param {Compiler} compiler the compiler instance
- * @returns {void}
- */
- apply(compiler) {
- const maxGenerations = this._maxGenerations;
- /** @type {Map<string, { etag: Etag | null, data: any }>} */
- const cache = new Map();
- /** @type {Map<string, { entry: { etag: Etag | null, data: any }, until: number }>} */
- const oldCache = new Map();
- let generation = 0;
- let cachePosition = 0;
- const logger = compiler.getInfrastructureLogger("MemoryWithGcCachePlugin");
- compiler.hooks.afterDone.tap("MemoryWithGcCachePlugin", () => {
- generation++;
- let clearedEntries = 0;
- let lastClearedIdentifier;
- for (const [identifier, entry] of oldCache) {
- if (entry.until > generation) break;
- oldCache.delete(identifier);
- if (cache.get(identifier) === undefined) {
- cache.delete(identifier);
- clearedEntries++;
- lastClearedIdentifier = identifier;
- }
- }
- if (clearedEntries > 0 || oldCache.size > 0) {
- logger.log(
- `${cache.size - oldCache.size} active entries, ${
- oldCache.size
- } recently unused cached entries${
- clearedEntries > 0
- ? `, ${clearedEntries} old unused cache entries removed e. g. ${lastClearedIdentifier}`
- : ""
- }`
- );
- }
- let i = (cache.size / maxGenerations) | 0;
- let j = cachePosition >= cache.size ? 0 : cachePosition;
- cachePosition = j + i;
- for (const [identifier, entry] of cache) {
- if (j !== 0) {
- j--;
- continue;
- }
- if (entry !== undefined) {
- // We don't delete the cache entry, but set it to undefined instead
- // This reserves the location in the data table and avoids rehashing
- // when constantly adding and removing entries.
- // It will be deleted when removed from oldCache.
- cache.set(identifier, undefined);
- oldCache.delete(identifier);
- oldCache.set(identifier, {
- entry,
- until: generation + maxGenerations
- });
- if (i-- === 0) break;
- }
- }
- });
- compiler.cache.hooks.store.tap(
- { name: "MemoryWithGcCachePlugin", stage: Cache.STAGE_MEMORY },
- (identifier, etag, data) => {
- cache.set(identifier, { etag, data });
- }
- );
- compiler.cache.hooks.get.tap(
- { name: "MemoryWithGcCachePlugin", stage: Cache.STAGE_MEMORY },
- (identifier, etag, gotHandlers) => {
- const cacheEntry = cache.get(identifier);
- if (cacheEntry === null) {
- return null;
- } else if (cacheEntry !== undefined) {
- return cacheEntry.etag === etag ? cacheEntry.data : null;
- }
- const oldCacheEntry = oldCache.get(identifier);
- if (oldCacheEntry !== undefined) {
- const cacheEntry = oldCacheEntry.entry;
- if (cacheEntry === null) {
- oldCache.delete(identifier);
- cache.set(identifier, cacheEntry);
- return null;
- } else {
- if (cacheEntry.etag !== etag) return null;
- oldCache.delete(identifier);
- cache.set(identifier, cacheEntry);
- return cacheEntry.data;
- }
- }
- gotHandlers.push((result, callback) => {
- if (result === undefined) {
- cache.set(identifier, null);
- } else {
- cache.set(identifier, { etag, data: result });
- }
- return callback();
- });
- }
- );
- compiler.cache.hooks.shutdown.tap(
- { name: "MemoryWithGcCachePlugin", stage: Cache.STAGE_MEMORY },
- () => {
- cache.clear();
- oldCache.clear();
- }
- );
- }
- }
- module.exports = MemoryWithGcCachePlugin;
|