From c2b01881dcb3bf302f9d83157e719cc5240a9042 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 7 Mar 2018 02:30:18 +0800 Subject: lib: restructure cjs and esm loaders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create `lib/internal/modules` and restructure the module loaders to make the purpose of those files clearer. Also make it clear in the code that the object exported by `lib/internal/modules/cjs/loader.js` is `CJSModule` instead of the ambiguous `Module`. Before: ``` lib ├── ... ├── internal │ ├── loaders │ │ ├── CreateDynamicModule.js │ │ ├── DefaultResolve.js │ │ ├── Loader.js │ │ ├── ModuleJob.js │ │ ├── ModuleMap.js │ │ └── Translators.js │ └── module.js └── module.js ``` After: ``` lib ├── ... ├── internal │ ├── ... │ └── modules │ ├── cjs │ │ ├── helpers.js │ │ └── loader.js │ └── esm │ ├── CreateDynamicModule.js │ ├── DefaultResolve.js │ ├── Loader.js │ ├── ModuleJob.js │ ├── ModuleMap.js │ └── Translators.js └── module.js # deleted in this commit to work with git file mode ``` PR-URL: https://github.com/nodejs/node/pull/19177 Refs: https://github.com/nodejs/node/pull/19112 Reviewed-By: Gus Caplan Reviewed-By: Matteo Collina Reviewed-By: Benjamin Gruenbaum --- lib/module.js | 780 ---------------------------------------------------------- 1 file changed, 780 deletions(-) delete mode 100644 lib/module.js (limited to 'lib/module.js') diff --git a/lib/module.js b/lib/module.js deleted file mode 100644 index 0b94cb84c6..0000000000 --- a/lib/module.js +++ /dev/null @@ -1,780 +0,0 @@ -// 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; - if (!trailingSlash) { - trailingSlash = /(?:^|\/)\.?\.$/.test(request); - } - - // 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); - } - } - - 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. - // try it with each of the extensions at "index" - if (exts === undefined) - exts = Object.keys(Module._extensions); - filename = tryPackage(basePath, exts, isMain); - if (!filename) { - filename = 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; -- cgit v1.2.1