diff --git a/lib/module.js b/lib/module.js index fb233d6c4541fe..ee1a1fb10c0342 100644 --- a/lib/module.js +++ b/lib/module.js @@ -103,20 +103,30 @@ function tryPackage(requestPath, exts, isMain) { if (!pkg) return false; var filename = path.resolve(requestPath, pkg); + return readPackagePath(filename, exts, isMain); +} + +function readPackagePath(filename, exts, isMain) { return tryFile(filename, isMain) || tryExtensions(filename, exts, isMain) || tryExtensions(path.resolve(filename, 'index'), exts, isMain); } +function readFilePath(requestPath, isMain) { + if (isMain) { + return fs.realpathSync(requestPath); + } + return path.resolve(requestPath); +} + // check if the file exists and is not a directory // resolve to the absolute realpath if running main module, // otherwise resolve to absolute while keeping symlinks intact. function tryFile(requestPath, isMain) { const rc = stat(requestPath); - if (isMain) { - return rc === 0 && fs.realpathSync(requestPath); + if (rc === 0) { + return readFilePath(requestPath, isMain); } - return rc === 0 && path.resolve(requestPath); } // given a path check a the file exists with any of the set extensions @@ -131,6 +141,34 @@ function tryExtensions(p, exts, isMain) { return false; } +function tryFindPath(basePath, exts, isMain, isAbsolute) { + var filename; + + if (isAbsolute) { + const rc = stat(basePath); + if (rc === 0) { // File. + filename = readFilePath(basePath, isMain); + } else if (rc === 1) { // Directory. + filename = tryPackage(basePath, exts, isMain); + } + + if (!filename) { + filename = tryExtensions(basePath, exts, isMain); + } + } + + if (!filename) { + filename = tryPackage(basePath, exts, isMain); + } + + if (!filename) { + // try it with each of the extensions at "index" + filename = tryExtensions(path.resolve(basePath, 'index'), exts, isMain); + } + + return filename; +} + var warned = false; Module._findPath = function(request, paths, isMain) { if (path.isAbsolute(request)) { @@ -144,51 +182,25 @@ Module._findPath = function(request, paths, isMain) { return Module._pathCache[cacheKey]; } - var exts; + const exts = Object.keys(Module._extensions); const trailingSlash = request.length > 0 && request.charCodeAt(request.length - 1) === 47/*/*/; + const isAbsolute = !trailingSlash; // For each path for (var i = 0; i < paths.length; i++) { // Don't search further if path doesn't exist - const curPath = paths[i]; + var curPath = paths[i]; if (curPath && stat(curPath) < 1) continue; - var basePath = path.resolve(curPath, request); - var filename; - - if (!trailingSlash) { - const rc = stat(basePath); - if (rc === 0) { // File. - if (!isMain) { - filename = path.resolve(basePath); - } else { - filename = fs.realpathSync(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) { - if (exts === undefined) - exts = Object.keys(Module._extensions); - filename = tryPackage(basePath, exts, isMain); - } + var basePath = path.resolve(curPath, request); + var filename = tryFindPath(basePath, exts, isMain, isAbsolute); if (!filename) { - // try it with each of the extensions at "index" - if (exts === undefined) - exts = Object.keys(Module._extensions); - filename = tryExtensions(path.resolve(basePath, 'index'), exts, isMain); + // If _basePath is a symlink, use the real path rather than the path of + // the symlink as cache key. + basePath = path.resolve(curPath, '_' + request); + filename = tryFindPath(basePath, exts, true, isAbsolute); } if (filename) { @@ -427,6 +439,7 @@ Module._resolveFilename = function(request, parent, isMain) { } var resolvedModule = Module._resolveLookupPaths(request, parent); + var id = resolvedModule[0]; var paths = resolvedModule[1]; diff --git a/test/fixtures/module-require-real-path/index.js b/test/fixtures/module-require-real-path/index.js new file mode 100644 index 00000000000000..4cb8e20ddc6f42 --- /dev/null +++ b/test/fixtures/module-require-real-path/index.js @@ -0,0 +1 @@ +exports.internalRequire = require; diff --git a/test/fixtures/module-require-real-path/node_modules/link-module/index.js b/test/fixtures/module-require-real-path/node_modules/link-module/index.js new file mode 100644 index 00000000000000..c45ea24398672a --- /dev/null +++ b/test/fixtures/module-require-real-path/node_modules/link-module/index.js @@ -0,0 +1 @@ +exports.dirname = __dirname; diff --git a/test/fixtures/module-require-real-path/real-module/index.js b/test/fixtures/module-require-real-path/real-module/index.js new file mode 100644 index 00000000000000..c45ea24398672a --- /dev/null +++ b/test/fixtures/module-require-real-path/real-module/index.js @@ -0,0 +1 @@ +exports.dirname = __dirname; diff --git a/test/fixtures/module-require-real-path/real-module/nested/index.js b/test/fixtures/module-require-real-path/real-module/nested/index.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/parallel/test-require-real-path.js b/test/parallel/test-require-real-path.js new file mode 100644 index 00000000000000..e21759b1a0fdd5 --- /dev/null +++ b/test/parallel/test-require-real-path.js @@ -0,0 +1,49 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const realModuleSymlinkPath = path.join(common.fixturesDir, + '/module-require-real-path/node_modules/_real-module'); + +const linkModuleSymlinkPath = path.join(common.fixturesDir, + '/module-require-real-path/node_modules/_link-module'); + +const localRealModulePath = path.join(common.fixturesDir, + '/module-require-real-path/real-module'); + +// module-require-real-path exports its require function. That way we can test +// how modules get resolved **from** its. +const _require = require( + path.join(common.fixturesDir, '/module-require-real-path') +).internalRequire; + +process.on('exit', function() { + fs.unlinkSync(realModuleSymlinkPath); + fs.unlinkSync(linkModuleSymlinkPath); +}); + +fs.symlinkSync('../real-module', realModuleSymlinkPath); +fs.symlinkSync('../real-module', linkModuleSymlinkPath); + +assert.equal(_require('./real-module'), _require('real-module')); + +// Ensure that _-prefixed symlinks are being required properly when requesting a +// nested file. +assert.equal(_require('./real-module'), _require('real-module/index.js')); +assert.equal( + _require('./real-module/nested/index.js'), + _require('real-module/nested/index.js') +); +assert.equal( + _require('./real-module/nested'), + _require('real-module/nested') +); + +assert.equal(_require('./real-module/index.js'), _require('real-module')); +assert.equal(_require('real-module').dirname, localRealModulePath); + +// When required directly with the _-prefix, resolve to path of symlink. +assert.notEqual(_require('./real-module'), _require('_link-module')); +assert.equal(_require('_link-module').dirname, linkModuleSymlinkPath);