Add support for source-phase imports (import source / import.source(...))#1
Merged
sbc100 merged 1 commit intoMay 21, 2026
Conversation
sbc100
reviewed
May 16, 2026
sbc100
left a comment
There was a problem hiding this comment.
I'm not too familiar with acorn internals. Do we need/want to land this right away or can we wait for the upstream terser patch to get reviewed?
lgtm assuming upstream thinks its reasonable.
sbc100
reviewed
May 16, 2026
sbc100
left a comment
There was a problem hiding this comment.
If you don't want to wait for upstream we can probably still go ahead, just let me know and I'll take a deeper look.
a537c17 to
6f9925b
Compare
guybedford
added a commit
to guybedford/emscripten
that referenced
this pull request
May 16, 2026
When -sSOURCE_PHASE_IMPORTS=1, emcc emits ```js import source wasmModule from './foo.wasm'; ``` in its JS runtime. At -O2/-O3/-Os/-Oz the emitted JS is run through tools/acorn-optimizer.mjs which currently fails with a SyntaxError at parse time because acorn 8.x does not yet understand the source-phase imports proposal (https://github.com/tc39/proposal-source-phase-imports). Wire in the acorn-import-phases plugin so acorn can parse the syntax, and pull in the matching terser support (downstream of emscripten-core/terser#1) so the round-trip through from_mozilla_ast / to_mozilla_ast preserves the `phase` keyword on the way back out. Without the terser side, terser would silently drop the keyword and the host would return the module's exports namespace instead of a WebAssembly.Module, changing runtime semantics. * package.json: add acorn-import-phases dependency. * tools/acorn-optimizer.mjs: extend acorn with the plugin and use the extended parser at the parse site. * third_party/terser/terser.js: rebuilt from the emscripten-core/terser branch with source-phase imports support (PR emscripten-core/terser#1, the v5.18.2 downstream port of upstream terser PR terser/terser#1682). * test/js_optimizer/sourcePhaseImports{,-output}.js: new fixture that feeds two `import source` declarations through the JSDCE pass and checks the keyword survives. * test/test_other.py: register the fixture in test_js_optimizer, and parametrize test_esm_source_phase_imports across no-args and -O2 to exercise the optimizer pipeline.
guybedford
added a commit
to guybedford/emscripten
that referenced
this pull request
May 16, 2026
When -sSOURCE_PHASE_IMPORTS=1, emcc emits ```js import source wasmModule from './foo.wasm'; ``` in its JS runtime. At -O2/-O3/-Os/-Oz the emitted JS is run through tools/acorn-optimizer.mjs which currently fails with a SyntaxError at parse time because acorn 8.x does not yet understand the source-phase imports proposal (https://github.com/tc39/proposal-source-phase-imports). Wire in the acorn-import-phases plugin so acorn can parse the syntax, and pull in the matching terser support (downstream of emscripten-core/terser#1) so the round-trip through from_mozilla_ast / to_mozilla_ast preserves the `phase` keyword on the way back out. Without the terser side, terser would silently drop the keyword and the host would return the module's exports namespace instead of a WebAssembly.Module, changing runtime semantics. * package.json: add acorn-import-phases dependency. * tools/acorn-optimizer.mjs: extend acorn with the plugin and use the extended parser at the parse site. * third_party/terser/terser.js: rebuilt from the emscripten-core/terser branch with source-phase imports support (PR emscripten-core/terser#1, the v5.18.2 downstream port of upstream terser PR terser/terser#1682). * test/js_optimizer/sourcePhaseImports{,-output}.js: new fixture that feeds two `import source` declarations through the JSDCE pass and checks the keyword survives. * test/test_other.py: register the fixture in test_js_optimizer, and parametrize test_esm_source_phase_imports across no-args and -O2 to exercise the optimizer pipeline.
Author
|
Will update here after upstream review process rather. |
…namic `import.source(...)` / `import.defer(...)`) (terser#1682) * add support for source-phase imports (`import source`, `import.source(...)`) Implements the TC39 source-phase imports proposal (https://github.com/tc39/proposal-source-phase-imports) for both static and dynamic forms: // static import source wasmModule from "./foo.wasm"; import defer * as ns from "./mod.js"; // dynamic const m = await import.source("./foo.wasm"); const m = await import.defer("./mod.js"); Without this, terser dropped the phase keyword on the mozilla-AST round-trip used by tools like Emscripten's acorn-optimizer, silently changing runtime semantics. Static side: * Parser recognises the contextual `source`/`defer` phase keyword after `import`, with peek-based disambiguation so existing patterns like `import source from "x"` keep parsing as default-name imports. * AST_Import gains a `phase` field (null for plain imports). * mozilla-ast and the printer carry phase through. Dynamic side: * New AST_DynamicImport node owns the whole `import.source(...)` / `import.defer(...)` expression, including its args. It is NOT an AST_Call. Plain `import(x)` is still parsed as an AST_Call with a synthetic `import` SymbolRef callee. * mozilla-ast: ImportExpression with `phase` round-trips to/from AST_DynamicImport. * size, equivalent-to and has_side_effects/may_throw updated. * address review feedback: tidy comments and use (x || null) === pattern * Update test/compress/harmony.js * Update test/compress/harmony.js --------- Co-authored-by: Fábio Santos <fabiosantosart@gmail.com>
6f9925b to
79b61c4
Compare
guybedford
added a commit
to guybedford/emscripten
that referenced
this pull request
May 21, 2026
When -sSOURCE_PHASE_IMPORTS=1, emcc emits ```js import source wasmModule from './foo.wasm'; ``` in its JS runtime. At -O2/-O3/-Os/-Oz the emitted JS is run through tools/acorn-optimizer.mjs which currently fails with a SyntaxError at parse time because acorn 8.x does not yet understand the source-phase imports proposal (https://github.com/tc39/proposal-source-phase-imports). Wire in the acorn-import-phases plugin so acorn can parse the syntax, and pull in the matching terser support (downstream of emscripten-core/terser#1) so the round-trip through from_mozilla_ast / to_mozilla_ast preserves the `phase` keyword on the way back out. Without the terser side, terser would silently drop the keyword and the host would return the module's exports namespace instead of a WebAssembly.Module, changing runtime semantics. * package.json: add acorn-import-phases dependency. * tools/acorn-optimizer.mjs: extend acorn with the plugin and use the extended parser at the parse site. * third_party/terser/terser.js: rebuilt from the emscripten-core/terser branch with source-phase imports support (PR emscripten-core/terser#1, the v5.18.2 downstream port of upstream terser PR terser/terser#1682). * test/js_optimizer/sourcePhaseImports{,-output}.js: new fixture that feeds two `import source` declarations through the JSDCE pass and checks the keyword survives. * test/test_other.py: register the fixture in test_js_optimizer, and parametrize test_esm_source_phase_imports across no-args and -O2 to exercise the optimizer pipeline.
Author
|
The upstream PR has now been landed and released and this PR implementation is now based to that updated upstream PR change. @sbc100 PTAL |
sbc100
approved these changes
May 21, 2026
sbc100
left a comment
There was a problem hiding this comment.
lgtm!
Can you maybe update the description now that the upstream change has landed?
Author
|
Sure, updated the description to match upstream. |
9759c2a
into
emscripten-core:emscripten_patches_v5.18.2
0 of 10 checks passed
guybedford
added a commit
to guybedford/emscripten
that referenced
this pull request
May 21, 2026
When -sSOURCE_PHASE_IMPORTS=1, emcc emits ```js import source wasmModule from './foo.wasm'; ``` in its JS runtime. At -O2/-O3/-Os/-Oz the emitted JS is run through tools/acorn-optimizer.mjs which currently fails with a SyntaxError at parse time because acorn 8.x does not yet understand the source-phase imports proposal (https://github.com/tc39/proposal-source-phase-imports). Wire in the acorn-import-phases plugin so acorn can parse the syntax, and pull in the matching terser support (downstream of emscripten-core/terser#1) so the round-trip through from_mozilla_ast / to_mozilla_ast preserves the `phase` keyword on the way back out. Without the terser side, terser would silently drop the keyword and the host would return the module's exports namespace instead of a WebAssembly.Module, changing runtime semantics. * package.json: add acorn-import-phases dependency. * tools/acorn-optimizer.mjs: extend acorn with the plugin and use the extended parser at the parse site. * third_party/terser/terser.js: rebuilt from the emscripten-core/terser branch with source-phase imports support (PR emscripten-core/terser#1, the v5.18.2 downstream port of upstream terser PR terser/terser#1682). * test/js_optimizer/sourcePhaseImports{,-output}.js: new fixture that feeds two `import source` declarations through the JSDCE pass and checks the keyword survives. * test/test_other.py: register the fixture in test_js_optimizer, and parametrize test_esm_source_phase_imports across no-args and -O2 to exercise the optimizer pipeline.
guybedford
added a commit
to guybedford/emscripten
that referenced
this pull request
May 21, 2026
When -sSOURCE_PHASE_IMPORTS=1, emcc emits ```js import source wasmModule from './foo.wasm'; ``` in its JS runtime. At -O2/-O3/-Os/-Oz the emitted JS is run through tools/acorn-optimizer.mjs which currently fails with a SyntaxError at parse time because acorn 8.x does not yet understand the source-phase imports proposal (https://github.com/tc39/proposal-source-phase-imports). Wire in the acorn-import-phases plugin so acorn can parse the syntax, and pull in the matching terser support (downstream of emscripten-core/terser#1) so the round-trip through from_mozilla_ast / to_mozilla_ast preserves the `phase` keyword on the way back out. Without the terser side, terser would silently drop the keyword and the host would return the module's exports namespace instead of a WebAssembly.Module, changing runtime semantics. * package.json: add acorn-import-phases dependency. * tools/acorn-optimizer.mjs: extend acorn with the plugin and use the extended parser at the parse site. * third_party/terser/terser.js: rebuilt from the emscripten-core/terser branch with source-phase imports support (PR emscripten-core/terser#1, the v5.18.2 downstream port of upstream terser PR terser/terser#1682). * test/js_optimizer/sourcePhaseImports{,-output}.js: new fixture that feeds two `import source` declarations through the JSDCE pass and checks the keyword survives. * test/test_other.py: register the fixture in test_js_optimizer, and parametrize test_esm_source_phase_imports across no-args and -O2 to exercise the optimizer pipeline.
sbc100
pushed a commit
to emscripten-core/emscripten
that referenced
this pull request
May 21, 2026
…26967) With `-sSOURCE_PHASE_IMPORTS=1`, emcc emits `import source wasmModule from './foo.wasm'` in its JS runtime, but at `-O2`+ the optimizer crashes because acorn 8.x doesn't yet understand the syntax. Even once acorn parses it, the bundled terser silently drops the `source` keyword on the mozilla-AST round-trip, which changes runtime semantics (host returns the exports namespace instead of a `WebAssembly.Module`). This wires in the `acorn-import-phases` plugin so acorn parses the syntax, and pulls in matching terser support so the keyword survives printing. Terser side is emscripten-core/terser#1, which is vendored via `third_party/terser/terser.js` rebuilt from that branch. New fixture `test/js_optimizer/sourcePhaseImports.js` runs two `import source` declarations through JSDCE and checks the keyword survives; runs via `./test/runner other.test_js_optimizer_sourcePhaseImports`. That terser PR should ideally land first so this can be rebuilt from a merged branch; happy to rebase the bundle afterwards.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds support for the TC39 source-phase imports proposal (https://github.com/tc39/proposal-source-phase-imports) for both static and dynamic forms:
The
sourcephase tells the host to materialize the module's source representation (e.g. aWebAssembly.Modulefor .wasm) instead of its exports namespace;deferrequests a lazily-evaluated namespace. Dropping the phase keyword silently changes runtime semantics, which is what terser was doing on the mozilla-AST round-trip used by tools like Emscripten's acorn-optimizer.This is the downstream port of upstream terser PR terser#1682 released in 5.48.0.
Static side
source/deferphase keyword afterimport, with peek-based disambiguation so existing patterns likeimport source from "x"andimport defer, {y} from "x"keep parsing as default-name imports.AST_Importgains aphasefield,nullfor plain imports.mozilla-astfrom_moz/to_mozcarryphaseacross, matching the shape produced by acorn-import-phases.DEFPRINT(AST_Import)emits the phase keyword betweenimportand the specifier; printingself.phaseverbatim handles bothsourceanddefer.equivalent-toincludesphaseinshallow_cmp.Before / after a round-trip through the optimizer:
Dynamic side
AST_DynamicImportnode used to represent a phased dynamic import; carries thephase. Plain dynamicimport(x)continues to use the syntheticAST_SymbolRef("import")callee for back-compat with downstream code that already groks that pattern.import_metadispatches onmeta/source/defer;import.source/import.defermust be followed by a call, else a parse error is raised, mirroring the proposal.mozilla-ast:from_moz ImportExpressionbuilds theAST_DynamicImportcallee whenM.phaseis set;to_moz AST_Callemits a phasedImportExpression { phase }when the callee isAST_DynamicImport.DEFPRINT(AST_DynamicImport)printsimport.source/import.deferfor the callee.size,equivalent-to,has_side_effectsandmay_throwall updated for the new node.Tests are also included in the backport.