From d4aa656d575b0c1c6da3b8dc2ae5f64ff410648d Mon Sep 17 00:00:00 2001 From: Corey Farrell Date: Sat, 23 Nov 2019 00:45:10 -0500 Subject: [PATCH] module: fix dynamic import from eval This allows dynamic import to work from CLI `--eval` with or without `--input-type=module`. Fixes: https://github.com/nodejs/node/issues/30591 PR-URL: https://github.com/nodejs/node/pull/30624 Reviewed-By: Gus Caplan Reviewed-By: Yorkie Liu Reviewed-By: Ben Coe --- lib/internal/modules/esm/loader.js | 13 +++++++++++-- lib/internal/process/execution.js | 15 +++++++++++++-- test/parallel/test-cli-eval.js | 20 ++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 97f57935292396..255e5d2aba7bd8 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -25,7 +25,6 @@ const defaultResolve = require('internal/modules/esm/default_resolve'); const createDynamicModule = require( 'internal/modules/esm/create_dynamic_module'); const { translators } = require('internal/modules/esm/translators'); -const { ModuleWrap } = internalBinding('module_wrap'); const { getOptionValue } = require('internal/options'); const debug = require('internal/util/debuglog').debuglog('esm'); @@ -117,7 +116,17 @@ class Loader { source, url = pathToFileURL(`${process.cwd()}/[eval${++this.evalIndex}]`).href ) { - const evalInstance = (url) => new ModuleWrap(url, undefined, source, 0, 0); + const evalInstance = (url) => { + const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); + const module = new ModuleWrap(url, undefined, source, 0, 0); + callbackMap.set(module, { + importModuleDynamically: (specifier, { url }) => { + return this.import(specifier, url); + } + }); + + return module; + }; const job = new ModuleJob(this, url, evalInstance, false, false); this.moduleMap.set(url, job); const { module, result } = await job.run(); diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js index 44c67452f5a01f..8b15fbbae46f26 100644 --- a/lib/internal/process/execution.js +++ b/lib/internal/process/execution.js @@ -57,6 +57,7 @@ function evalModule(source, print) { function evalScript(name, body, breakFirstLine, print) { const CJSModule = require('internal/modules/cjs/loader').Module; const { kVmBreakFirstLineSymbol } = require('internal/util'); + const { pathToFileURL } = require('url'); const cwd = tryGetCwd(); const origModule = global.module; // Set e.g. when called from the REPL. @@ -64,20 +65,30 @@ function evalScript(name, body, breakFirstLine, print) { const module = new CJSModule(name); module.filename = path.join(cwd, name); module.paths = CJSModule._nodeModulePaths(cwd); + global.kVmBreakFirstLineSymbol = kVmBreakFirstLineSymbol; + global.asyncESM = require('internal/process/esm_loader'); + + const baseUrl = pathToFileURL(module.filename).href; + const script = ` global.__filename = ${JSONStringify(name)}; global.exports = exports; global.module = module; global.__dirname = __dirname; global.require = require; - const { kVmBreakFirstLineSymbol } = global; + const { kVmBreakFirstLineSymbol, asyncESM } = global; delete global.kVmBreakFirstLineSymbol; + delete global.asyncESM; return require("vm").runInThisContext( ${JSONStringify(body)}, { filename: ${JSONStringify(name)}, displayErrors: true, - [kVmBreakFirstLineSymbol]: ${!!breakFirstLine} + [kVmBreakFirstLineSymbol]: ${!!breakFirstLine}, + async importModuleDynamically (specifier) { + const loader = await asyncESM.ESMLoader; + return loader.import(specifier, ${JSONStringify(baseUrl)}); + } });\n`; const result = module._compile(script, `${name}-wrapper`); if (print) { diff --git a/test/parallel/test-cli-eval.js b/test/parallel/test-cli-eval.js index 2cece62a437d52..65d31642055627 100644 --- a/test/parallel/test-cli-eval.js +++ b/test/parallel/test-cli-eval.js @@ -283,3 +283,23 @@ child.exec( assert.ifError(err); assert.strictEqual(stdout, '.mjs file\n'); })); + + +// Assert that packages can be dynamic imported initial cwd-relative with --eval +child.exec( + `${nodejs} ${execOptions} ` + + '--eval "process.chdir(\'..\');' + + 'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"', + common.mustCall((err, stdout) => { + assert.ifError(err); + assert.strictEqual(stdout, '.mjs file\n'); + })); + +child.exec( + `${nodejs} ` + + '--eval "process.chdir(\'..\');' + + 'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"', + common.mustCall((err, stdout) => { + assert.ifError(err); + assert.strictEqual(stdout, '.mjs file\n'); + }));