From 92f446b9374481ed85be9b5639c42fd3a1d30c8c Mon Sep 17 00:00:00 2001 From: phestermcs Date: Mon, 21 Nov 2016 06:40:24 -0700 Subject: [PATCH] implement --adjacent-node-modules, so we can get symlinks that just work. --- lib/module.js | 59 +++++++++++++++++++++++++++++++++----------- src/node.cc | 10 +++++++- src/node_config.cc | 3 +++ src/node_internals.h | 1 + 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/lib/module.js b/lib/module.js index 09c4b85b9e5599..68ba455dda409e 100644 --- a/lib/module.js +++ b/lib/module.js @@ -11,6 +11,7 @@ const path = require('path'); const internalModuleReadFile = process.binding('fs').internalModuleReadFile; const internalModuleStat = process.binding('fs').internalModuleStat; const preserveSymlinks = !!process.binding('config').preserveSymlinks; +const adjacentNodeModules = !!process.binding('config').adjacentNodeModules; // If obj.hasOwnProperty has been overridden, then calling // obj.hasOwnProperty(prop) will break. @@ -120,7 +121,7 @@ const realpathCache = new Map(); // absolute realpath. function tryFile(requestPath, isMain) { const rc = stat(requestPath); - if (preserveSymlinks && !isMain) { + if (preserveSymlinks && !isMain || adjacentNodeModules) { return rc === 0 && path.resolve(requestPath); } return rc === 0 && toRealPath(requestPath); @@ -172,7 +173,7 @@ Module._findPath = function(request, paths, isMain) { const rc = stat(basePath); if (!trailingSlash) { if (rc === 0) { // File. - if (preserveSymlinks && !isMain) { + if (preserveSymlinks && !isMain || adjacentNodeModules) { filename = path.resolve(basePath); } else { filename = toRealPath(basePath); @@ -253,9 +254,22 @@ if (process.platform === 'win32') { // Use colon as an extra condition since we can get node_modules // path for dirver root like 'C:\node_modules' and don't need to // parse driver name. - if (code === 92/*\*/ || code === 47/*/*/ || code === 58/*:*/) { - if (p !== nmLen) - paths.push(from.slice(0, last) + '\\node_modules'); + if (code === 92/*\*/ || code === 47/*/*/ || code === 58/*:*/ + || (adjacentNodeModules && code === 46/*.*/)) { + if (p !== nmLen) { + var parent = from.slice(0, last); + paths.push(parent + '\\node_modules'); + + if (adjacentNodeModules) { + paths.push(parent + '.node_modules'); + if (code === 46/*.*/) + while (i > 1) { + const code = from.charCodeAt(--i); + if (code === 92/*\*/ || code === 47/*/*/ || code === 58/*:*/) + break; + } + } + } last = i; p = 0; } else if (p !== -1) { @@ -267,6 +281,9 @@ if (process.platform === 'win32') { } } + // superfluous "/.node_modules" + if (adjacentNodeModules) paths.pop(); + return paths; }; } else { // posix @@ -287,9 +304,17 @@ if (process.platform === 'win32') { var last = from.length; for (var i = from.length - 1; i >= 0; --i) { const code = from.charCodeAt(i); - if (code === 47/*/*/) { - if (p !== nmLen) - paths.push(from.slice(0, last) + '/node_modules'); + if (code === 47/*/*/ || (adjacentNodeModules && code === 46/*.*/)) { + if (p !== nmLen) { + var parent = from.slice(0, last); + paths.push(parent + '/node_modules'); + + if (adjacentNodeModules) { + paths.push(parent + '.node_modules'); + if (code === 46/*.*/) + while (i > 1 && from.charCodeAt(--i) !== 47/*/*/); + } + } last = i; p = 0; } else if (p !== -1) { @@ -301,6 +326,9 @@ if (process.platform === 'win32') { } } + // superfluous "/.node_modules" + if (adjacentNodeModules) paths.pop(); + // Append /node_modules to handle root paths. paths.push('/node_modules'); @@ -416,13 +444,16 @@ Module._load = function(request, parent, isMain) { } var filename = Module._resolveFilename(request, parent, isMain); + var doesNonInternalExist = NativeModule.nonInternalExists(filename); + var cachePath = adjacentNodeModules && !doesNonInternalExist + ? toRealPath(filename) : filename; - var cachedModule = Module._cache[filename]; + var cachedModule = Module._cache[cachePath]; if (cachedModule) { return cachedModule.exports; } - if (NativeModule.nonInternalExists(filename)) { + if (doesNonInternalExist) { debug('load native module %s', request); return NativeModule.require(filename); } @@ -434,21 +465,21 @@ Module._load = function(request, parent, isMain) { module.id = '.'; } - Module._cache[filename] = module; + Module._cache[cachePath] = module; - tryModuleLoad(module, filename); + tryModuleLoad(module, filename, cachePath); return module.exports; }; -function tryModuleLoad(module, filename) { +function tryModuleLoad(module, filename, cachePath) { var threw = true; try { module.load(filename); threw = false; } finally { if (threw) { - delete Module._cache[filename]; + delete Module._cache[cachePath]; } } } diff --git a/src/node.cc b/src/node.cc index e4c17af3ec23ed..a5ef144bb4c008 100644 --- a/src/node.cc +++ b/src/node.cc @@ -186,7 +186,7 @@ bool trace_warnings = false; // Used in node_config.cc to set a constant on process.binding('config') // that is used by lib/module.js bool config_preserve_symlinks = false; - +bool config_adjacent_node_modules = false; bool v8_initialized = false; // process-relative uptime base, initialized at start-up @@ -3702,6 +3702,8 @@ static void ParseArgs(int* argc, Revert(cve); } else if (strcmp(arg, "--preserve-symlinks") == 0) { config_preserve_symlinks = true; + } else if (strcmp(arg, "--adjacent-node-modules") == 0) { + config_adjacent_node_modules = true; } else if (strcmp(arg, "--prof-process") == 0) { prof_process = true; short_circuit = true; @@ -4213,6 +4215,12 @@ void Init(int* argc, config_preserve_symlinks = (*preserve_symlinks == '1'); } + if (auto adjacent_node_modules + = secure_getenv("NODE_ADJACENT_NODE_MODULES")) { + + config_adjacent_node_modules = (*adjacent_node_modules == '1'); + } + // Parse a few arguments which are specific to Node. int v8_argc; const char** v8_argv; diff --git a/src/node_config.cc b/src/node_config.cc index 401345f6a608be..39cb1badee5c14 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -44,6 +44,9 @@ void InitConfig(Local target, if (config_preserve_symlinks) READONLY_BOOLEAN_PROPERTY("preserveSymlinks"); + + if (config_adjacent_node_modules) + READONLY_BOOLEAN_PROPERTY("adjacentNodeModules"); } // InitConfig } // namespace node diff --git a/src/node_internals.h b/src/node_internals.h index ae284660782cfe..ff17e195e698b7 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -40,6 +40,7 @@ extern const char* openssl_config; // Used in node_config.cc to set a constant on process.binding('config') // that is used by lib/module.js extern bool config_preserve_symlinks; +extern bool config_adjacent_node_modules; // Tells whether it is safe to call v8::Isolate::GetCurrent(). extern bool v8_initialized;