Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ const { BuiltinModule } = require('internal/bootstrap/realm');
const {
maybeCacheSourceMap,
} = require('internal/source_map/source_map_cache');
const { pathToFileURL, fileURLToPath, isURL } = require('internal/url');
const { pathToFileURL, fileURLToPath, isURL, URL } = require('internal/url');
const {
pendingDeprecate,
emitExperimentalWarning,
Expand Down Expand Up @@ -1923,7 +1923,7 @@ Module._extensions['.node'] = function(module, filename) {
* @param {string} filename The path to the module
* @returns {any}
*/
function createRequireFromPath(filename) {
function createRequireFromPath(filename, fileURL) {
// Allow a directory to be passed as the filename
const trailingSlash =
StringPrototypeEndsWith(filename, '/') ||
Expand All @@ -1935,6 +1935,10 @@ function createRequireFromPath(filename) {

const m = new Module(proxyPath);
m.filename = proxyPath;
if (fileURL !== undefined) {
// Save the URL if createRequire() was given a URL, to preserve search params, if any.
m[kURL] = fileURL.href;
}

m.paths = Module._nodeModulePaths(m.path);
return makeRequireFunction(m, null);
Expand All @@ -1945,28 +1949,32 @@ const createRequireError = 'must be a file URL object, file URL string, or ' +

/**
* Creates a new `require` function that can be used to load modules.
* @param {string | URL} filename The path or URL to the module context for this `require`
* @param {string | URL} filenameOrURL The path or URL to the module context for this `require`
* @throws {ERR_INVALID_ARG_VALUE} If `filename` is not a string or URL, or if it is a relative path that cannot be
* resolved to an absolute path.
* @returns {object}
*/
function createRequire(filename) {
let filepath;
function createRequire(filenameOrURL) {
let filepath, fileURL;

if (isURL(filename) ||
(typeof filename === 'string' && !path.isAbsolute(filename))) {
if (isURL(filenameOrURL) ||
(typeof filenameOrURL === 'string' && !path.isAbsolute(filenameOrURL))) {
try {
filepath = fileURLToPath(filename);
// It might be an URL, try to convert it.
// If it's a relative path, it would not parse and would be considered invalid per
// the documented contract.
fileURL = new URL(filenameOrURL);
filepath = fileURLToPath(fileURL);
} catch {
throw new ERR_INVALID_ARG_VALUE('filename', filename,
throw new ERR_INVALID_ARG_VALUE('filename', filenameOrURL,
createRequireError);
}
} else if (typeof filename !== 'string') {
throw new ERR_INVALID_ARG_VALUE('filename', filename, createRequireError);
} else if (typeof filenameOrURL !== 'string') {
throw new ERR_INVALID_ARG_VALUE('filename', filenameOrURL, createRequireError);
} else {
filepath = filename;
filepath = filenameOrURL;
}
return createRequireFromPath(filepath);
return createRequireFromPath(filepath, fileURL);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/module-hooks/create-require-with-url.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url);
require('./empty.mjs');
Empty file.
26 changes: 26 additions & 0 deletions test/module-hooks/test-module-hooks-create-require-with-url.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Verify that if URL is used to createRequire, that URL is passed to the resolve hook
// as parentURL.
import * as common from '../common/index.mjs';
import assert from 'node:assert';
import { registerHooks } from 'node:module';
import * as fixtures from '../common/fixtures.mjs';

const fixtureURL = fixtures.fileURL('module-hooks/create-require-with-url.mjs').href + '?test=1';
registerHooks({
resolve: common.mustCall((specifier, context, defaultResolve) => {
const resolved = defaultResolve(specifier, context, defaultResolve);
if (specifier.startsWith('node:')) {
return resolved;
}

if (specifier === fixtureURL) {
assert.strictEqual(context.parentURL, import.meta.url);
} else { // From the createRequire call.
assert.strictEqual(specifier, './empty.mjs');
assert.strictEqual(context.parentURL, fixtureURL);
}
return resolved;
}, 3),
});

await import(fixtureURL);
Loading