diff --git a/doc/api/errors.md b/doc/api/errors.md
index 82f59190bbb9e9..6353228f444c37 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -889,6 +889,11 @@ provided.
Encoding provided to `TextDecoder()` API was not one of the
[WHATWG Supported Encodings][].
+
+### `ERR_EVAL_ESM_CANNOT_PRINT`
+
+`--print` cannot be used with ESM input.
+
### `ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE`
diff --git a/doc/api/esm.md b/doc/api/esm.md
index 1c1317b6df7cb7..30534a5cdaa49b 100644
--- a/doc/api/esm.md
+++ b/doc/api/esm.md
@@ -36,8 +36,8 @@ initial input, or when referenced by `import` statements within ES module code:
* Files ending in `.js` when the nearest parent `package.json` file contains a
top-level field `"type"` with a value of `"module"`.
-* Strings passed in as an argument to `--eval` or `--print`, or piped to
- `node` via `STDIN`, with the flag `--input-type=module`.
+* Strings passed in as an argument to `--eval`, or piped to `node` via `STDIN`,
+ with the flag `--input-type=module`.
Node.js will treat as CommonJS all other forms of input, such as `.js` files
where the nearest parent `package.json` file contains no top-level `"type"`
@@ -52,8 +52,8 @@ or when referenced by `import` statements within ES module code:
* Files ending in `.js` when the nearest parent `package.json` file contains a
top-level field `"type"` with a value of `"commonjs"`.
-* Strings passed in as an argument to `--eval` or `--print`, or piped to
- `node` via `STDIN`, with the flag `--input-type=commonjs`.
+* Strings passed in as an argument to `--eval` or `--print`, or piped to `node`
+ via `STDIN`, with the flag `--input-type=commonjs`.
### `package.json` `"type"` field
@@ -159,9 +159,9 @@ package scope:
### `--input-type` flag
-Strings passed in as an argument to `--eval` or `--print` (or `-e` or `-p`), or
-piped to `node` via `STDIN`, will be treated as ES modules when the
-`--input-type=module` flag is set.
+Strings passed in as an argument to `--eval` (or `-e`), or piped to `node` via
+`STDIN`, will be treated as ES modules when the `--input-type=module` flag is
+set.
```sh
node --input-type=module --eval "import { sep } from 'path'; console.log(sep);"
@@ -1076,6 +1076,32 @@ node --experimental-wasm-modules index.mjs
would provide the exports interface for the instantiation of `module.wasm`.
+## Experimental Top-Level `await`
+
+When the `--experimental-top-level-await` flag is provided, `await` may be used
+in the top level (outside of async functions) within modules. This implements
+the [ECMAScript Top-Level `await` proposal][].
+
+Assuming an `a.mjs` with
+
+
+```js
+export const five = await Promise.resolve(5);
+```
+
+And a `b.mjs` with
+
+```js
+import { five } from './a.mjs';
+
+console.log(five); // Logs `5`
+```
+
+```bash
+node b.mjs # fails
+node --experimental-top-level-await b.mjs # works
+```
+
## Experimental Loaders
**Note: This API is currently being redesigned and will still change.**
@@ -1779,6 +1805,7 @@ success!
[Conditional Exports]: #esm_conditional_exports
[Dynamic `import()`]: https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports
[ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
+[ECMAScript Top-Level `await` proposal]: https://github.com/tc39/proposal-top-level-await/
[ES Module Integration Proposal for Web Assembly]: https://github.com/webassembly/esm-integration
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
[Terminology]: #esm_terminology
diff --git a/doc/api/vm.md b/doc/api/vm.md
index 56e779cfb87be6..a6cd7a08831ae7 100644
--- a/doc/api/vm.md
+++ b/doc/api/vm.md
@@ -402,7 +402,10 @@ support is planned.
```js
const vm = require('vm');
-const contextifiedObject = vm.createContext({ secret: 42 });
+const contextifiedObject = vm.createContext({
+ secret: 42,
+ print: console.log,
+});
(async () => {
// Step 1
@@ -418,6 +421,7 @@ const contextifiedObject = vm.createContext({ secret: 42 });
const bar = new vm.SourceTextModule(`
import s from 'foo';
s;
+ print(s);
`, { context: contextifiedObject });
// Step 2
@@ -460,16 +464,11 @@ const contextifiedObject = vm.createContext({ secret: 42 });
// Step 3
//
- // Evaluate the Module. The evaluate() method returns a Promise with a single
- // property "result" that contains the result of the very last statement
- // executed in the Module. In the case of `bar`, it is `s;`, which refers to
- // the default export of the `foo` module, the `secret` we set in the
- // beginning to 42.
+ // Evaluate the Module. The evaluate() method returns a promise which will
+ // resolve after the module has finished evaluating.
- const { result } = await bar.evaluate();
-
- console.log(result);
// Prints 42.
+ await bar.evaluate();
})();
```
@@ -512,17 +511,14 @@ in the ECMAScript specification.
Evaluate the module.
-This must be called after the module has been linked; otherwise it will
-throw an error. It could be called also when the module has already been
-evaluated, in which case it will do one of the following two things:
-
-* return `undefined` if the initial evaluation ended in success (`module.status`
- is `'evaluated'`)
-* rethrow the same exception the initial evaluation threw if the initial
- evaluation ended in an error (`module.status` is `'errored'`)
+This must be called after the module has been linked; otherwise it will reject.
+It could be called also when the module has already been evaluated, in which
+case it will either do nothing if the initial evaluation ended in success
+(`module.status` is `'evaluated'`) or it will re-throw the exception that the
+initial evaluation resulted in (`module.status` is `'errored'`).
This method cannot be called while the module is being evaluated
-(`module.status` is `'evaluating'`) to prevent infinite recursion.
+(`module.status` is `'evaluating'`).
Corresponds to the [Evaluate() concrete method][] field of [Cyclic Module
Record][]s in the ECMAScript specification.
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index c4e9fb3d59b4a3..b60643ba3b9e27 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -805,6 +805,7 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) {
}, TypeError);
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
RangeError);
+E('ERR_EVAL_ESM_CANNOT_PRINT', '--print cannot be used with ESM input', Error);
E('ERR_FALSY_VALUE_REJECTION', function(reason) {
this.reason = reason;
return 'Promise was rejected with falsy value';
diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js
index be5868553fa8df..135c541813d49c 100644
--- a/lib/internal/modules/esm/loader.js
+++ b/lib/internal/modules/esm/loader.js
@@ -167,10 +167,9 @@ class Loader {
};
const job = new ModuleJob(this, url, evalInstance, false, false);
this.moduleMap.set(url, job);
- const { module, result } = await job.run();
+ const { module } = await job.run();
return {
namespace: module.getNamespace(),
- result
};
}
diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js
index 3b79ac2df5ceef..b00a4fb2a58a7f 100644
--- a/lib/internal/modules/esm/module_job.js
+++ b/lib/internal/modules/esm/module_job.js
@@ -31,21 +31,26 @@ class ModuleJob {
this.isMain = isMain;
this.inspectBrk = inspectBrk;
- // This is a Promise<{ module, reflect }>, whose fields will be copied
- // onto `this` by `link()` below once it has been resolved.
- this.modulePromise = moduleProvider.call(loader, url, isMain);
this.module = undefined;
+ // Expose the promise to the ModuleWrap directly for linking below.
+ // `this.module` is also filled in below.
+ this.modulePromise = moduleProvider.call(loader, url, isMain);
// Wait for the ModuleWrap instance being linked with all dependencies.
const link = async () => {
this.module = await this.modulePromise;
assert(this.module instanceof ModuleWrap);
+ // Explicitly keeping track of dependency jobs is needed in order
+ // to flatten out the dependency graph below in `_instantiate()`,
+ // so that circular dependencies can't cause a deadlock by two of
+ // these `link` callbacks depending on each other.
const dependencyJobs = [];
const promises = this.module.link(async (specifier) => {
const jobPromise = this.loader.getModuleJob(specifier, url);
dependencyJobs.push(jobPromise);
- return (await jobPromise).modulePromise;
+ const job = await jobPromise;
+ return job.modulePromise;
});
if (promises !== undefined)
@@ -59,25 +64,20 @@ class ModuleJob {
// 'unhandled rejection' warnings.
this.linked.catch(noop);
- // instantiated == deep dependency jobs wrappers instantiated,
- // module wrapper instantiated
+ // instantiated == deep dependency jobs wrappers are instantiated,
+ // and module wrapper is instantiated.
this.instantiated = undefined;
}
- async instantiate() {
- if (!this.instantiated) {
- return this.instantiated = this._instantiate();
+ instantiate() {
+ if (this.instantiated === undefined) {
+ this.instantiated = this._instantiate();
}
- await this.instantiated;
- return this.module;
+ return this.instantiated;
}
- // This method instantiates the module associated with this job and its
- // entire dependency graph, i.e. creates all the module namespaces and the
- // exported/imported variables.
async _instantiate() {
const jobsInGraph = new SafeSet();
-
const addJobsToDependencyGraph = async (moduleJob) => {
if (jobsInGraph.has(moduleJob)) {
return;
@@ -87,6 +87,7 @@ class ModuleJob {
return PromiseAll(dependencyJobs.map(addJobsToDependencyGraph));
};
await addJobsToDependencyGraph(this);
+
try {
if (!hasPausedEntry && this.inspectBrk) {
hasPausedEntry = true;
@@ -122,19 +123,20 @@ class ModuleJob {
}
throw e;
}
+
for (const dependencyJob of jobsInGraph) {
// Calling `this.module.instantiate()` instantiates not only the
// ModuleWrap in this module, but all modules in the graph.
dependencyJob.instantiated = resolvedPromise;
}
- return this.module;
}
async run() {
- const module = await this.instantiate();
+ await this.instantiate();
const timeout = -1;
const breakOnSigint = false;
- return { module, result: module.evaluate(timeout, breakOnSigint) };
+ await this.module.evaluate(timeout, breakOnSigint);
+ return { module: this.module };
}
}
ObjectSetPrototypeOf(ModuleJob.prototype, null);
diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js
index a6d6d2325a7f79..bbefcd47c74617 100644
--- a/lib/internal/process/execution.js
+++ b/lib/internal/process/execution.js
@@ -10,8 +10,9 @@ const path = require('path');
const {
codes: {
ERR_INVALID_ARG_TYPE,
- ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET
- }
+ ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET,
+ ERR_EVAL_ESM_CANNOT_PRINT,
+ },
} = require('internal/errors');
const {
@@ -39,6 +40,9 @@ function tryGetCwd() {
}
function evalModule(source, print) {
+ if (print) {
+ throw new ERR_EVAL_ESM_CANNOT_PRINT();
+ }
const { log, error } = require('internal/console/global');
const { decorateErrorStack } = require('internal/util');
const asyncESM = require('internal/process/esm_loader');
diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js
index 992753ef680dbb..1d26e7a601b5bc 100644
--- a/lib/internal/vm/module.js
+++ b/lib/internal/vm/module.js
@@ -219,8 +219,7 @@ class Module {
'must be one of linked, evaluated, or errored'
);
}
- const result = this[kWrap].evaluate(timeout, breakOnSigint);
- return { __proto__: null, result };
+ await this[kWrap].evaluate(timeout, breakOnSigint);
}
[customInspectSymbol](depth, options) {
diff --git a/src/module_wrap.cc b/src/module_wrap.cc
index 13dd331e6b23e1..59ef9daf727ed5 100644
--- a/src/module_wrap.cc
+++ b/src/module_wrap.cc
@@ -375,7 +375,13 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo& args) {
return;
}
- args.GetReturnValue().Set(result.ToLocalChecked());
+ // If TLA is enabled, `result` is the evaluation's promise.
+ // Otherwise, `result` is the last evaluated value of the module,
+ // which could be a promise, which would result in it being incorrectly
+ // unwrapped when the higher level code awaits the evaluation.
+ if (env->isolate_data()->options()->experimental_top_level_await) {
+ args.GetReturnValue().Set(result.ToLocalChecked());
+ }
}
void ModuleWrap::GetNamespace(const FunctionCallbackInfo& args) {
@@ -387,13 +393,17 @@ void ModuleWrap::GetNamespace(const FunctionCallbackInfo& args) {
Local module = obj->module_.Get(isolate);
switch (module->GetStatus()) {
- default:
+ case v8::Module::Status::kUninstantiated:
+ case v8::Module::Status::kInstantiating:
return env->ThrowError(
- "cannot get namespace, Module has not been instantiated");
+ "cannot get namespace, module has not been instantiated");
case v8::Module::Status::kInstantiated:
case v8::Module::Status::kEvaluating:
case v8::Module::Status::kEvaluated:
+ case v8::Module::Status::kErrored:
break;
+ default:
+ UNREACHABLE();
}
Local result = module->GetModuleNamespace();
@@ -616,19 +626,19 @@ MaybeLocal ModuleWrap::SyntheticModuleEvaluationStepsCallback(
TryCatchScope try_catch(env);
Local synthetic_evaluation_steps =
obj->synthetic_evaluation_steps_.Get(isolate);
+ obj->synthetic_evaluation_steps_.Reset();
MaybeLocal ret = synthetic_evaluation_steps->Call(context,
obj->object(), 0, nullptr);
if (ret.IsEmpty()) {
CHECK(try_catch.HasCaught());
}
- obj->synthetic_evaluation_steps_.Reset();
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
CHECK(!try_catch.Message().IsEmpty());
CHECK(!try_catch.Exception().IsEmpty());
try_catch.ReThrow();
return MaybeLocal();
}
- return ret;
+ return Undefined(isolate);
}
void ModuleWrap::SetSyntheticExport(const FunctionCallbackInfo& args) {
diff --git a/src/node_options-inl.h b/src/node_options-inl.h
index 682a1c6c47d020..4e1a12296bc77e 100644
--- a/src/node_options-inl.h
+++ b/src/node_options-inl.h
@@ -138,10 +138,9 @@ void OptionsParser::Implies(const char* from,
const char* to) {
auto it = options_.find(to);
CHECK_NE(it, options_.end());
- CHECK_EQ(it->second.type, kBoolean);
- implications_.emplace(from, Implication {
- it->second.field, true
- });
+ CHECK(it->second.type == kBoolean || it->second.type == kV8Option);
+ implications_.emplace(
+ from, Implication{it->second.type, to, it->second.field, true});
}
template
@@ -150,9 +149,8 @@ void OptionsParser::ImpliesNot(const char* from,
auto it = options_.find(to);
CHECK_NE(it, options_.end());
CHECK_EQ(it->second.type, kBoolean);
- implications_.emplace(from, Implication {
- it->second.field, false
- });
+ implications_.emplace(
+ from, Implication{it->second.type, to, it->second.field, false});
}
template
@@ -196,9 +194,11 @@ template
auto OptionsParser::Convert(
typename OptionsParser::Implication original,
ChildOptions* (Options::* get_child)()) {
- return Implication {
- Convert(original.target_field, get_child),
- original.target_value
+ return Implication{
+ original.type,
+ original.name,
+ Convert(original.target_field, get_child),
+ original.target_value,
};
}
@@ -366,19 +366,23 @@ void OptionsParser::Parse(
break;
}
- if (it == options_.end()) {
- v8_args->push_back(arg);
- continue;
- }
-
{
auto implications = implications_.equal_range(name);
for (auto it = implications.first; it != implications.second; ++it) {
- *it->second.target_field->template Lookup(options) =
- it->second.target_value;
+ if (it->second.type == kV8Option) {
+ v8_args->push_back(it->second.name);
+ } else {
+ *it->second.target_field->template Lookup(options) =
+ it->second.target_value;
+ }
}
}
+ if (it == options_.end()) {
+ v8_args->push_back(arg);
+ continue;
+ }
+
const OptionInfo& info = it->second;
std::string value;
if (info.type != kBoolean && info.type != kNoOp && info.type != kV8Option) {
diff --git a/src/node_options.cc b/src/node_options.cc
index 3b9142c19e98a8..b3e7f403b05854 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -593,6 +593,13 @@ PerIsolateOptionsParser::PerIsolateOptionsParser(
kAllowedInEnvironment);
Implies("--report-signal", "--report-on-signal");
+ AddOption("--experimental-top-level-await",
+ "enable experimental support for ECMAScript Top-Level Await",
+ &PerIsolateOptions::experimental_top_level_await);
+ AddOption("--harmony-top-level-await", "", V8Option{});
+ Implies("--experimental-top-level-await", "--harmony-top-level-await");
+ Implies("--harmony-top-level-await", "--experimental-top-level-await");
+
Insert(eop, &PerIsolateOptions::get_per_env_options);
}
diff --git a/src/node_options.h b/src/node_options.h
index 539e41e67ac6ee..bbe5617cdc832d 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -186,6 +186,7 @@ class PerIsolateOptions : public Options {
bool no_node_snapshot = false;
bool report_uncaught_exception = false;
bool report_on_signal = false;
+ bool experimental_top_level_await = false;
std::string report_signal = "SIGUSR2";
inline EnvironmentOptions* get_per_env_options();
void CheckOptions(std::vector* errors) override;
@@ -418,6 +419,8 @@ class OptionsParser {
// An implied option is composed of the information on where to store a
// specific boolean value (if another specific option is encountered).
struct Implication {
+ OptionType type;
+ std::string name;
std::shared_ptr target_field;
bool target_value;
};
diff --git a/test/es-module/test-esm-tla.mjs b/test/es-module/test-esm-tla.mjs
new file mode 100644
index 00000000000000..816f88dd80a74e
--- /dev/null
+++ b/test/es-module/test-esm-tla.mjs
@@ -0,0 +1,11 @@
+// Flags: --experimental-top-level-await
+
+import '../common/index.mjs';
+import fixtures from '../common/fixtures.js';
+import assert from 'assert';
+import { pathToFileURL } from 'url';
+
+import(pathToFileURL(fixtures.path('/es-modules/tla/parent.mjs')))
+ .then(({ default: order }) => {
+ assert.deepStrictEqual(order, ['order', 'b', 'c', 'd', 'a', 'parent']);
+ });
diff --git a/test/fixtures/es-modules/tla/a.mjs b/test/fixtures/es-modules/tla/a.mjs
new file mode 100644
index 00000000000000..c899a1c658c385
--- /dev/null
+++ b/test/fixtures/es-modules/tla/a.mjs
@@ -0,0 +1,7 @@
+import order from './order.mjs';
+
+await new Promise((resolve) => {
+ setTimeout(resolve, 200);
+});
+
+order.push('a');
diff --git a/test/fixtures/es-modules/tla/b.mjs b/test/fixtures/es-modules/tla/b.mjs
new file mode 100644
index 00000000000000..5149f5fda9361e
--- /dev/null
+++ b/test/fixtures/es-modules/tla/b.mjs
@@ -0,0 +1,3 @@
+import order from './order.mjs';
+
+order.push('b');
diff --git a/test/fixtures/es-modules/tla/c.mjs b/test/fixtures/es-modules/tla/c.mjs
new file mode 100644
index 00000000000000..ab0479f12b8eac
--- /dev/null
+++ b/test/fixtures/es-modules/tla/c.mjs
@@ -0,0 +1,3 @@
+import order from './order.mjs';
+
+order.push('c');
diff --git a/test/fixtures/es-modules/tla/d.mjs b/test/fixtures/es-modules/tla/d.mjs
new file mode 100644
index 00000000000000..4d6e7d496fc88a
--- /dev/null
+++ b/test/fixtures/es-modules/tla/d.mjs
@@ -0,0 +1,6 @@
+import order from './order.mjs';
+
+const end = Date.now() + 500;
+while (end < Date.now()) {}
+
+order.push('d');
diff --git a/test/fixtures/es-modules/tla/order.mjs b/test/fixtures/es-modules/tla/order.mjs
new file mode 100644
index 00000000000000..3258bf986eae36
--- /dev/null
+++ b/test/fixtures/es-modules/tla/order.mjs
@@ -0,0 +1 @@
+export default ['order'];
diff --git a/test/fixtures/es-modules/tla/parent.mjs b/test/fixtures/es-modules/tla/parent.mjs
new file mode 100644
index 00000000000000..ed17e102267696
--- /dev/null
+++ b/test/fixtures/es-modules/tla/parent.mjs
@@ -0,0 +1,9 @@
+import order from './order.mjs';
+import './a.mjs';
+import './b.mjs';
+import './c.mjs';
+import './d.mjs';
+
+order.push('parent');
+
+export default order;
diff --git a/test/parallel/test-cli-eval.js b/test/parallel/test-cli-eval.js
index 65d31642055627..cbe0a09887f8eb 100644
--- a/test/parallel/test-cli-eval.js
+++ b/test/parallel/test-cli-eval.js
@@ -245,9 +245,10 @@ child.exec(
// Assert that "42\n" is written to stdout with print option.
child.exec(
`${nodejs} ${execOptions} --print --eval "42"`,
- common.mustCall((err, stdout) => {
- assert.ifError(err);
- assert.strictEqual(stdout, '42\n');
+ common.mustCall((err, stdout, stderr) => {
+ assert.ok(err);
+ assert.strictEqual(stdout, '');
+ assert.ok(stderr.includes('--print cannot be used with ESM input'));
}));
// Assert that error is written to stderr on invalid input.
diff --git a/test/parallel/test-internal-module-wrap.js b/test/parallel/test-internal-module-wrap.js
index b0cc3a1cf02d8a..09cccba76de004 100644
--- a/test/parallel/test-internal-module-wrap.js
+++ b/test/parallel/test-internal-module-wrap.js
@@ -10,7 +10,7 @@ const { ModuleWrap } = internalBinding('module_wrap');
const { getPromiseDetails, isPromise } = internalBinding('util');
const setTimeoutAsync = require('util').promisify(setTimeout);
-const foo = new ModuleWrap('foo', undefined, 'export * from "bar"; 6;', 0, 0);
+const foo = new ModuleWrap('foo', undefined, 'export * from "bar";', 0, 0);
const bar = new ModuleWrap('bar', undefined, 'export const five = 5', 0, 0);
(async () => {
@@ -24,6 +24,6 @@ const bar = new ModuleWrap('bar', undefined, 'export const five = 5', 0, 0);
foo.instantiate();
- assert.strictEqual(await foo.evaluate(-1, false), 6);
+ assert.strictEqual(await foo.evaluate(-1, false), undefined);
assert.strictEqual(foo.getNamespace().five, 5);
})();
diff --git a/test/parallel/test-vm-module-basic.js b/test/parallel/test-vm-module-basic.js
index 26751fd0b58e31..c842ac01fb8d93 100644
--- a/test/parallel/test-vm-module-basic.js
+++ b/test/parallel/test-vm-module-basic.js
@@ -26,26 +26,26 @@ const util = require('util');
assert.strictEqual(m.status, 'unlinked');
await m.link(common.mustNotCall());
assert.strictEqual(m.status, 'linked');
- const result = await m.evaluate();
+ assert.strictEqual(await m.evaluate(), undefined);
assert.strictEqual(m.status, 'evaluated');
- assert.strictEqual(Object.getPrototypeOf(result), null);
assert.deepStrictEqual(context, {
foo: 'bar',
baz: 'bar',
typeofProcess: 'undefined'
});
- assert.strictEqual(result.result, 'function');
}());
(async () => {
- const m = new SourceTextModule(
- 'global.vmResult = "foo"; Object.prototype.toString.call(process);'
- );
+ const m = new SourceTextModule(`
+ global.vmResultFoo = "foo";
+ global.vmResultTypeofProcess = Object.prototype.toString.call(process);
+ `);
await m.link(common.mustNotCall());
- const { result } = await m.evaluate();
- assert.strictEqual(global.vmResult, 'foo');
- assert.strictEqual(result, '[object process]');
- delete global.vmResult;
+ await m.evaluate();
+ assert.strictEqual(global.vmResultFoo, 'foo');
+ assert.strictEqual(global.vmResultTypeofProcess, '[object process]');
+ delete global.vmResultFoo;
+ delete global.vmResultTypeofProcess;
})();
(async () => {
diff --git a/test/parallel/test-vm-module-dynamic-import.js b/test/parallel/test-vm-module-dynamic-import.js
index 70229b3897874b..9d04cf96e6f979 100644
--- a/test/parallel/test-vm-module-dynamic-import.js
+++ b/test/parallel/test-vm-module-dynamic-import.js
@@ -5,19 +5,23 @@
const common = require('../common');
const assert = require('assert');
-const { Script, SourceTextModule, createContext } = require('vm');
+const { Script, SourceTextModule } = require('vm');
async function testNoCallback() {
- const m = new SourceTextModule('import("foo")', { context: createContext() });
+ const m = new SourceTextModule(`
+ globalThis.importResult = import("foo");
+ globalThis.importResult.catch(() => {});
+ `);
await m.link(common.mustNotCall());
- const { result } = await m.evaluate();
+ await m.evaluate();
let threw = false;
try {
- await result;
+ await globalThis.importResult;
} catch (err) {
threw = true;
assert.strictEqual(err.code, 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING');
}
+ delete globalThis.importResult;
assert(threw);
}
@@ -40,7 +44,7 @@ async function test() {
}
{
- const m = new SourceTextModule('import("foo")', {
+ const m = new SourceTextModule('globalThis.fooResult = import("foo")', {
importModuleDynamically: common.mustCall((specifier, wrap) => {
assert.strictEqual(specifier, 'foo');
assert.strictEqual(wrap, m);
@@ -48,24 +52,26 @@ async function test() {
}),
});
await m.link(common.mustNotCall());
- const { result } = await m.evaluate();
- assert.strictEqual(foo.namespace, await result);
+ await m.evaluate();
+ assert.strictEqual(foo.namespace, await globalThis.fooResult);
+ delete globalThis.fooResult;
}
}
async function testInvalid() {
- const m = new SourceTextModule('import("foo")', {
+ const m = new SourceTextModule('globalThis.fooResult = import("foo")', {
importModuleDynamically: common.mustCall((specifier, wrap) => {
return 5;
}),
});
await m.link(common.mustNotCall());
- const { result } = await m.evaluate();
- await result.catch(common.mustCall((e) => {
+ await m.evaluate();
+ await globalThis.fooResult.catch(common.mustCall((e) => {
assert.strictEqual(e.code, 'ERR_VM_MODULE_NOT_MODULE');
}));
+ delete globalThis.fooResult;
- const s = new Script('import("foo")', {
+ const s = new Script('import("bar")', {
importModuleDynamically: common.mustCall((specifier, wrap) => {
return undefined;
}),
diff --git a/test/parallel/test-vm-module-dynamic-namespace.js b/test/parallel/test-vm-module-dynamic-namespace.js
index 987b21f2ec1151..84937cd78de4af 100644
--- a/test/parallel/test-vm-module-dynamic-namespace.js
+++ b/test/parallel/test-vm-module-dynamic-namespace.js
@@ -9,22 +9,18 @@ const assert = require('assert');
const { types } = require('util');
const { SourceTextModule } = require('vm');
-async function getNamespace() {
- const m = new SourceTextModule('');
- await m.link(() => 0);
- await m.evaluate();
- return m.namespace;
-}
-
(async () => {
- const namespace = await getNamespace();
- const m = new SourceTextModule('export const A = "A"; import("");', {
- importModuleDynamically: common.mustCall((specifier, wrap) => {
- return namespace;
- })
+ const m = new SourceTextModule('globalThis.importResult = import("");', {
+ importModuleDynamically: common.mustCall(async (specifier, wrap) => {
+ const m = new SourceTextModule('');
+ await m.link(() => 0);
+ await m.evaluate();
+ return m.namespace;
+ }),
});
await m.link(() => 0);
- const { result } = await m.evaluate();
- const ns = await result;
+ await m.evaluate();
+ const ns = await globalThis.importResult;
+ delete globalThis.importResult;
assert.ok(types.isModuleNamespaceObject(ns));
})().then(common.mustCall());
diff --git a/test/parallel/test-vm-module-errors.js b/test/parallel/test-vm-module-errors.js
index d14296b1e9ebde..fee243f101113b 100644
--- a/test/parallel/test-vm-module-errors.js
+++ b/test/parallel/test-vm-module-errors.js
@@ -182,13 +182,11 @@ async function checkExecution() {
await (async () => {
const m = new SourceTextModule('throw new Error();');
await m.link(common.mustNotCall());
- const evaluatePromise = m.evaluate();
- await evaluatePromise.catch(() => {});
- assert.strictEqual(m.status, 'errored');
try {
- await evaluatePromise;
+ await m.evaluate();
} catch (err) {
assert.strictEqual(m.error, err);
+ assert.strictEqual(m.status, 'errored');
return;
}
assert.fail('Missing expected exception');
diff --git a/test/parallel/test-vm-module-import-meta.js b/test/parallel/test-vm-module-import-meta.js
index 0f86f2fa646914..baf6c5b37d1b3b 100644
--- a/test/parallel/test-vm-module-import-meta.js
+++ b/test/parallel/test-vm-module-import-meta.js
@@ -7,14 +7,16 @@ const assert = require('assert');
const { SourceTextModule } = require('vm');
async function testBasic() {
- const m = new SourceTextModule('import.meta;', {
+ const m = new SourceTextModule('globalThis.importMeta = import.meta;', {
initializeImportMeta: common.mustCall((meta, module) => {
assert.strictEqual(module, m);
meta.prop = 42;
})
});
await m.link(common.mustNotCall());
- const { result } = await m.evaluate();
+ await m.evaluate();
+ const result = globalThis.importMeta;
+ delete globalThis.importMeta;
assert.strictEqual(typeof result, 'object');
assert.strictEqual(Object.getPrototypeOf(result), null);
assert.strictEqual(result.prop, 42);
diff --git a/test/parallel/test-vm-module-link.js b/test/parallel/test-vm-module-link.js
index 56443e3e1a73bc..48f06439b6dacf 100644
--- a/test/parallel/test-vm-module-link.js
+++ b/test/parallel/test-vm-module-link.js
@@ -13,7 +13,8 @@ async function simple() {
const foo = new SourceTextModule('export default 5;');
await foo.link(common.mustNotCall());
- const bar = new SourceTextModule('import five from "foo"; five');
+ globalThis.fiveResult = undefined;
+ const bar = new SourceTextModule('import five from "foo"; fiveResult = five');
assert.deepStrictEqual(bar.dependencySpecifiers, ['foo']);
@@ -23,7 +24,9 @@ async function simple() {
return foo;
}));
- assert.strictEqual((await bar.evaluate()).result, 5);
+ await bar.evaluate();
+ assert.strictEqual(globalThis.fiveResult, 5);
+ delete globalThis.fiveResult;
}
async function depth() {
diff --git a/test/parallel/test-vm-module-reevaluate.js b/test/parallel/test-vm-module-reevaluate.js
index 4767541dd24c37..6208f140ffab88 100644
--- a/test/parallel/test-vm-module-reevaluate.js
+++ b/test/parallel/test-vm-module-reevaluate.js
@@ -12,11 +12,16 @@ const finished = common.mustCall();
(async function main() {
{
- const m = new SourceTextModule('1');
+ globalThis.count = 0;
+ const m = new SourceTextModule('count += 1;');
await m.link(common.mustNotCall());
- assert.strictEqual((await m.evaluate()).result, 1);
- assert.strictEqual((await m.evaluate()).result, undefined);
- assert.strictEqual((await m.evaluate()).result, undefined);
+ assert.strictEqual(await m.evaluate(), undefined);
+ assert.strictEqual(globalThis.count, 1);
+ assert.strictEqual(await m.evaluate(), undefined);
+ assert.strictEqual(globalThis.count, 1);
+ assert.strictEqual(await m.evaluate(), undefined);
+ assert.strictEqual(globalThis.count, 1);
+ delete globalThis.count;
}
{
diff --git a/test/parallel/test-vm-module-synthetic.js b/test/parallel/test-vm-module-synthetic.js
index 83a78f2f3f5ba2..a3ca6630f7eab4 100644
--- a/test/parallel/test-vm-module-synthetic.js
+++ b/test/parallel/test-vm-module-synthetic.js
@@ -2,7 +2,7 @@
// Flags: --experimental-vm-modules
-require('../common');
+const common = require('../common');
const { SyntheticModule, SourceTextModule } = require('vm');
const assert = require('assert');
@@ -26,6 +26,17 @@ const assert = require('assert');
assert.strictEqual(m.namespace.getX(), 42);
}
+ {
+ const s = new SyntheticModule([], () => {
+ const p = Promise.reject();
+ p.catch(() => {});
+ return p;
+ });
+
+ await s.link(common.mustNotCall());
+ assert.strictEqual(await s.evaluate(), undefined);
+ }
+
for (const invalidName of [1, Symbol.iterator, {}, [], null, true, 0]) {
const s = new SyntheticModule([], () => {});
await s.link(() => {});