Modularized Output

By default, emscripten outputs regular JS scripts which can be loaded on the Web via a script tag, or on the command line using Node or other JS runtime.

In this default configuration the generated Module object variable (and indeed all of the emscripten internal symbols) are added to the global scope. This means that only one emscripten-built program can be loaded in a given scope (since multiple modules would clobber each other’s state).

The MODULARIZE setting (along with other related settings) can be used to isolate the generated module within its own private scope and/or allow multiple instances of the same module to be created.

This section covers the various modularization options available and how to use them.

Using -sMODULARIZE

The MODULARIZE setting has two primary functions. Firstly, it puts all of the generated code inside its own scope so that the global namespace is not polluted when the JS is loaded (for example, via script tag). Secondly, it allows multiple instances of the module to be created.

The generated factory function is async and it returns a promise of an instance of the program. It can be called any number of times to create multiple separate and isolated instances of the same program.

By default, the factory function is called Module, but it can be given a unique name via the -sEXPORT_NAME setting.

For example, a program is built using -sMODUARLIZE -sEXPORT_NAME=Foo can be instantiated using:

var myFoo = await Foo({ /* optional params */ });
myFoo._nativeMethod();

or:

Foo({ /* optional params */ }).then((myFoo) => {
  myFoo._nativeMethod();
}

If node is included in the -sENVIRONMENT setting then the generated module also acts as a CommonJS module under node.

Because there is no global Module object in this case any parameters to the module creation are passed via the optional parameter to the factory function. For example, if you wanted to use a custom print method you could write:

var myFoo = await Foo({ print: myPrint });

Generating ES6 Modules

The EXPORT_ES6 setting can be used to instead output JavaScript in the form of an ES module. (The ES module format did not exist at time when the MODULARIZE setting was first created otherwise this would likely be the default).

Using an output filename that ends in .mjs will automatically enable this setting.

In this mode the module has a single default export which is the factory function for creating new instances of the module. For example:

import Foo from './emcc-output.js';
var myFoo = await Foo({ print: myPrint });

The name Foo here can be whatever you choose and will always be assigned to the default export of the module.

Using -sMODULARIZE=instance (experimental)

Emscripten has experimental support for performing only the encapsulation part of modularization, and not the ability to create multiple instances. In this mode a module is created that exports a single instance of the program rather than a factory function.

This setting implicitly enables EXPORT_ES6 and will not work when EXPORT_ES6 is explictly disabled.

In this mode the default export of the module is an initializer function which allows input parameters to be passed to the instance. Other elements normally found on the module object are instead exported directly. For example:

// Import the default init function and a named native method
import init, { _nativeMethod }  from './emcc-output.js';
await init({ print: myPrint });
_nativeMethod();

Limitations

Some major features still do not work in this mode. Many of these we hope to fix in future releses. Current limitations include:

  • Internal usage (e.g. usage within EM_JS / JS libary code) of the Module global does not work. This is because symbols are exported directly using ES6 module syntax rathar than using the Module global.

  • The wasmExports internal global does not exist.

  • ccall/cwrap are not supported (depends on the Module global).

  • ABORT_ON_WASM_EXCEPTIONS is not supported (requires wrapping wasm exports).

  • DYNCALLS is not supported (depends on the Module global)

  • ASYNCIFY is not supported (depends on DYNCALLS)

  • ASYNCIFY_LAZY_LOAD_CODE is not supported (depends on wasmExports global)

  • MINIMAL_RUNTIME is not supported.

  • The output of file_packager is not compatible so --preload-file <name> and --embed-file <file> do not work.

Source Phase Imports (experimental)

Source phase imports is a JavaScript proposal that adds support for importing Wasm modules via ES import statements. This allows emscripten to elide some of the auto-generated code for finding and fetching the Wasm binary.

See SOURCE_PHASE_IMPORTS.

This setting implicitly enables EXPORT_ES6 and will not work when EXPORT_ES6 is explictly disabled.

ES Module Integration (experimental)

Wasm ESM integration is a WebAssembly proposal that allows Wasm instances to be imported directly as ES modules. This allows emscripten to elide a lot of boilerplate code for linking up Wasm and JavaScript.

See WASM_ESM_INTEGRATION.

Limitations

This setting implicitly enables EXPORT_ES6 and sets MODULARIZE to instance. Because of this all the same limitations mentioned above for -sMODULARIZE=intance apply.

Some additional limitations are: