Skip to content

Instantly share code, notes, and snippets.

@mikesamuel
Created July 27, 2018 00:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikesamuel/2627b4ce7d1b52a4bdcd7f8471722fba to your computer and use it in GitHub Desktop.
Save mikesamuel/2627b4ce7d1b52a4bdcd7f8471722fba to your computer and use it in GitHub Desktop.
Node CJS module resolver hooks
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