Skip to content

Add new AUTO_INIT setting to enable auto-initialized ES6 modules#27151

Open
guybedford wants to merge 1 commit into
emscripten-core:mainfrom
guybedford:add-tla-setting
Open

Add new AUTO_INIT setting to enable auto-initialized ES6 modules#27151
guybedford wants to merge 1 commit into
emscripten-core:mainfrom
guybedford:add-tla-setting

Conversation

@guybedford

@guybedford guybedford commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Adds a new -sAUTO_INIT option, for MODULARIZE=instance and WASM_ESM_INTEGRATION output modes. Throws if INCOMING_MODULE_JS_API is not empty, -sSTRICT is passed, or any other module output mode is used. When provided, instead of exporting the init function, the ES module initializes itself via top-level await. The named Wasm/runtime exports are then ready to use as soon as the module is imported. Also works with pthreads.

Previous version auto-detected empty INCOMING_MODULE_JS_API, but this was deemed too magical.

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
Comment thread src/postamble.js Outdated
Comment thread src/postamble.js Outdated
Comment thread tools/link.py Outdated
@guybedford guybedford force-pushed the add-tla-setting branch 3 times, most recently from 37154f4 to 05663fa Compare June 27, 2026 01:26
@hoodmane

Copy link
Copy Markdown
Collaborator

Personally I like it better with a separate setting. If it's self-initializing, it seems like we can only have one instance because multiple imports to ES6 modules all resolve to the same object. Also, it's a bit weird and unexpected for -sINCOMING_MODULE_JS_API=[] to do this. One can always do -sINCOMING_MODULE_JS_API=[whateverUsesTheLeastBytes], so it's not the end of the world. But I think a separate setting would be less surprising and clearer.

@hoodmane

Copy link
Copy Markdown
Collaborator

Also, someone reading the linker flags won't get a good sense for what the implications of -sINCOMING_MODULE_JS_API=[] are.

@sbc100

sbc100 commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

If it's self-initializing, it seems like we can only have one instance because multiple imports to ES6 modules all resolve to the same object.

I think WASM_ESM_INTEGRATION already implies MODULARIZE=instance which means we always have a single instance in this case anyway.

@sbc100

sbc100 commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

But I kind of agree, an explicit setting might be best here.

Note that the standard HTML shell code that we have would completely fail to work in this mode since it relies on overriding print and printErr to get stdout and stdeerr, at the very least.

I wonder how useful this setting will be in on the web where I believe folks do use elements from INCOMING_MODULE_JS_API (at least all our examples do).

@brendandahl

Copy link
Copy Markdown
Collaborator

I'd lean towards a setting if we do support this. I agree it seems a bit odd to change the shape of the API magically based on the those other settings.

Wasm-bindgen (the API we mimic) has gone through a similar proposal. Alex mentioned waiting for ESM modules to help, but I don't see how that would help in our case here for processing module args.

@guybedford

Copy link
Copy Markdown
Collaborator Author

I've updated this PR to now use -sSELF_INIT. Agreed that a less magical opt-in makes sense.

@brendandahl yes wasm-bindgen already does auto-initialization for its native JS module output without a custom option being necessary. We just don't run the main() still under this output mode, but that is because libraries don't usually implement main functions (instead we support arbitrary #[wasm_bindgen(start)] functions being defined, all then running in non-deterministic order).

Comment thread site/source/docs/tools_reference/settings_reference.rst Outdated
Comment thread src/postamble.js

@sbc100 sbc100 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment thread site/source/docs/tools_reference/settings_reference.rst
Comment thread src/preamble.js

#if PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return startWorker();
if (ENVIRONMENT_IS_PTHREAD) return;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why move startWorker?

@guybedford guybedford Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, initRuntime() would drive the ready event, but now only CMD_LOAD itself fires that event, separating initialization from the ready event itself.

@sbc100

sbc100 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Oh, maybe add ChangeLog entry?

Instead of inferring self-initialization from an empty INCOMING_MODULE_JS_API,
gate it behind an explicit AUTO_INIT setting. When set, an instance ES module
(MODULARIZE=instance or WASM_ESM_INTEGRATION) initializes itself via top-level
await on import and does not export the default `init` function.

Since there is no init/moduleArg, module-level configuration is unavailable:
INCOMING_MODULE_JS_API is disabled and passing a non-empty one is an error.
AUTO_INIT requires MODULARIZE=instance or WASM_ESM_INTEGRATION.
@sbc100 sbc100 changed the title Self-initializing ES modules Add new AUTO_INIT setting to enable auto-initialized ES6 modules Jul 1, 2026
@sbc100

sbc100 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

The current failures looks like CI infra flake. I'll retry them and try to land this tomorrow morning.

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.

4 participants