// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; const { NativeModule } = require('internal/bootstrap_loaders'); const util = require('util'); const { decorateErrorStack } = require('internal/util'); const { getURLFromFilePath } = require('internal/url'); const vm = require('vm'); const assert = require('assert').ok; const fs = require('fs'); const internalFS = require('internal/fs'); const path = require('path'); const { internalModuleReadJSON, internalModuleStat } = process.binding('fs'); const { safeGetenv } = process.binding('util'); const internalModule = require('internal/module'); const preserveSymlinks = !!process.binding('config').preserveSymlinks; const experimentalModules = !!process.binding('config').experimentalModules; const { ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_REQUIRE_ESM } = require('internal/errors').codes; module.exports = Module; // these are below module.exports for the circular reference const internalESModule = require('internal/process/modules'); const ModuleJob = require('internal/loader/ModuleJob'); const createDynamicModule = require('internal/loader/CreateDynamicModule'); const { CHAR_UPPERCASE_A, CHAR_LOWERCASE_A, CHAR_UPPERCASE_Z, CHAR_LOWERCASE_Z, CHAR_FORWARD_SLASH, CHAR_BACKWARD_SLASH, CHAR_COLON, CHAR_DOT, CHAR_UNDERSCORE, CHAR_0, CHAR_9, } = require('internal/constants'); function stat(filename) { filename = path.toNamespacedPath(filename); const cache = stat.cache; if (cache !== null) { const result = cache.get(filename); if (result !== undefined) return result; } const result = internalModuleStat(filename); if (cache !== null) cache.set(filename, result); return result; } stat.cache = null; function updateChildren(parent, child, scan) { var children = parent && parent.children; if (children && !(scan && children.includes(child))) children.push(child); } function Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; updateChildren(parent, this, false); this.filename = null; this.loaded = false; this.children = []; } const builtinModules = Object.keys(NativeModule._source) .filter(NativeModule.nonInternalExists); Object.freeze(builtinModules); Module.builtinModules = builtinModules; Module._cache = Object.create(null); Module._pathCache = Object.create(null); Module._extensions = Object.create(null); var modulePaths = []; Module.globalPaths = []; Module.wrap = function(script) { return Module.wrapper[0] + script + Module.wrapper[1]; }; Module.wrapper = [ '(function (exports, require, module, __filename, __dirname) { ', '\n});' ]; const debug = util.debuglog('module'); Module._debug = util.deprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); // given a module name, and a list of paths to test, returns the first // matching file in the following precedence. // // require("a.") // -> a. // // require("a") // -> a // -> a. // -> a/index. // check if the directory is a package.json dir const packageMainCache = Object.create(null); function readPackage(requestPath) { const entry = packageMainCache[requestPath]; if (entry) return entry; const jsonPath = path.resolve(requestPath, 'package.json'); const json = internalModuleReadJSON(path.toNamespacedPath(jsonPath)); if (json === undefined) { return false; } try { var pkg = packageMainCache[requestPath] = JSON.parse(json).main; } catch (e) { e.path = jsonPath; e.message = 'Error parsing ' + jsonPath + ': ' + e.message; throw e; } return pkg; } function tryPackage(requestPath, exts, isMain) { var pkg = readPackage(requestPath); if (!pkg) return false; var filename = path.resolve(requestPath, pkg); return tryFile(filename, isMain) || tryExtensions(filename, exts, isMain) || tryExtensions(path.resolve(filename, 'index'), exts, isMain); } // In order to minimize unnecessary lstat() calls, // this cache is a list of known-real paths. // Set to an empty Map to reset. const realpathCache = new Map(); // check if the file exists and is not a directory // if using --preserve-symlinks and isMain is false, // keep symlinks intact, otherwise resolve to the // absolute realpath. function tryFile(requestPath, isMain) { const rc = stat(requestPath); if (preserveSymlinks && !isMain) { return rc === 0 && path.resolve(requestPath); } return rc === 0 && toRealPath(requestPath); } function toRealPath(requestPath) { return fs.realpathSync(requestPath, { [internalFS.realpathCacheKey]: realpathCache }); } // given a path, check if the file exists with any of the set extensions function tryExtensions(p, exts, isMain) { for (var i = 0; i < exts.length; i++) { const filename = tryFile(p + exts[i], isMain); if (filename) { return filename; } } return false; } var warned = false; Module._findPath = function(request, paths, isMain) { if (path.isAbsolute(request)) { paths = ['']; } else if (!paths || paths.length === 0) { return false; } var cacheKey = request + '\x00' + (paths.length === 1 ? paths[0] : paths.join('\x00')); var entry = Module._pathCache[cacheKey]; if (entry) return entry; var exts; var trailingSlash = request.length > 0 && request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH; // For each path for (var i = 0; i < paths.length; i++) { // Don't search further if path doesn't exist const curPath = paths[i]; if (curPath && stat(curPath) < 1) continue; var basePath = path.resolve(curPath, request); var filename; var rc = stat(basePath); if (!trailingSlash) { if (rc === 0) { // File. if (preserveSymlinks && !isMain) { filename = path.resolve(basePath); } else { filename = toRealPath(basePath); } } else if (rc === 1) { // Directory. if (exts === undefined) exts = Object.keys(Module._extensions); filename = tryPackage(basePath, exts, isMain); } if (!filename) { // try it with each of the extensions if (exts === undefined) exts = Object.keys(Module._extensions); filename = tryExtensions(basePath, exts, isMain); } } if (!filename && rc === 1) { // Directory. if (exts === undefined) exts = Object.keys(Module._extensions); filename = tryPackage(basePath, exts, isMain) || // try it with each of the extensions at "index" tryExtensions(path.resolve(basePath, 'index'), exts, isMain); } if (filename) { // Warn once if '.' resolved outside the module dir if (request === '.' && i > 0) { if (!warned) { warned = true; process.emitWarning( 'warning: require(\'.\') resolved outside the package ' + 'directory. This functionality is deprecated and will be removed ' + 'soon.', 'DeprecationWarning', 'DEP0019'); } } Module._pathCache[cacheKey] = filename; return filename; } } return false; }; // 'node_modules' character codes reversed var nmChars = [ 115, 101, 108, 117, 100, 111, 109, 95, 101, 100, 111, 110 ]; var nmLen = nmChars.length; if (process.platform === 'win32') { // 'from' is the __dirname of the module. Module._nodeModulePaths = function(from) { // guarantee that 'from' is absolute. from = path.resolve(from); // note: this approach *only* works when the path is guaranteed // to be absolute. Doing a fully-edge-case-correct path.split // that works on both Windows and Posix is non-trivial. // return root node_modules when path is 'D:\\'. // path.resolve will make sure from.length >=3 in Windows. if (from.charCodeAt(from.length - 1) === CHAR_BACKWARD_SLASH && from.charCodeAt(from.length - 2) === CHAR_COLON) return [from + 'node_modules']; const paths = []; var p = 0; var last = from.length; for (var i = from.length - 1; i >= 0; --i) { const code = from.charCodeAt(i); // The path segment separator check ('\' and '/') was used to get // node_modules path for every path segment. // Use colon as an extra condition since we can get node_modules // path for drive root like 'C:\node_modules' and don't need to // parse drive name. if (code === CHAR_BACKWARD_SLASH || code === CHAR_FORWARD_SLASH || code === CHAR_COLON) { if (p !== nmLen) paths.push(from.slice(0, last) + '\\node_modules'); last = i; p = 0; } else if (p !== -1) { if (nmChars[p] === code) { ++p; } else { p = -1; } } } return paths; }; } else { // posix // 'from' is the __dirname of the module. Module._nodeModulePaths = function(from) { // guarantee that 'from' is absolute. from = path.resolve(from); // Return early not only to avoid unnecessary work, but to *avoid* returning // an array of two items for a root: [ '//node_modules', '/node_modules' ] if (from === '/') return ['/node_modules']; // note: this approach *only* works when the path is guaranteed // to be absolute. Doing a fully-edge-case-correct path.split // that works on both Windows and Posix is non-trivial. const paths = []; var p = 0; var last = from.length; for (var i = from.length - 1; i >= 0; --i) { const code = from.charCodeAt(i); if (code === CHAR_FORWARD_SLASH) { if (p !== nmLen) paths.push(from.slice(0, last) + '/node_modules'); last = i; p = 0; } else if (p !== -1) { if (nmChars[p] === code) { ++p; } else { p = -1; } } } // Append /node_modules to handle root paths. paths.push('/node_modules'); return paths; }; } // 'index.' character codes var indexChars = [ 105, 110, 100, 101, 120, 46 ]; var indexLen = indexChars.length; Module._resolveLookupPaths = function(request, parent, newReturn) { if (NativeModule.nonInternalExists(request)) { debug('looking for %j in []', request); return (newReturn ? null : [request, []]); } // Check for relative path if (request.length < 2 || request.charCodeAt(0) !== CHAR_DOT || (request.charCodeAt(1) !== CHAR_DOT && request.charCodeAt(1) !== CHAR_FORWARD_SLASH)) { var paths = modulePaths; if (parent) { if (!parent.paths) paths = parent.paths = []; else paths = parent.paths.concat(paths); } // Maintain backwards compat with certain broken uses of require('.') // by putting the module's directory in front of the lookup paths. if (request === '.') { if (parent && parent.filename) { paths.unshift(path.dirname(parent.filename)); } else { paths.unshift(path.resolve(request)); } } debug('looking for %j in %j', request, paths); return (newReturn ? (paths.length > 0 ? paths : null) : [request, paths]); } // with --eval, parent.id is not set and parent.filename is null if (!parent || !parent.id || !parent.filename) { // make require('./path/to/foo') work - normally the path is taken // from realpath(__filename) but with eval there is no filename var mainPaths = ['.'].concat(Module._nodeModulePaths('.'), modulePaths); debug('looking for %j in %j', request, mainPaths); return (newReturn ? mainPaths : [request, mainPaths]); } // Is the parent an index module? // We can assume the parent has a valid extension, // as it already has been accepted as a module. const base = path.basename(parent.filename); var parentIdPath; if (base.length > indexLen) { var i = 0; for (; i < indexLen; ++i) { if (indexChars[i] !== base.charCodeAt(i)) break; } if (i === indexLen) { // We matched 'index.', let's validate the rest for (; i < base.length; ++i) { const code = base.charCodeAt(i); if (code !== CHAR_UNDERSCORE && (code < CHAR_0 || code > CHAR_9) && (code < CHAR_UPPERCASE_A || code > CHAR_UPPERCASE_Z) && (code < CHAR_LOWERCASE_A || code > CHAR_LOWERCASE_Z)) break; } if (i === base.length) { // Is an index module parentIdPath = parent.id; } else { // Not an index module parentIdPath = path.dirname(parent.id); } } else { // Not an index module parentIdPath = path.dirname(parent.id); } } else { // Not an index module parentIdPath = path.dirname(parent.id); } var id = path.resolve(parentIdPath, request); // make sure require('./path') and require('path') get distinct ids, even // when called from the toplevel js file if (parentIdPath === '.' && id.indexOf('/') === -1) { id = './' + id; } debug('RELATIVE: requested: %s set ID to: %s from %s', request, id, parent.id); var parentDir = [path.dirname(parent.filename)]; debug('looking for %j in %j', id, parentDir); return (newReturn ? parentDir : [id, parentDir]); }; // 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 `NativeModule.require()` with the // filename and return the result. // 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. Module._load = function(request, parent, isMain) { if (parent) { debug('Module._load REQUEST %s parent: %s', request, parent.id); } if (experimentalModules && isMain) { internalESModule.loaderPromise.then((loader) => { return loader.import(getURLFromFilePath(request).pathname); }) .catch((e) => { decorateErrorStack(e); console.error(e); process.exit(1); }); return; } var filename = Module._resolveFilename(request, parent, isMain); var cachedModule = Module._cache[filename]; if (cachedModule) { updateChildren(parent, cachedModule, true); return cachedModule.exports; } if (NativeModule.nonInternalExists(filename)) { debug('load native module %s', request); return NativeModule.require(filename); } // Don't call updateChildren(), Module constructor already does. var module = new Module(filename, parent); if (isMain) { process.mainModule = module; module.id = '.'; } Module._cache[filename] = module; tryModuleLoad(module, filename); return module.exports; }; function tryModuleLoad(module, filename) { var threw = true; try { module.load(filename); threw = false; } finally { if (threw) { delete Module._cache[filename]; } } } Module._resolveFilename = function(request, parent, isMain, options) { if (NativeModule.nonInternalExists(request)) { return request; } var paths; if (typeof options === 'object' && options !== null && Array.isArray(options.paths)) { const fakeParent = new Module('', null); paths = []; for (var i = 0; i < options.paths.length; i++) { const path = options.paths[i]; fakeParent.paths = Module._nodeModulePaths(path); const lookupPaths = Module._resolveLookupPaths(request, fakeParent, true); if (!paths.includes(path)) paths.push(path); for (var j = 0; j < lookupPaths.length; j++) { if (!paths.includes(lookupPaths[j])) paths.push(lookupPaths[j]); } } } else { paths = Module._resolveLookupPaths(request, parent, true); } // look up the filename first, since that's the cache key. var filename = Module._findPath(request, paths, isMain); if (!filename) { var err = new Error(`Cannot find module '${request}'`); err.code = 'MODULE_NOT_FOUND'; throw err; } return filename; }; // Given a file name, pass it to the proper extension handler. Module.prototype.load = function(filename) { debug('load %j for module %j', filename, this.id); assert(!this.loaded); this.filename = filename; this.paths = Module._nodeModulePaths(path.dirname(filename)); var extension = path.extname(filename) || '.js'; if (!Module._extensions[extension]) extension = '.js'; Module._extensions[extension](this, filename); this.loaded = true; if (experimentalModules) { const ESMLoader = internalESModule.ESMLoader; const url = getURLFromFilePath(filename); const urlString = `${url}`; const exports = this.exports; if (ESMLoader.moduleMap.has(urlString) !== true) { ESMLoader.moduleMap.set( urlString, new ModuleJob(ESMLoader, url, async () => { const ctx = createDynamicModule( ['default'], url); ctx.reflect.exports.default.set(exports); return ctx; }) ); } else { const job = ESMLoader.moduleMap.get(urlString); if (job.reflect) job.reflect.exports.default.set(exports); } } }; // Loads a module at the given file path. Returns that module's // `exports` property. Module.prototype.require = function(id) { if (typeof id !== 'string') { throw new ERR_INVALID_ARG_TYPE('id', 'string', id); } if (id === '') { throw new ERR_INVALID_ARG_VALUE('id', id, 'must be a non-empty string'); } return Module._load(id, this, /* isMain */ false); }; // Resolved path to process.argv[1] will be lazily placed here // (needed for setting breakpoint when called with --inspect-brk) var resolvedArgv; // Run the file contents in the correct scope or sandbox. Expose // the correct helper variables (require, module, exports) to // the file. // Returns exception, if any. Module.prototype._compile = function(content, filename) { content = internalModule.stripShebang(content); // create wrapper function var wrapper = Module.wrap(content); var compiledWrapper = vm.runInThisContext(wrapper, { filename: filename, lineOffset: 0, displayErrors: true }); var inspectorWrapper = null; if (process._breakFirstLine && process._eval == null) { if (!resolvedArgv) { // we enter the repl if we're not given a filename argument. if (process.argv[1]) { resolvedArgv = Module._resolveFilename(process.argv[1], null, false); } else { resolvedArgv = 'repl'; } } // Set breakpoint on module start if (filename === resolvedArgv) { delete process._breakFirstLine; inspectorWrapper = process.binding('inspector').callAndPauseOnStart; } } var dirname = path.dirname(filename); var require = internalModule.makeRequireFunction(this); var depth = internalModule.requireDepth; if (depth === 0) stat.cache = new Map(); var result; if (inspectorWrapper) { result = inspectorWrapper(compiledWrapper, this.exports, this.exports, require, this, filename, dirname); } else { result = compiledWrapper.call(this.exports, this.exports, require, this, filename, dirname); } if (depth === 0) stat.cache = null; return result; }; // Native extension for .js Module._extensions['.js'] = function(module, filename) { var content = fs.readFileSync(filename, 'utf8'); module._compile(internalModule.stripBOM(content), filename); }; // Native extension for .json Module._extensions['.json'] = function(module, filename) { var content = fs.readFileSync(filename, 'utf8'); try { module.exports = JSON.parse(internalModule.stripBOM(content)); } catch (err) { err.message = filename + ': ' + err.message; throw err; } }; //Native extension for .node Module._extensions['.node'] = function(module, filename) { return process.dlopen(module, path.toNamespacedPath(filename)); }; if (experimentalModules) { Module._extensions['.mjs'] = function(module, filename) { throw new ERR_REQUIRE_ESM(filename); }; } // bootstrap main module. Module.runMain = function() { // Load the main module--the command line argument. Module._load(process.argv[1], null, true); // Handle any nextTicks added in the first tick of the program process._tickCallback(); }; Module._initPaths = function() { const isWindows = process.platform === 'win32'; var homeDir; var nodePath; if (isWindows) { homeDir = process.env.USERPROFILE; nodePath = process.env.NODE_PATH; } else { homeDir = safeGetenv('HOME'); nodePath = safeGetenv('NODE_PATH'); } // $PREFIX/lib/node, where $PREFIX is the root of the Node.js installation. var prefixDir; // process.execPath is $PREFIX/bin/node except on Windows where it is // $PREFIX\node.exe. if (isWindows) { prefixDir = path.resolve(process.execPath, '..'); } else { prefixDir = path.resolve(process.execPath, '..', '..'); } var paths = [path.resolve(prefixDir, 'lib', 'node')]; if (homeDir) { paths.unshift(path.resolve(homeDir, '.node_libraries')); paths.unshift(path.resolve(homeDir, '.node_modules')); } if (nodePath) { paths = nodePath.split(path.delimiter).filter(function(path) { return !!path; }).concat(paths); } modulePaths = paths; // clone as a shallow copy, for introspection. Module.globalPaths = modulePaths.slice(0); }; Module._preloadModules = function(requests) { if (!Array.isArray(requests)) return; // Preloaded modules have a dummy parent module which is deemed to exist // in the current working directory. This seeds the search path for // preloaded modules. var parent = new Module('internal/preload', null); try { parent.paths = Module._nodeModulePaths(process.cwd()); } catch (e) { if (e.code !== 'ENOENT') { throw e; } } for (var n = 0; n < requests.length; n++) parent.require(requests[n]); }; Module._initPaths(); // backwards compatibility Module.Module = Module;