Skip to content
Merged
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
30 changes: 30 additions & 0 deletions doc/api/vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,36 @@ to disallow any changes to it.
Corresponds to the `[[RequestedModules]]` field of [Cyclic Module Record][]s in
the ECMAScript specification.
### `sourceTextModule.hasAsyncGraph()`
<!-- YAML
added: REPLACEME
-->
* Returns: {boolean}
Iterates over the dependency graph and returns `true` if any module in its
dependencies or this module itself contains top-level `await` expressions,
otherwise returns `false`.
The search may be slow if the graph is big enough.
This requires the module to be instantiated first. If the module is not
instantiated yet, an error will be thrown.
### `sourceTextModule.hasTopLevelAwait()`
<!-- YAML
added: REPLACEME
-->
* Returns: {boolean}
Returns whether the module itself contains any top-level `await` expressions.
This corresponds to the field `[[HasTLA]]` in [Cyclic Module Record][] in the
ECMAScript specification.
### `sourceTextModule.instantiate()`
<!-- YAML
Expand Down
13 changes: 13 additions & 0 deletions lib/internal/vm/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,19 @@ class SourceTextModule extends Module {
return super.error;
}

hasAsyncGraph() {
validateThisInternalField(this, kWrap, 'SourceTextModule');
if (this[kWrap].getStatus() < kInstantiated) {
throw new ERR_VM_MODULE_STATUS('must be instantiated');
}
return this[kWrap].hasAsyncGraph;
}

hasTopLevelAwait() {
validateThisInternalField(this, kWrap, 'SourceTextModule');
return this[kWrap].hasTopLevelAwait;
}

createCachedData() {
const { status } = this;
if (status === 'evaluating' ||
Expand Down
67 changes: 67 additions & 0 deletions test/parallel/test-vm-module-hasasyncgraph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict';

// Flags: --experimental-vm-modules

require('../common');

const assert = require('assert');

const { SourceTextModule } = require('vm');
const test = require('node:test');

test('module is not instantiated yet', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;
`);
assert.throws(() => foo.hasAsyncGraph(), {
code: 'ERR_VM_MODULE_STATUS',
});
});

test('simple module with top-level await', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;

await 0;
`);
foo.linkRequests([]);
foo.instantiate();

assert.strictEqual(foo.hasAsyncGraph(), true);
});

test('simple module with non top-level await', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;

export async function f() {
await 0;
}
`);
foo.linkRequests([]);
foo.instantiate();

assert.strictEqual(foo.hasAsyncGraph(), false);
});

test('module with a dependency containing top-level await', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;

await 0;
`);
foo.linkRequests([]);

const bar = new SourceTextModule(`
export { foo } from 'foo';
`);
bar.linkRequests([foo]);
bar.instantiate();

assert.strictEqual(foo.hasAsyncGraph(), true);
assert.strictEqual(bar.hasAsyncGraph(), true);
});
59 changes: 59 additions & 0 deletions test/parallel/test-vm-module-hastoplevelawait.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

// Flags: --experimental-vm-modules

require('../common');

const assert = require('assert');

const { SourceTextModule } = require('vm');
const test = require('node:test');

test('simple module', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;
`);
assert.strictEqual(foo.hasTopLevelAwait(), false);
});

test('simple module with top-level await', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;

await 0;
`);
assert.strictEqual(foo.hasTopLevelAwait(), true);
});

test('simple module with non top-level await', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;

export async function f() {
await 0;
}
`);
assert.strictEqual(foo.hasTopLevelAwait(), false);
});

test('module with a dependency containing top-level await', () => {
const foo = new SourceTextModule(`
export const foo = 4
export default 5;

await 0;
`);
foo.linkRequests([]);

const bar = new SourceTextModule(`
export { foo } from 'foo';
`);
bar.linkRequests([foo]);
bar.instantiate();

assert.strictEqual(foo.hasTopLevelAwait(), true);
assert.strictEqual(bar.hasTopLevelAwait(), false);
});
Loading