Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

module: self referential modules in repl or -r #32261

Closed
wants to merge 1 commit into from
Closed
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: 27 additions & 7 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {

const {
ArrayIsArray,
ArrayPrototypeJoin,
Error,
JSONParse,
Map,
Expand Down Expand Up @@ -424,7 +425,23 @@ function findLongestRegisteredExtension(filename) {
return '.js';
}

function trySelfParentPath(parent) {
if (!parent) return false;

if (parent.filename) {
return parent.filename;
} else if (parent.id === '<repl>' || parent.id === 'internal/preload') {
try {
return process.cwd() + path.sep;
} catch {
return false;
}
}
}

function trySelf(parentPath, request) {
if (!parentPath) return false;
guybedford marked this conversation as resolved.
Show resolved Hide resolved

const { data: pkg, path: basePath } = readPackageScope(parentPath) || {};
if (!pkg || pkg.exports === undefined) return false;
if (typeof pkg.name !== 'string') return false;
Expand Down Expand Up @@ -1055,13 +1072,16 @@ Module._resolveFilename = function(request, parent, isMain, options) {
}
}
}
const filename = trySelf(parent.filename, request);
if (filename) {
const cacheKey = request + '\x00' +
(paths.length === 1 ? paths[0] : paths.join('\x00'));
Module._pathCache[cacheKey] = filename;
return filename;
}
}

// Try module self resoultion first
const parentPath = trySelfParentPath(parent);
const selfResolved = trySelf(parentPath, request);
if (selfResolved) {
const cacheKey = request + '\x00' +
(paths.length === 1 ? paths[0] : ArrayPrototypeJoin(paths, '\x00'));
dnlup marked this conversation as resolved.
Show resolved Hide resolved
Module._pathCache[cacheKey] = selfResolved;
return selfResolved;
}

// Look up the filename first, since that's the cache key.
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use strict';

// This is needed to avoid cycles in esm/resolve <-> cjs/loader
require('internal/modules/cjs/loader');

const {
FunctionPrototypeBind,
ObjectSetPrototypeOf,
Expand Down
3 changes: 2 additions & 1 deletion lib/internal/modules/esm/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const {
} = require('fs');
const { getOptionValue } = require('internal/options');
const { sep, relative } = require('path');
const { Module: CJSModule } = require('internal/modules/cjs/loader');
const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const typeFlag = getOptionValue('--input-type');
Expand All @@ -49,11 +48,13 @@ const {
ERR_UNSUPPORTED_DIR_IMPORT,
ERR_UNSUPPORTED_ESM_URL_SCHEME,
} = require('internal/errors').codes;
const { Module: CJSModule } = require('internal/modules/cjs/loader');

const packageJsonReader = require('internal/modules/package_json_reader');
const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import']);
const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS);


function getConditionsSet(conditions) {
if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) {
if (!ArrayIsArray(conditions)) {
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/self_ref_module/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use strict'

module.exports = 'Self resolution working';

13 changes: 13 additions & 0 deletions test/fixtures/self_ref_module/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "self_ref",
"version": "1.0.0",
"description": "",
"main": "index.js",
"exports": "./index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
20 changes: 20 additions & 0 deletions test/parallel/test-preload-self-referential.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

const common = require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const { exec } = require('child_process');

const nodeBinary = process.argv[0];

if (!common.isMainThread)
common.skip('process.chdir is not available in Workers');

const selfRefModule = fixtures.path('self_ref_module');
const fixtureA = fixtures.path('printA.js');

exec(`"${nodeBinary}" -r self_ref "${fixtureA}"`, { cwd: selfRefModule },
(err, stdout, stderr) => {
assert.ifError(err);
assert.strictEqual(stdout, 'A\n');
});
25 changes: 25 additions & 0 deletions test/parallel/test-repl-require-self-referential.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

const common = require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const { spawn } = require('child_process');

if (!common.isMainThread)
common.skip('process.chdir is not available in Workers');

const selfRefModule = fixtures.path('self_ref_module');
const child = spawn(process.execPath,
['--interactive'],
{ cwd: selfRefModule }
);
let output = '';
child.stdout.on('data', (chunk) => output += chunk);
child.on('exit', common.mustCall(() => {
const results = output.replace(/^> /mg, '').split('\n').slice(2);
assert.deepStrictEqual(results, [ "'Self resolution working'", '' ]);
}));

child.stdin.write('require("self_ref");\n');
child.stdin.write('.exit');
child.stdin.end();