diff --git a/lib/internal/modules/esm/default_resolve.js b/lib/internal/modules/esm/default_resolve.js index fea151a0ff..5e0fec9514 100644 --- a/lib/internal/modules/esm/default_resolve.js +++ b/lib/internal/modules/esm/default_resolve.js @@ -7,6 +7,7 @@ const { realpathSync } = require('fs'); const { getOptionValue } = require('internal/options'); const preserveSymlinks = getOptionValue('--preserve-symlinks'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); +const experimentalWasmModules = getOptionValue('--experimental-wasm-modules'); const { ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes; const { resolve: moduleWrapResolve } = internalBinding('module_wrap'); const { pathToFileURL, fileURLToPath } = require('internal/url'); @@ -30,6 +31,16 @@ const legacyExtensionFormatMap = { '.node': 'cjs' }; +if (experimentalWasmModules) { + // This is a total hack + Object.assign(extensionFormatMap, { + '.wasm': 'wasm' + }); + Object.assign(legacyExtensionFormatMap, { + '.wasm': 'wasm' + }); +} + function resolve(specifier, parentURL) { if (NativeModule.canBeRequiredByUsers(specifier)) { return { diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 4fc0d70b53..56098a8c58 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -13,7 +13,7 @@ const fs = require('fs'); const { SafeMap, } = primordials; -const { URL } = require('url'); +const { fileURLToPath, URL } = require('url'); const { debuglog, promisify } = require('util'); const esmLoader = require('internal/process/esm_loader'); @@ -94,3 +94,24 @@ translators.set('builtin', async function(url) { reflect.exports.default.set(module.exports); }); }); + +// Strategy for loading a wasm module +translators.set('wasm', async function(url) { + const pathname = fileURLToPath(url); + const buffer = await readFileAsync(pathname); + debug(`Translating WASMModule ${url}`); + let result, keys; + try { + WebAssembly.validate(buffer); + result = await WebAssembly.instantiate(buffer, {}); + keys = Object.keys(result.instance.exports); + } catch (err) { + err.message = pathname + ': ' + err.message; + throw err; + } + return createDynamicModule([...keys, 'default'], url, (reflect) => { + for (const key of keys) + reflect.exports[key].set(result.instance.exports[key]); + reflect.exports.default.set(result.instance.exports); + }); +}); diff --git a/src/node_options.cc b/src/node_options.cc index 9c0dce5cde..cf248642c1 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -164,6 +164,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "experimental ES Module support and caching modules", &EnvironmentOptions::experimental_modules, kAllowedInEnvironment); + AddOption("--experimental-wasm-modules", + "experimental ES Module support for webassembly modules", + &EnvironmentOptions::experimental_wasm_modules, + kAllowedInEnvironment); AddOption("--experimental-policy", "use the specified file as a " "security policy", diff --git a/src/node_options.h b/src/node_options.h index f52f79c895..2ec769e28e 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -86,6 +86,7 @@ class EnvironmentOptions : public Options { public: bool abort_on_uncaught_exception = false; bool experimental_modules = false; + bool experimental_wasm_modules = false; std::string module_type; std::string experimental_policy; bool experimental_repl_await = false; diff --git a/test/es-module/test-esm-wasm.mjs b/test/es-module/test-esm-wasm.mjs new file mode 100644 index 0000000000..76a43c708a --- /dev/null +++ b/test/es-module/test-esm-wasm.mjs @@ -0,0 +1,9 @@ +// Flags: --experimental-modules --experimental-wasm-modules +/* eslint-disable node-core/required-modules */ +import wasmMod from '../fixtures/simple.wasm' +import {add} from '../fixtures/simple.wasm'; +import {strictEqual} from 'assert'; + +strictEqual(wasmMod.add(10, 20), 30); +strictEqual(add(10, 20), 30); +strictEqual(wasmMod.add, add);