Skip to content

Self-initializing ES modules#27151

Open
guybedford wants to merge 9 commits into
emscripten-core:mainfrom
guybedford:add-tla-setting
Open

Self-initializing ES modules#27151
guybedford wants to merge 9 commits into
emscripten-core:mainfrom
guybedford:add-tla-setting

Conversation

@guybedford

@guybedford guybedford commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Under MODULARIZE=instance and WASM_ESM_INTEGRATION, when INCOMING_MODULE_JS_API is empty, or when -sSTRICT is passed, the ES module initializes itself via top-level await rather than exporting an init function. The named Wasm/runtime exports are ready to use as soon as the module is imported. Also works with pthreads.

Initial version used a -pTLA, but was adapted to not require a custom option since there is already existing use of top-level await.

This PR was made with AI assistance

@sbc100

sbc100 commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

The reason we have the init function is so that the user gets a chance to configure the module via moduleArgs.

If the user doesn't want that configuration then yes we could just async initialize the module. However, I wonder if we could just ifer that intent via -sINCOMING_MODULE_JS_API=[] (which means there are not configuration points that could live in moduleArgs).

Note that -sSTRING mode already implies -sINCOMING_MODULE_JS_API=[] and I hope the make the default one day.

I'm not necessarily opposed to some kind explicit option but as always I'll try to resist adding new settings :)

Comment thread src/settings.js Outdated
Comment thread tools/link.py Outdated
@guybedford

This comment was marked as outdated.

@guybedford

This comment was marked as outdated.

@sbc100

sbc100 commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

The argument for the option is that TLA is quite invasive, and requires the JS environment to properly support it.

In particular the major friction currently is that require() of ES Modules can't support it, so it restricts consumers to ESM only.

I'm not sure we need to worry too much about that use case. We have the ability to generate normal non-ESM modules. If somebody want to opt into ESM output, I think its fair to assume they will be consume it as an ESM module.

Also, the majority of our users are targeting the web where require() is not a thing anyway right?

@guybedford guybedford changed the title Add -sTLA setting for self-initializing ES modules Self-initializing ES modules Jun 18, 2026
Comment thread tools/link.py Outdated
@guybedford

Copy link
Copy Markdown
Collaborator Author

Agreed, ok, I've refactored this PR to be based on -sSTRICT / empty INCOMING_MODULE_JS_API option. This makes it now formally a breaking change, but I suppose we can do this under the experimental option.

@sbc100

sbc100 commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Agreed, ok, I've refactored this PR to be based on -sSTRICT / empty INCOMING_MODULE_JS_API option. This makes it now formally a breaking change, but I suppose we can do this under the experimental option.

Great.

@brendandahl WDYT? Is this too magical to automatically opt-out of the init() function like this? Obviously too much magic is bad, but also adding a new setting such for this would be bad too.

Comment thread src/pthread_esm_startup.mjs Outdated
…configure

In MODULARIZE=instance (and WASM_ESM_INTEGRATION) the `init` function exists so
the caller can configure the instance via `moduleArg` before it starts. When
there are no configuration points (INCOMING_MODULE_JS_API is empty, as implied
by STRICT) there is nothing to configure, so the module now self-initializes via
top-level await and `init` is no longer exported; the named Wasm/runtime exports
are ready to use as soon as the module is imported.

For WASM_ESM_INTEGRATION pthread workers self-initialize from within the module
(via the load message) rather than the wrapper exporting init.
Comment thread src/postamble.js Outdated
Comment thread src/postamble.js
Comment thread tools/link.py Outdated
Use the @esm_integration decorator instead of hand-rolling the setup so
the test inherits the wasm64 skip (ESM integration has no export
wrappers, so pointer-returning exports stay BigInt and writeStackCookie
fails).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants