diff --git a/lib/index.js b/lib/index.js index 3d86f6d..9b10143 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,9 +5,7 @@ "use strict"; -const fs = require("graceful-fs"); -const CachedInputFileSystem = require("./CachedInputFileSystem"); -const ResolverFactory = require("./ResolverFactory"); +const memoize = require("./util/memoize"); /** @typedef {import("./CachedInputFileSystem").BaseFileSystem} BaseFileSystem */ /** @typedef {import("./PnpPlugin").PnpApiImpl} PnpApi */ @@ -36,17 +34,28 @@ const ResolverFactory = require("./ResolverFactory"); * }} ResolveFunction */ -const nodeFileSystem = new CachedInputFileSystem(fs, 4000); +const getCachedFileSystem = memoize(() => require("./CachedInputFileSystem")); -const nodeContext = { - environments: ["node+es3+es5+process+native"], -}; +const getNodeFileSystem = memoize(() => { + const fs = require("graceful-fs"); + + const CachedInputFileSystem = getCachedFileSystem(); -const asyncResolver = ResolverFactory.createResolver({ - conditionNames: ["node"], - extensions: [".js", ".json", ".node"], - fileSystem: nodeFileSystem, + return new CachedInputFileSystem(fs, 4000); }); +const getNodeContext = memoize(() => ({ + environments: ["node+es3+es5+process+native"], +})); + +const getResolverFactory = memoize(() => require("./ResolverFactory")); + +const getAsyncResolver = memoize(() => + getResolverFactory().createResolver({ + conditionNames: ["node"], + extensions: [".js", ".json", ".node"], + fileSystem: getNodeFileSystem(), + }), +); /** * @type {ResolveFunctionAsync} @@ -65,12 +74,12 @@ const resolve = resolveContext = /** @type {ResolveContext} */ (request); request = path; path = context; - context = nodeContext; + context = getNodeContext(); } if (typeof callback !== "function") { callback = /** @type {ResolveCallback} */ (resolveContext); } - asyncResolver.resolve( + getAsyncResolver().resolve( context, path, /** @type {string} */ (request), @@ -79,12 +88,14 @@ const resolve = ); }; -const syncResolver = ResolverFactory.createResolver({ - conditionNames: ["node"], - extensions: [".js", ".json", ".node"], - useSyncFileSystemCalls: true, - fileSystem: nodeFileSystem, -}); +const getSyncResolver = memoize(() => + getResolverFactory().createResolver({ + conditionNames: ["node"], + extensions: [".js", ".json", ".node"], + useSyncFileSystemCalls: true, + fileSystem: getNodeFileSystem(), + }), +); /** * @type {ResolveFunction} @@ -100,9 +111,9 @@ const resolveSync = if (typeof context === "string") { request = path; path = context; - context = nodeContext; + context = getNodeContext(); } - return syncResolver.resolveSync( + return getSyncResolver().resolveSync( context, path, /** @type {string} */ (request), @@ -116,8 +127,8 @@ const resolveSync = * @returns {ResolveFunctionAsync} Resolver function */ function create(options) { - const resolver = ResolverFactory.createResolver({ - fileSystem: nodeFileSystem, + const resolver = getResolverFactory().createResolver({ + fileSystem: getNodeFileSystem(), ...options, }); /** @@ -133,7 +144,7 @@ function create(options) { resolveContext = /** @type {ResolveContext} */ (request); request = path; path = context; - context = nodeContext; + context = getNodeContext(); } if (typeof callback !== "function") { callback = /** @type {ResolveCallback} */ (resolveContext); @@ -153,9 +164,9 @@ function create(options) { * @returns {ResolveFunction} Resolver function */ function createSync(options) { - const resolver = ResolverFactory.createResolver({ + const resolver = getResolverFactory().createResolver({ useSyncFileSystemCalls: true, - fileSystem: nodeFileSystem, + fileSystem: getNodeFileSystem(), ...options, }); /** @@ -168,7 +179,7 @@ function createSync(options) { if (typeof context === "string") { request = path; path = context; - context = nodeContext; + context = getNodeContext(); } return resolver.resolveSync(context, path, /** @type {string} */ (request)); }; @@ -196,8 +207,12 @@ module.exports = mergeExports(resolve, { return createSync; }, }), - ResolverFactory, - CachedInputFileSystem, + get ResolverFactory() { + return getResolverFactory(); + }, + get CachedInputFileSystem() { + return getCachedFileSystem(); + }, get CloneBasenamePlugin() { return require("./CloneBasenamePlugin"); }, diff --git a/lib/util/memoize.js b/lib/util/memoize.js new file mode 100644 index 0000000..b46e252 --- /dev/null +++ b/lib/util/memoize.js @@ -0,0 +1,37 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +/** + * @template T + * @typedef {() => T} FunctionReturning + */ + +/** + * @template T + * @param {FunctionReturning} fn memorized function + * @returns {FunctionReturning} new function + */ +const memoize = (fn) => { + let cache = false; + /** @type {T | undefined} */ + let result; + return () => { + if (cache) { + return /** @type {T} */ (result); + } + + result = fn(); + cache = true; + // Allow to clean up memory for fn + // and all dependent resources + /** @type {FunctionReturning | undefined} */ + (fn) = undefined; + return /** @type {T} */ (result); + }; +}; + +module.exports = memoize; diff --git a/package.json b/package.json index de3fd7f..d57d704 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "prepare": "husky install", "lint": "yarn lint:code && yarn lint:types && yarn lint:types-test && yarn lint:special && yarn fmt:check && yarn lint:spellcheck", "lint:code": "eslint --cache .", - "lint:special": "node node_modules/tooling/lockfile-lint && node node_modules/tooling/inherit-types && node node_modules/tooling/format-file-header && node node_modules/tooling/generate-types", + "lint:special": "node node_modules/tooling/lockfile-lint && node node_modules/tooling/inherit-types && node node_modules/tooling/generate-types", "lint:types": "tsc", "lint:types-test": "tsc -p tsconfig.types.test.json", "lint:spellcheck": "cspell --no-must-find-files \"**/*.*\"", @@ -63,7 +63,7 @@ "fmt:base": "node_modules/prettier/bin/prettier.cjs --cache --ignore-unknown .", "fix": "yarn fix:code && yarn fix:special", "fix:code": "yarn lint:code --fix", - "fix:special": "node node_modules/tooling/inherit-types --write && node node_modules/tooling/format-file-header --write && node node_modules/tooling/generate-types --write", + "fix:special": "node node_modules/tooling/inherit-types --write && node node_modules/tooling/generate-types --write", "type-report": "rimraf coverage && yarn cover:types && yarn cover:report && open-cli coverage/lcov-report/index.html", "pretest": "yarn lint", "test": "yarn test:coverage",