Skip to content

Commit cc7a259

Browse files
guybedfordaduh95
authored andcommitted
esm: support top-level Wasm without package type
PR-URL: #57610 Reviewed-By: Jordan Harband Reviewed-By: Geoffrey Booth Reviewed-By: Matteo Collina Reviewed-By: Antoine du Hamel
1 parent b63d63f commit cc7a259

File tree

7 files changed

+197
-1
lines changed

7 files changed

+197
-1
lines changed

doc/api/cli.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ If a file is found, its path will be passed to the
3636
point to be loaded with ECMAScript module loader, such as `--import` or
3737
[`--experimental-default-type=module`][].
3838
* The file has an `.mjs` extension.
39+
* The file has an `.mjs` or `.wasm` (with `--experimental-wasm-modules`)
40+
extension.
3941
* The file does not have a `.cjs` extension, and the nearest parent
4042
`package.json` file contains a top-level [`"type"`][] field with a value of
4143
`"module"`.

lib/internal/modules/run_main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ function shouldUseESMLoader(mainPath) {
7878

7979
// Determine the module format of the entry point.
8080
if (mainPath && StringPrototypeEndsWith(mainPath, '.mjs')) { return true; }
81+
if (mainPath && StringPrototypeEndsWith(mainPath, '.wasm')) { return true; }
8182
if (!mainPath || StringPrototypeEndsWith(mainPath, '.cjs')) { return false; }
8283

8384
if (getOptionValue('--experimental-strip-types')) {

test/es-module/test-esm-wasm.mjs

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { spawnPromisified } from '../common/index.mjs';
22
import * as fixtures from '../common/fixtures.mjs';
3-
import { strictEqual, match } from 'node:assert';
3+
import { strictEqual, match, ok, notStrictEqual } from 'node:assert';
44
import { execPath } from 'node:process';
55
import { describe, it } from 'node:test';
66

@@ -90,4 +90,177 @@ describe('ESM: WASM modules', { concurrency: !process.env.TEST_PARALLEL }, () =>
9090
match(stderr, /ExperimentalWarning/);
9191
match(stderr, /WebAssembly/);
9292
});
93+
94+
it('should support top-level execution', async () => {
95+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
96+
'--no-warnings',
97+
'--experimental-wasm-modules',
98+
fixtures.path('es-modules/top-level-wasm.wasm'),
99+
]);
100+
101+
strictEqual(stderr, '');
102+
strictEqual(stdout, '[Object: null prototype] { prop: \'hello world\' }\n');
103+
strictEqual(code, 0);
104+
});
105+
106+
// `import source` is not supported on this version of V8.
107+
it.skip('should support static source phase imports', async () => {
108+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
109+
'--no-warnings',
110+
'--experimental-wasm-modules',
111+
'--input-type=module',
112+
'--eval',
113+
[
114+
'import { strictEqual } from "node:assert";',
115+
`import * as wasmExports from ${JSON.stringify(fixtures.fileURL('es-modules/wasm-source-phase.js'))};`,
116+
'strictEqual(wasmExports.mod instanceof WebAssembly.Module, true);',
117+
'const AbstractModuleSourceProto = Object.getPrototypeOf(Object.getPrototypeOf(wasmExports.mod));',
118+
'const toStringTag = Object.getOwnPropertyDescriptor(AbstractModuleSourceProto, Symbol.toStringTag).get;',
119+
'strictEqual(toStringTag.call(wasmExports.mod), "WebAssembly.Module");',
120+
].join('\n'),
121+
]);
122+
123+
strictEqual(stderr, '');
124+
strictEqual(stdout, '');
125+
strictEqual(code, 0);
126+
});
127+
128+
// TODO: Enable this once https://github.com/nodejs/node/pull/56842 lands.
129+
it.skip('should support dynamic source phase imports', async () => {
130+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
131+
'--no-warnings',
132+
'--experimental-wasm-modules',
133+
'--input-type=module',
134+
'--eval',
135+
[
136+
'import { strictEqual } from "node:assert";',
137+
`import * as wasmExports from ${JSON.stringify(fixtures.fileURL('es-modules/wasm-source-phase.js'))};`,
138+
'strictEqual(wasmExports.mod instanceof WebAssembly.Module, true);',
139+
'strictEqual(await wasmExports.dyn("./simple.wasm") instanceof WebAssembly.Module, true);',
140+
'const AbstractModuleSourceProto = Object.getPrototypeOf(Object.getPrototypeOf(wasmExports.mod));',
141+
'const toStringTag = Object.getOwnPropertyDescriptor(AbstractModuleSourceProto, Symbol.toStringTag).get;',
142+
'strictEqual(toStringTag.call(wasmExports.mod), "WebAssembly.Module");',
143+
].join('\n'),
144+
]);
145+
146+
strictEqual(stderr, '');
147+
strictEqual(stdout, '');
148+
strictEqual(code, 0);
149+
});
150+
151+
// `import source` is not supported on this version of V8.
152+
it.skip('should not execute source phase imports', async () => {
153+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
154+
'--no-warnings',
155+
'--experimental-wasm-modules',
156+
'--input-type=module',
157+
'--eval',
158+
[
159+
'import { strictEqual } from "node:assert";',
160+
`import source mod from ${JSON.stringify(fixtures.fileURL('es-modules/unimportable.wasm'))};`,
161+
'assert.strictEqual(mod instanceof WebAssembly.Module, true);',
162+
`await assert.rejects(import(${JSON.stringify(fixtures.fileURL('es-modules/unimportable.wasm'))}));`,
163+
].join('\n'),
164+
]);
165+
166+
strictEqual(stderr, '');
167+
strictEqual(stdout, '');
168+
strictEqual(code, 0);
169+
});
170+
171+
// TODO: Enable this once https://github.com/nodejs/node/pull/56842 lands.
172+
it.skip('should not execute dynamic source phase imports', async () => {
173+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
174+
'--no-warnings',
175+
'--experimental-wasm-modules',
176+
'--input-type=module',
177+
'--eval',
178+
`await import.source(${JSON.stringify(fixtures.fileURL('es-modules/unimportable.wasm'))})`,
179+
]);
180+
181+
strictEqual(stderr, '');
182+
strictEqual(stdout, '');
183+
strictEqual(code, 0);
184+
});
185+
186+
// TODO: Enable this once https://github.com/nodejs/node/pull/56842 lands.
187+
it.skip('should throw for dynamic source phase imports not defined', async () => {
188+
const fileUrl = fixtures.fileURL('es-modules/wasm-source-phase.js');
189+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
190+
'--no-warnings',
191+
'--experimental-wasm-modules',
192+
'--input-type=module',
193+
'--eval',
194+
[
195+
'import { ok, strictEqual } from "node:assert";',
196+
`await assert.rejects(import.source(${JSON.stringify(fileUrl)}), (e) => {`,
197+
' strictEqual(e instanceof SyntaxError, true);',
198+
' strictEqual(e.message.includes("Source phase import object is not defined for module"), true);',
199+
` strictEqual(e.message.includes(${JSON.stringify(fileUrl)}), true);`,
200+
'});',
201+
].join('\n'),
202+
]);
203+
204+
strictEqual(stderr, '');
205+
strictEqual(stdout, '');
206+
strictEqual(code, 0);
207+
});
208+
209+
// `import source` is not supported on this version of V8.
210+
it.skip('should throw for static source phase imports not defined', async () => {
211+
const fileUrl = fixtures.fileURL('es-modules/wasm-source-phase.js');
212+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
213+
'--no-warnings',
214+
'--experimental-wasm-modules',
215+
'--input-type=module',
216+
'--eval',
217+
`import source nosource from ${JSON.stringify(fileUrl)};`,
218+
]);
219+
match(stderr, /Source phase import object is not defined for module/);
220+
ok(stderr.includes(fileUrl));
221+
strictEqual(stdout, '');
222+
notStrictEqual(code, 0);
223+
});
224+
225+
// `import source` is not supported on this version of V8
226+
it.skip('should throw for vm source phase static import', async () => {
227+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
228+
'--no-warnings',
229+
'--experimental-wasm-modules',
230+
'--experimental-vm-modules',
231+
'--input-type=module',
232+
'--eval',
233+
[
234+
'const m1 = new vm.SourceTextModule("import source x from \\"y\\";");',
235+
'const m2 = new vm.SourceTextModule("export var p = 5;");',
236+
'await m1.link(() => m2);',
237+
'await m1.evaluate();',
238+
].join('\n'),
239+
]);
240+
match(stderr, /Source phase import object is not defined for module/);
241+
strictEqual(stdout, '');
242+
notStrictEqual(code, 0);
243+
});
244+
245+
// TODO: Enable this once https://github.com/nodejs/node/pull/56842 lands.
246+
it.skip('should throw for vm source phase dynamic import', async () => {
247+
const { code, stderr, stdout } = await spawnPromisified(execPath, [
248+
'--no-warnings',
249+
'--experimental-wasm-modules',
250+
'--experimental-vm-modules',
251+
'--input-type=module',
252+
'--eval',
253+
[
254+
'import { constants } from "node:vm";',
255+
'const opts = { importModuleDynamically: () => m2 };',
256+
'const m1 = new vm.SourceTextModule("await import.source(\\"y\\");", opts);',
257+
'const m2 = new vm.SourceTextModule("export var p = 5;");',
258+
'await m1.link(() => m2);',
259+
'await m1.evaluate();',
260+
].join('\n'),
261+
]);
262+
match(stderr, /Source phase import object is not defined for module/);
263+
strictEqual(stdout, '');
264+
notStrictEqual(code, 0);
265+
});
93266
});
490 Bytes
Binary file not shown.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function call1 (func, thisObj, arg0) {
2+
return func.call(thisObj, arg0);
3+
}
4+
5+
export function call2 (func, thisObj, arg0, arg1) {
6+
return func.call(thisObj, arg0, arg1);
7+
}
8+
9+
export function call3 (func, thisObj, arg0, arg1, arg2) {
10+
return func.call(thisObj, arg0, arg1, arg2);
11+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const { get: getProperty, set: setProperty } = Reflect;
2+
export const { create } = Object;
3+
export const global = globalThis;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const console = 'console';
2+
const hello_world = 'hello world';
3+
const log = 'log';
4+
const prop = 'prop';
5+
6+
export { console, hello_world as 'hello world', log, prop }

0 commit comments

Comments
 (0)