Skip to content

Commit

Permalink
cjs/loader.js WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
GeoffreyBooth committed Sep 8, 2023
1 parent 2cadb5e commit 1c78689
Showing 1 changed file with 78 additions and 13 deletions.
91 changes: 78 additions & 13 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 === '') {
Expand All @@ -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) {
Expand All @@ -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<Module, Module>} */
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);
Expand All @@ -235,16 +265,24 @@ function Module(id = '', parent) {
this[require_private_symbol] = internalRequire;
}

/** @type {Record<string, Module>} */
Module._cache = { __proto__: null };
/** @type {Record<string, string>} */
Module._pathCache = { __proto__: null };
/** @type {Record<string, (module: Module, filename: string) => 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];
};

Expand Down Expand Up @@ -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);
}
Expand All @@ -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();
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -553,9 +613,10 @@ function resolveExports(nmPath, request) {
}

/**
* @param {string} request a relative or absolute file path
* @param {Array<string>} 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<string>} 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) {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 1c78689

Please sign in to comment.