3 const NativeModule = require('native_module');
4 const util = require('util');
5 const runInThisContext = require('vm').runInThisContext;
6 const assert = require('assert').ok;
7 const fs = require('fs');
8 const path = require('path');
11 // If obj.hasOwnProperty has been overridden, then calling
12 // obj.hasOwnProperty(prop) will break.
13 // See: https://github.com/joyent/node/issues/1707
14 function hasOwnProperty(obj, prop) {
15 return Object.prototype.hasOwnProperty.call(obj, prop);
19 function Module(id, parent) {
23 if (parent && parent.children) {
24 parent.children.push(this);
31 module.exports = Module;
34 Module._pathCache = {};
35 Module._extensions = {};
37 Module.globalPaths = [];
39 Module.wrapper = NativeModule.wrapper;
40 Module.wrap = NativeModule.wrap;
41 Module._debug = util.debuglog('module');
44 // We use this alias for the preprocessor that filters it out
45 const debug = Module._debug;
48 // given a module name, and a list of paths to test, returns the first
49 // matching file in the following precedence.
59 function statPath(path) {
61 return fs.statSync(path);
66 // check if the directory is a package.json dir
67 const packageMainCache = {};
69 function readPackage(requestPath) {
70 if (hasOwnProperty(packageMainCache, requestPath)) {
71 return packageMainCache[requestPath];
75 var jsonPath = path.resolve(requestPath, 'package.json');
76 var json = fs.readFileSync(jsonPath, 'utf8');
82 var pkg = packageMainCache[requestPath] = JSON.parse(json).main;
85 e.message = 'Error parsing ' + jsonPath + ': ' + e.message;
91 function tryPackage(requestPath, exts) {
92 var pkg = readPackage(requestPath);
94 if (!pkg) return false;
96 var filename = path.resolve(requestPath, pkg);
97 return tryFile(filename, null) || tryExtensions(filename, exts) ||
98 tryExtensions(path.resolve(filename, 'index'), exts);
101 // In order to minimize unnecessary lstat() calls,
102 // this cache is a list of known-real paths.
103 // Set to an empty object to reset.
104 Module._realpathCache = {};
106 // check if the file exists and is not a directory
107 function tryFile(requestPath, stats) {
108 stats = stats || statPath(requestPath);
109 if (stats && !stats.isDirectory()) {
110 return fs.realpathSync(requestPath, Module._realpathCache);
115 // given a path check a the file exists with any of the set extensions
116 function tryExtensions(p, exts) {
117 for (var i = 0, EL = exts.length; i < EL; i++) {
118 var filename = tryFile(p + exts[i], null);
128 Module._findPath = function(request, paths) {
129 var exts = Object.keys(Module._extensions);
131 if (request.charAt(0) === '/') {
135 var trailingSlash = (request.slice(-1) === '/');
137 var cacheKey = JSON.stringify({request: request, paths: paths});
138 if (Module._pathCache[cacheKey]) {
139 return Module._pathCache[cacheKey];
143 for (var i = 0, PL = paths.length; i < PL; i++) {
144 var basePath = path.resolve(paths[i], request);
147 if (!trailingSlash) {
148 var stats = statPath(basePath);
149 // try to join the request to the path
150 filename = tryFile(basePath, stats);
152 if (!filename && stats && stats.isDirectory()) {
153 filename = tryPackage(basePath, exts);
157 // try it with each of the extensions
158 filename = tryExtensions(basePath, exts);
163 filename = tryPackage(basePath, exts);
167 // try it with each of the extensions at "index"
168 filename = tryExtensions(path.resolve(basePath, 'index'), exts);
172 Module._pathCache[cacheKey] = filename;
179 // 'from' is the __dirname of the module.
180 Module._nodeModulePaths = function(from) {
181 // guarantee that 'from' is absolute.
182 from = path.resolve(from);
184 // note: this approach *only* works when the path is guaranteed
185 // to be absolute. Doing a fully-edge-case-correct path.split
186 // that works on both Windows and Posix is non-trivial.
187 var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//;
189 var parts = from.split(splitRe);
191 for (var tip = parts.length - 1; tip >= 0; tip--) {
192 // don't search in .../node_modules/node_modules
193 if (parts[tip] === 'node_modules') continue;
194 var dir = parts.slice(0, tip + 1).concat('node_modules').join(path.sep);
202 Module._resolveLookupPaths = function(request, parent) {
203 if (NativeModule.exists(request)) {
204 return [request, []];
207 var start = request.substring(0, 2);
208 if (start !== './' && start !== '..') {
209 var paths = modulePaths;
211 if (!parent.paths) parent.paths = [];
212 paths = parent.paths.concat(paths);
214 return [request, paths];
217 // with --eval, parent.id is not set and parent.filename is null
218 if (!parent || !parent.id || !parent.filename) {
219 // make require('./path/to/foo') work - normally the path is taken
220 // from realpath(__filename) but with eval there is no filename
221 var mainPaths = ['.'].concat(modulePaths);
222 mainPaths = Module._nodeModulePaths('.').concat(mainPaths);
223 return [request, mainPaths];
226 // Is the parent an index module?
227 // We can assume the parent has a valid extension,
228 // as it already has been accepted as a module.
229 var isIndex = /^index\.\w+?$/.test(path.basename(parent.filename));
230 var parentIdPath = isIndex ? parent.id : path.dirname(parent.id);
231 var id = path.resolve(parentIdPath, request);
233 // make sure require('./path') and require('path') get distinct ids, even
234 // when called from the toplevel js file
235 if (parentIdPath === '.' && id.indexOf('/') === -1) {
239 debug('RELATIVE: requested:' + request +
240 ' set ID to: ' + id + ' from ' + parent.id);
242 return [id, [path.dirname(parent.filename)]];
246 // Check the cache for the requested file.
247 // 1. If a module already exists in the cache: return its exports object.
248 // 2. If the module is native: call `NativeModule.require()` with the
249 // filename and return the result.
250 // 3. Otherwise, create a new module for the file and save it to the cache.
251 // Then have it load the file contents before returning its exports
253 Module._load = function(request, parent, isMain) {
255 debug('Module._load REQUEST ' + (request) + ' parent: ' + parent.id);
258 var filename = Module._resolveFilename(request, parent);
260 var cachedModule = Module._cache[filename];
262 return cachedModule.exports;
265 if (NativeModule.exists(filename)) {
266 // REPL is a special case, because it needs the real require.
267 if (filename == 'repl') {
268 var replModule = new Module('repl');
269 replModule._compile(NativeModule.getSource('repl'), 'repl.js');
270 NativeModule._cache.repl = replModule;
271 return replModule.exports;
274 debug('load native module ' + request);
275 return NativeModule.require(filename);
278 var module = new Module(filename, parent);
281 process.mainModule = module;
285 Module._cache[filename] = module;
287 var hadException = true;
290 module.load(filename);
291 hadException = false;
294 delete Module._cache[filename];
298 return module.exports;
301 Module._resolveFilename = function(request, parent) {
302 if (NativeModule.exists(request)) {
306 var resolvedModule = Module._resolveLookupPaths(request, parent);
307 var id = resolvedModule[0];
308 var paths = resolvedModule[1];
310 // look up the filename first, since that's the cache key.
311 debug('looking for ' + JSON.stringify(id) +
312 ' in ' + JSON.stringify(paths));
314 var filename = Module._findPath(request, paths);
316 var err = new Error("Cannot find module '" + request + "'");
317 err.code = 'MODULE_NOT_FOUND';
324 // Given a file name, pass it to the proper extension handler.
325 Module.prototype.load = function(filename) {
326 debug('load ' + JSON.stringify(filename) +
327 ' for module ' + JSON.stringify(this.id));
329 assert(!this.loaded);
330 this.filename = filename;
331 this.paths = Module._nodeModulePaths(path.dirname(filename));
333 var extension = path.extname(filename) || '.js';
334 if (!Module._extensions[extension]) extension = '.js';
335 Module._extensions[extension](this, filename);
340 // Loads a module at the given file path. Returns that module's
341 // `exports` property.
342 Module.prototype.require = function(path) {
343 assert(path, 'missing path');
344 assert(typeof path === 'string', 'path must be a string');
345 return Module._load(path, this);
349 // Resolved path to process.argv[1] will be lazily placed here
350 // (needed for setting breakpoint when called with --debug-brk)
354 // Run the file contents in the correct scope or sandbox. Expose
355 // the correct helper variables (require, module, exports) to
357 // Returns exception, if any.
358 Module.prototype._compile = function(content, filename) {
361 content = content.replace(/^\#\!.*/, '');
363 function require(path) {
364 return self.require(path);
367 require.resolve = function(request) {
368 return Module._resolveFilename(request, self);
371 Object.defineProperty(require, 'paths', { get: function() {
372 throw new Error('require.paths is removed. Use ' +
373 'node_modules folders, or the NODE_PATH ' +
374 'environment variable instead.');
377 require.main = process.mainModule;
379 // Enable support to add extra extension types
380 require.extensions = Module._extensions;
381 require.registerExtension = function() {
382 throw new Error('require.registerExtension() removed. Use ' +
383 'require.extensions instead.');
386 require.cache = Module._cache;
388 var dirname = path.dirname(filename);
390 // create wrapper function
391 var wrapper = Module.wrap(content);
393 var compiledWrapper = runInThisContext(wrapper, { filename: filename });
394 if (global.v8debug) {
396 // we enter the repl if we're not given a filename argument.
397 if (process.argv[1]) {
398 resolvedArgv = Module._resolveFilename(process.argv[1], null);
400 resolvedArgv = 'repl';
404 // Set breakpoint on module start
405 if (filename === resolvedArgv) {
406 global.v8debug.Debug.setBreakPoint(compiledWrapper, 0, 0);
409 var args = [self.exports, require, self, filename, dirname];
410 return compiledWrapper.apply(self.exports, args);
414 function stripBOM(content) {
415 // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
416 // because the buffer-to-string conversion in `fs.readFileSync()`
417 // translates it to FEFF, the UTF-16 BOM.
418 if (content.charCodeAt(0) === 0xFEFF) {
419 content = content.slice(1);
425 // Native extension for .js
426 Module._extensions['.js'] = function(module, filename) {
427 var content = fs.readFileSync(filename, 'utf8');
428 module._compile(stripBOM(content), filename);
432 // Native extension for .json
433 Module._extensions['.json'] = function(module, filename) {
434 var content = fs.readFileSync(filename, 'utf8');
436 module.exports = JSON.parse(stripBOM(content));
438 err.message = filename + ': ' + err.message;
444 //Native extension for .node
445 Module._extensions['.node'] = process.dlopen;
448 // bootstrap main module.
449 Module.runMain = function() {
450 // Load the main module--the command line argument.
451 Module._load(process.argv[1], null, true);
452 // Handle any nextTicks added in the first tick of the program
453 process._tickCallback();
456 Module._initPaths = function() {
457 const isWindows = process.platform === 'win32';
460 var homeDir = process.env.USERPROFILE;
462 var homeDir = process.env.HOME;
465 var paths = [path.resolve(process.execPath, '..', '..', 'lib', 'node')];
468 paths.unshift(path.resolve(homeDir, '.node_libraries'));
469 paths.unshift(path.resolve(homeDir, '.node_modules'));
472 var nodePath = process.env['NODE_PATH'];
474 paths = nodePath.split(path.delimiter).concat(paths);
479 // clone as a read-only copy, for introspection.
480 Module.globalPaths = modulePaths.slice(0);
484 Module.requireRepl = function() {
485 return Module._load('repl', '.');
490 // backwards compatibility
491 Module.Module = Module;