Created
July 27, 2018 00:55
-
-
Save mikesamuel/2627b4ce7d1b52a4bdcd7f8471722fba to your computer and use it in GitHub Desktop.
Node CJS module resolver hooks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js | |
index 33d8907e4f..6df7c32efd 100644 | |
--- a/lib/internal/modules/cjs/loader.js | |
+++ b/lib/internal/modules/cjs/loader.js | |
@@ -46,6 +46,9 @@ const preserveSymlinks = !!process.binding('config').preserveSymlinks; | |
const preserveSymlinksMain = !!process.binding('config').preserveSymlinksMain; | |
const experimentalModules = !!process.binding('config').experimentalModules; | |
+const { defineProperties, hasOwnProperty } = Object; | |
+const { apply } = Reflect; | |
+ | |
const { | |
ERR_INVALID_ARG_TYPE, | |
ERR_INVALID_ARG_VALUE, | |
@@ -628,6 +631,8 @@ Module.prototype.load = function(filename) { | |
}; | |
+var requireModuleHook; | |
+ | |
// Loads a module at the given file path. Returns that module's | |
// `exports` property. | |
Module.prototype.require = function(id) { | |
@@ -638,6 +643,33 @@ Module.prototype.require = function(id) { | |
throw new ERR_INVALID_ARG_VALUE('id', id, | |
'must be a non-empty string'); | |
} | |
+ | |
+ if (requireModuleHook === undefined) { // On first module load. | |
+ requireModuleHook = null; // Do not invoke bootstrap to find the hook. | |
+ var hookid = process.binding('config').cjsUserLoader; | |
+ if (hookid && typeof hookid === 'string') { | |
+ var hook = this.require(hookid); | |
+ if (typeof hook === 'function') { | |
+ requireModuleHook = hook; | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (typeof requireModuleHook === 'function') { | |
+ var isBuiltin = apply(hasOwnProperty, NativeModule._source, [id]); | |
+ id = requireModuleHook( | |
+ this.filename, this.id, id, | |
+ (req, opts) => Module._resolveFilename(req, this, false, opts), | |
+ isBuiltin); | |
+ | |
+ 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); | |
}; | |
diff --git a/src/node.cc b/src/node.cc | |
index 71b130a63f..efd683dc64 100644 | |
--- a/src/node.cc | |
+++ b/src/node.cc | |
@@ -262,6 +262,11 @@ bool config_experimental_repl_await = false; | |
// that is used by lib/internal/bootstrap/node.js | |
std::string config_userland_loader; // NOLINT(runtime/string) | |
+// Set in node.cc by ParseArgs when --cjs-loader is used. | |
+// Used in node_config.cc to set a constant on process.binding('config') | |
+// that is used by lib/internal/bootstrap/node.js | |
+std::string config_cjs_userland_loader; // NOLINT(runtime/string) | |
+ | |
// Set by ParseArgs when --pending-deprecation or NODE_PENDING_DEPRECATION | |
// is used. | |
bool config_pending_deprecation = false; | |
@@ -3656,6 +3661,7 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env, | |
static const char* whitelist[] = { | |
// Node options, sorted in `node --help` order for ease of comparison. | |
+ "--cjs-loader", | |
"--enable-fips", | |
"--experimental-modules", | |
"--experimental-repl-await", | |
@@ -3873,6 +3879,14 @@ static void ParseArgs(int* argc, | |
} | |
args_consumed += 1; | |
config_userland_loader = module; | |
+ } else if (strcmp(arg, "--cjs-loader") == 0) { | |
+ const char* module = argv[index + 1]; | |
+ if (module == nullptr) { | |
+ fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); | |
+ exit(9); | |
+ } | |
+ args_consumed += 1; | |
+ config_cjs_userland_loader = module; | |
} else if (strcmp(arg, "--prof-process") == 0) { | |
prof_process = true; | |
short_circuit = true; | |
diff --git a/src/node_config.cc b/src/node_config.cc | |
index 603d55491a..2bc9d7af69 100644 | |
--- a/src/node_config.cc | |
+++ b/src/node_config.cc | |
@@ -88,6 +88,16 @@ static void Initialize(Local<Object> target, | |
} | |
} | |
+ if (!config_cjs_userland_loader.empty()) { | |
+ target->DefineOwnProperty( | |
+ context, | |
+ FIXED_ONE_BYTE_STRING(isolate, "cjsUserLoader"), | |
+ String::NewFromUtf8(isolate, | |
+ config_cjs_userland_loader.data(), | |
+ v8::NewStringType::kNormal).ToLocalChecked(), | |
+ ReadOnly).FromJust(); | |
+ } | |
+ | |
if (config_experimental_vm_modules) | |
READONLY_BOOLEAN_PROPERTY("experimentalVMModules"); | |
diff --git a/src/node_internals.h b/src/node_internals.h | |
index 1ba56458b1..e0c43e4bfa 100644 | |
--- a/src/node_internals.h | |
+++ b/src/node_internals.h | |
@@ -198,6 +198,11 @@ extern bool config_experimental_repl_await; | |
// that is used by lib/internal/bootstrap/node.js | |
extern std::string config_userland_loader; | |
+// Set in node.cc by ParseArgs when --cjs-loader is used. | |
+// Used in node_config.cc to set a constant on process.binding('config') | |
+// that is used by lib/internal/bootstrap/node.js | |
+extern std::string config_cjs_userland_loader; | |
+ | |
// Set in node.cc by ParseArgs when --expose-internals or --expose_internals is | |
// used. | |
// Used in node_config.cc to set a constant on process.binding('config') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment