From 1c786891306201180436a12bf4b0e74db5782bad Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Thu, 7 Sep 2023 23:04:50 -0700 Subject: [PATCH] cjs/loader.js WIP --- lib/internal/modules/cjs/loader.js | 91 +++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index e8f84aed3b6468..548ac548bace19 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -155,6 +155,11 @@ let requireDepth = 0; let isPreloading = false; let statCache = null; +/** + * Our internal implementation of `require`. + * @param {Module} module Parent module of what is being required + * @param {string} id Specifier of the child module being imported + */ function internalRequire(module, id) { validateString(id, 'id'); if (id === '') { @@ -169,6 +174,10 @@ function internalRequire(module, id) { } } +/** + * Get a path's properties, using an in-memory cache to minimize lookups. + * @param {string} filename Absolute path to the file + */ function stat(filename) { filename = path.toNamespacedPath(filename); if (statCache !== null) { @@ -195,24 +204,45 @@ ObjectDefineProperty(Module, '_stat', { configurable: true, }); +/** + * Update the parent's children array with the child module. + * @param {Module} parent Module requiring the children + * @param {Module} child Module being required + * @param {boolean} scan Add the child to the parent's children if not already present + */ function updateChildren(parent, child, scan) { const children = parent?.children; if (children && !(scan && ArrayPrototypeIncludes(children, child))) { ArrayPrototypePush(children, child); } } +/** + * Tell the watch mode that a module was required. + * @param {string} filename Absolute path of the module + */ function reportModuleToWatchMode(filename) { if (shouldReportRequiredModules() && process.send) { process.send({ 'watch:require': [filename] }); } } +/** + * Tell the watch mode that a module was not found. + * @param {string} basePath The absolute path that errored + * @param {string[]} extensions The extensions that were tried + */ function reportModuleNotFoundToWatchMode(basePath, extensions) { if (shouldReportRequiredModules() && process.send) { process.send({ 'watch:require': ArrayPrototypeMap(extensions, (ext) => path.resolve(`${basePath}${ext}`)) }); } } +/** @type {Map} */ const moduleParentCache = new SafeWeakMap(); +/** + * Create a new module instance. + * @param {string} id + * @param {Module} parent + */ function Module(id = '', parent) { this.id = id; this.path = path.dirname(id); @@ -235,16 +265,24 @@ function Module(id = '', parent) { this[require_private_symbol] = internalRequire; } +/** @type {Record} */ Module._cache = { __proto__: null }; +/** @type {Record} */ Module._pathCache = { __proto__: null }; +/** @type {Record void>} */ Module._extensions = { __proto__: null }; +/** @type {string[]} */ let modulePaths = []; +/** @type {string[]} */ Module.globalPaths = []; let patched = false; -// eslint-disable-next-line func-style -let wrap = function(script) { +/** + * Add the CommonJS wrapper around a module's source code. + * @param {string} script Module source code + */ +let wrap = function(script) { // eslint-disable-line func-style return Module.wrapper[0] + script + Module.wrapper[1]; }; @@ -295,10 +333,17 @@ const isPreloadingDesc = { get() { return isPreloading; } }; ObjectDefineProperty(Module.prototype, 'isPreloading', isPreloadingDesc); ObjectDefineProperty(BuiltinModule.prototype, 'isPreloading', isPreloadingDesc); +/** + * Get the parent of the current module from our cache. + */ function getModuleParent() { return moduleParentCache.get(this); } +/** + * Set the parent of the current module in our cache. + * @param {Module} value + */ function setModuleParent(value) { moduleParentCache.set(this, value); } @@ -325,7 +370,10 @@ ObjectDefineProperty(Module.prototype, 'parent', { Module._debug = pendingDeprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); Module.isBuiltin = BuiltinModule.isBuiltin; -// This function is called during pre-execution, before any user code is run. +/** + * Prepare to run CommonJS code. + * This function is called during pre-execution, before any user code is run. + */ function initializeCJS() { // This need to be done at runtime in case --expose-internals is set. const builtinModules = BuiltinModule.getCanBeRequiredByUsersWithoutSchemeList(); @@ -373,6 +421,11 @@ ObjectDefineProperty(Module, '_readPackage', { configurable: true, }); +/** + * Get the nearest parent package.json file from a given path. + * Return the package.json data and the path to the package.json file, or false. + * @param {string} checkPath The path to start searching from. + */ function readPackageScope(checkPath) { const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep); let separatorIndex; @@ -397,6 +450,13 @@ function readPackageScope(checkPath) { return false; } +/** + * Try to load a specifier as a package. + * @param {string} requestPath The path to what we are trying to load + * @param {string[]} exts File extensions to try appending in order to resolve the file + * @param {boolean} isMain Whether the file is the main entry point of the app + * @param {string} originalPath The specifier passed to `require` + */ function tryPackage(requestPath, exts, isMain, originalPath) { const pkg = _readPackage(requestPath).main; @@ -553,9 +613,10 @@ function resolveExports(nmPath, request) { } /** - * @param {string} request a relative or absolute file path - * @param {Array} paths file system directories to search as file paths - * @param {boolean} isMain if the request is the main app entry point + * Get the absolute path to a module. + * @param {string} request Relative or absolute file path + * @param {Array} paths Folders to search as file paths + * @param {boolean} isMain Whether the request is the main app entry point * @returns {string | false} */ Module._findPath = function(request, paths, isMain) { @@ -851,13 +912,17 @@ function getExportsForCircularRequire(module) { return module.exports; } -// Check the cache for the requested file. -// 1. If a module already exists in the cache: return its exports object. -// 2. If the module is native: call -// `BuiltinModule.prototype.compileForPublicLoader()` and return the exports. -// 3. Otherwise, create a new module for the file and save it to the cache. -// Then have it load the file contents before returning its exports -// object. +/** + * Load a module from cache if it exists, otherwise create a new module instance. + * 1. If a module already exists in the cache: return its exports object. + * 2. If the module is native: call + * `BuiltinModule.prototype.compileForPublicLoader()` and return the exports. + * 3. Otherwise, create a new module for the file and save it to the cache. + * Then have it load the file contents before returning its exports object. + * @param {string} request Specifier of module to load via `require` + * @param {string} parent Absolute path of the module importing the child + * @param {boolean} isMain Whether the module is the main entry point + */ Module._load = function(request, parent, isMain) { let relResolveCacheIdentifier; if (parent) {