UNPKG

@speedy/require-cache

Version:

Speed up Node load time by caching resolved module paths to avoid module resolution and refetching each time the application is loaded.

145 lines (144 loc) 4.91 kB
Object.defineProperty(exports, "__esModule", { value: true }); const nodeModule = require("module"); const path_1 = require("path"); const fs_extra_1 = require("fs-extra"); const node_core_1 = require("@speedy/node-core"); const MODULES_PATH = `node_modules${path_1.sep}`; /** * Speed up Node load time by caching resolved module paths to avoid Node refetching * and resolving the modules each time the application is loaded. * * The first time the application loads, a cache of resolved file paths is saved in the file system. * * @example * import { RequireCache } from "@speedy/require-cache"; * new RequireCache().start(); * * import * as stylelint from "stylelint"; * import * as _ from "lodash"; * * @class RequireCache */ class RequireCache { /** * Creates an instance of RequireCache. * @param {Partial<CacheOptions>} [options] */ constructor(options) { this.isCacheModified = false; this.logger = new node_core_1.Logger("Require Cache"); this.resolveFileNameOriginal = nodeModule._resolveFilename; this.cwd = process.cwd(); this.filesLookUp = {}; this._options = { readOnlyMode: false, cacheKiller: 0, cacheFilePath: path_1.resolve("./.cache/speedy-require-cache.json") }; this._isEnabled = false; this._stats = { cacheHit: 0, cacheMiss: 0, notCached: 0 }; if (!options || !options.cacheKiller) { this._options.cacheKiller = node_core_1.packageMeta.getVersion(); } this._options = Object.assign({}, this._options, options); } /** * Start caching of module locations. * * @returns {RequireCache} */ start() { this._isEnabled = true; nodeModule._resolveFilename = this.resolveFilenameOptimized.bind(this); process.once("exit", () => this.save()); let cacheFile; try { cacheFile = fs_extra_1.readJsonSync(this._options.cacheFilePath); } catch (error) { return this; } const isKillerTimestamp = cacheFile.cacheKiller.toString().indexOf(".") === -1; if (!cacheFile || (isKillerTimestamp && cacheFile.cacheKiller < new Date().getTime() / 1000) || (!isKillerTimestamp && cacheFile.cacheKiller !== this._options.cacheKiller)) { return this; } this.filesLookUp = cacheFile.paths; return this; } /** Stop caching of the modules locations. */ stop() { this._isEnabled = false; nodeModule._resolveFilename = this.resolveFileNameOriginal; this.save(); } /** Deletes the cache file. */ reset() { fs_extra_1.removeSync(this._options.cacheFilePath); } /** Saves cached paths to file. */ save() { if (!this.isCacheModified) { this.logger.debug(this.save.name, "Save exited. Cache has not been modified"); return; } if (this._options.readOnlyMode) { this.logger.debug(this.save.name, "Save exited. Cache is in 'ReadOnly' mode"); return; } const cacheFile = { cacheKiller: this._options.cacheKiller, paths: this.filesLookUp }; const { cacheHit, cacheMiss } = this._stats; this.logger.debug(this.save.name, `Trying to saving cache, Path: ${this._options.cacheFilePath}, cacheHit: ${cacheHit}, cacheMiss: ${cacheMiss}`); fs_extra_1.ensureFileSync(this._options.cacheFilePath); fs_extra_1.writeJsonSync(this._options.cacheFilePath, cacheFile); this.logger.debug(this.save.name, `Saved cached successfully.`); } /** * Whether or not the cache is currently enabled. * * @readonly * @type {boolean} */ get isEnabled() { return this._isEnabled; } /** * Caching effectiveness statistics. * * @readonly * @type {CacheStats} */ get stats() { return this._stats; } resolveFilenameOptimized(path, parentModule) { const key = this.getCacheKey(parentModule.id, path); const cachedPath = this.filesLookUp[key]; if (cachedPath) { this._stats.cacheHit++; return cachedPath; } const filename = this.resolveFileNameOriginal.apply(nodeModule, arguments); if (filename.indexOf("node_modules") > -1) { this.filesLookUp[key] = filename; this._stats.cacheMiss++; this.isCacheModified = true; } else { this._stats.notCached++; } return filename; } getCacheKey(path, filename) { return `${path_1.relative(this.cwd, path).replace(MODULES_PATH, "").replace(/\\/g, "/")}:${filename}`; } } exports.RequireCache = RequireCache;