Skip to content

Commit

Permalink
module: resolve _-prefixed dependencies to real path
Browse files Browse the repository at this point in the history
Since #5950, `require('some-symlink')` no longer resolves the module to its
real path, but instead uses the path of the symlink itself as a cache key.

This change allows users to resolve dependencies to their real path when
required using the _-prefix.

Example using old mechanism (pre v6.0.0):
---

node_modules/_real-module -> ../real-module
real-modules/index.js

require('./real_module') === require('real_module')

Example using new mechanism (post v6.0.0):
---

node_modules/real-module -> ../real-module
real-modules/index.js

require('./real_module') !== require('real_module')

As discussed in #3402
  • Loading branch information
alexanderGugel committed Apr 29, 2016
1 parent 3cdb506 commit 6564aae
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 39 deletions.
89 changes: 50 additions & 39 deletions lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)) {
Expand All @@ -144,52 +182,24 @@ 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];
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 _basePath is a symlink, use the real path rather than the path of the
// symlink as cache key.
const _basePath = path.resolve(curPath, '_' + request);
const basePath = path.resolve(curPath, request);

if (!filename) {
if (exts === undefined)
exts = Object.keys(Module._extensions);
filename = tryPackage(basePath, exts, isMain);
}

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);
}
const filename = tryFindPath(_basePath, exts, true, isAbsolute) ||
tryFindPath(basePath, exts, isMain, isAbsolute);

if (filename) {
// Warn once if '.' resolved outside the module dir
Expand Down Expand Up @@ -427,6 +437,7 @@ Module._resolveFilename = function(request, parent, isMain) {
}

var resolvedModule = Module._resolveLookupPaths(request, parent);

var id = resolvedModule[0];
var paths = resolvedModule[1];

Expand Down
1 change: 1 addition & 0 deletions test/fixtures/module-require-real-path/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.internalRequire = require;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.dirname = __dirname;
30 changes: 30 additions & 0 deletions test/parallel/test-require-real-path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'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 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.symlinkSync('../real-module', realModuleSymlinkPath);

assert.equal(_require('./real-module'), _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('_real-module'));
assert.equal(_require('_real-module').dirname, realModuleSymlinkPath);

0 comments on commit 6564aae

Please sign in to comment.