Skip to content

Commit

Permalink
doc: add top-level await syntax in vm.md
Browse files Browse the repository at this point in the history
  • Loading branch information
aduh95 committed Mar 4, 2021
1 parent 49342fe commit 7b26be5
Showing 1 changed file with 115 additions and 10 deletions.
125 changes: 115 additions & 10 deletions doc/api/vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,78 @@ This implementation lies at a lower level than the [ECMAScript Module
loader][]. There is also no way to interact with the Loader yet, though
support is planned.

```js
```mjs
import vm from 'vm';

const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
});

// Step 1
//
// Create a Module by constructing a new `vm.SourceTextModule` object. This
// parses the provided source text, throwing a `SyntaxError` if anything goes
// wrong. By default, a Module is created in the top context. But here, we
// specify `contextifiedObject` as the context this Module belongs to.
//
// Here, we attempt to obtain the default export from the module "foo", and
// put it into local binding "secret".

const bar = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });

// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// The provided linking callback (the "linker") accepts two arguments: the
// parent module (`bar` in this case) and the string that is the specifier of
// the imported module. The callback is expected to return a Module that
// corresponds to the provided specifier, with certain requirements documented
// in `module.link()`.
//
// If linking has not started for the returned Module, the same linker
// callback will be called on the returned Module.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// callback provided would never be called, however.
//
// The link() method returns a Promise that will be resolved when all the
// Promises returned by the linker resolve.
//
// Note: This is a contrived example in that the linker function creates a new
// "foo" module every time it is called. In a full-fledged module system, a
// cache would probably be used to avoid duplicated modules.

async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: referencingModule.context });

// Using `contextifiedObject` instead of `referencingModule.context`
// here would work as well.
}
throw new Error(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);

// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.

// Prints 42.
await bar.evaluate();
```

```cjs
const vm = require('vm');

const contextifiedObject = vm.createContext({
Expand Down Expand Up @@ -542,8 +613,7 @@ The identifier of the current module, as set in the constructor.

* `linker` {Function}
* `specifier` {string} The specifier of the requested module:
<!-- eslint-skip -->
```js
```mjs
import foo from 'foo';
// ^^^^^ the module specifier
```
Expand Down Expand Up @@ -673,11 +743,37 @@ Properties assigned to the `import.meta` object that are objects may
allow the module to access information outside the specified `context`. Use
`vm.runInContext()` to create objects in a specific context.
```js
const vm = require('vm');
```mjs
import vm from 'vm';
const contextifiedObject = vm.createContext({ secret: 42 });
const module = new vm.SourceTextModule(
'Object.getPrototypeOf(import.meta.prop).secret = secret;',
{
initializeImportMeta(meta) {
// Note: this object is created in the top context. As such,
// Object.getPrototypeOf(import.meta.prop) points to the
// Object.prototype in the top context rather than that in
// the contextified object.
meta.prop = {};
}
});
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {});
await module.evaluate();
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
// meta.prop = {};
// above with
// meta.prop = vm.runInContext('{}', contextifiedObject);
```
```cjs
const vm = require('vm');
const contextifiedObject = vm.createContext({ secret: 42 });
(async () => {
const module = new vm.SourceTextModule(
'Object.getPrototypeOf(import.meta.prop).secret = secret;',
Expand All @@ -693,7 +789,6 @@ const contextifiedObject = vm.createContext({ secret: 42 });
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {});
await module.evaluate();
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
Expand Down Expand Up @@ -794,17 +889,27 @@ This method is used after the module is linked to set the values of exports. If
it is called before the module is linked, an [`ERR_VM_MODULE_STATUS`][] error
will be thrown.

```js
const vm = require('vm');
```mjs
import vm from 'vm';
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.link(() => {});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
```

```cjs
const vm = require('vm');
(async () => {
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.link(() => {});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
})();
```
Expand Down

0 comments on commit 7b26be5

Please sign in to comment.