feat: support source phase import for WebAssembly modules#20364
feat: support source phase import for WebAssembly modules#20364alexander-akait merged 12 commits intowebpack:mainfrom
Conversation
🦋 Changeset detectedLatest commit: ecbbe50 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
This PR introduces experimental support for TC39 source-phase imports for WebAssembly modules in webpack, allowing import.source() of .wasm/.wat to yield a WebAssembly.Module object instead of an instantiated instance. It wires the feature through parser options, experiments config, runtime globals, wasm async loading/compilation, and adds tests and schema/type updates.
Changes:
- Add
experiments.importSourceand associated JavaScript parser options (parser.javascript*.importSource) to configuration schemas, defaults, CLI snapshots, and TypeScript declaration files. - Extend the import phase infrastructure to support a new
Sourcephase, integrate it with both ESM and dynamic import parsers, and plumb asourcePhaseflag throughNormalModuleand the async WebAssembly module pipeline. - Introduce
RuntimeGlobals.compileWasmand a newAsyncWasmCompileRuntimeModule, along with generator/parser changes so async WebAssembly modules imported viaimport.source()compile to and export a cachedWebAssembly.Moduledefault, plus config-case tests for basic behavior and caching.
Reviewed changes
Copilot reviewed 23 out of 26 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
types.d.ts |
Adds experiments.importSource, parser importSource options, ImportExpressionJavascriptParser.phase support for "source", NormalModule.sourcePhase, and RuntimeGlobals.compileWasm to the public type surface. |
declarations/WebpackOptions.d.ts |
Mirrors new importSource experiment and parser options in the exported TS configuration interfaces. |
schemas/WebpackOptions.json |
Extends the JSON schema with experiments.importSource and module.parser.javascript*.importSource boolean options so config validation/CLI help know about the new feature. |
lib/config/defaults.js |
Wires experiments.importSource into defaults, passes it through module/parser option defaults, and ensures JS parser importSource defaults from the experiment flag. |
lib/WebpackOptionsApply.js |
Enables acorn-import-phases whenever deferImport or importSource is enabled and configures it to recognize source-phase syntax when experiments.importSource is true. |
lib/javascript/JavascriptParser.js |
Updates the ImportExpression typedef so the AST node can carry `phase: "defer" |
lib/dependencies/ImportPhase.js |
Introduces ImportPhase.Source and ImportPhaseUtils.isSource, and updates createGetImportPhase to classify both defer and source syntax when enabled. |
lib/dependencies/ImportParserPlugin.js |
Switches import-phase detection to be gated by either deferImport or importSource parser options for dynamic imports. |
lib/dependencies/HarmonyImportDependencyParserPlugin.js |
Same as above for static import/export (harmony) parsing. |
lib/dependencies/ImportDependency.js |
Uses ImportPhaseUtils and, for source-phase imports, unwraps the default export so import.source() resolves to the underlying value (the WebAssembly.Module) instead of the namespace object. |
lib/dependencies/HarmonyImportDependency.js |
Extends debugging/serialization output to reflect ` |
lib/RuntimeGlobals.js |
Registers a new runtime global compileWasm (__webpack_require__.vs) with documentation indicating it compiles a wasm module from id and hash to WebAssembly.Module. |
lib/WebpackOptionsApply.js |
Ensures that when experiments.importSource is enabled, the parser is extended with source-phase import support, without affecting builds where the experiment is off. |
lib/NormalModule.js |
Adds a sourcePhase flag to NormalModule construction data, stores it on instances, and includes it in serialization/deserialization for caching. |
lib/wasm-async/AsyncWebAssemblyModulesPlugin.js |
Detects when an async wasm module is reached via a source-phase dependency and marks its create data as sourcePhase so downstream parser/generator behavior can switch. |
lib/wasm-async/AsyncWebAssemblyParser.js |
For NormalModule.sourcePhase wasm modules, validates only the wasm magic header, marks buildMeta.sourcePhase, declares a single default export, and skips full wasm decoding; otherwise keeps existing async wasm parsing. |
lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js |
Routes source generation for buildMeta.sourcePhase modules to a new _generateSourcePhase code path that defines an async module using RuntimeGlobals.compileWasm and exports the resulting WebAssembly.Module as default. |
lib/wasm-async/AsyncWasmCompileRuntimeModule.js |
New runtime module that defines RuntimeGlobals.compileWasm as a function taking (wasmModuleId, wasmModuleHash), loading the wasm asset (via target-specific loader code), using WebAssembly.compile / compileStreaming with a MIME-type-sensitive fallback, and returning a Promise<WebAssembly.Module>. |
lib/web/FetchCompileAsyncWasmPlugin.js |
Hooks the web/fetch wasm loading pipeline to also provide the new compile runtime using AsyncWasmCompileRuntimeModule when async wasm modules are present, sharing the same generateLoadBinaryCode as instantiation. |
lib/node/ReadFileCompileAsyncWasmPlugin.js |
Similarly wires the node/async-node wasm pipeline to add AsyncWasmCompileRuntimeModule (non-streaming, using fs/url loader) when async wasm modules are in the chunk. |
test/configCases/wasm/source-phase-basic/webpack.config.js |
Adds an async-node test configuration enabling experiments.importSource and async wasm via a .wat loader. |
test/configCases/wasm/source-phase-basic/wasm.wat |
Simple WAT module exporting an add(i32, i32) function to be used in source-phase import tests. |
test/configCases/wasm/source-phase-basic/test.filter.js |
Skips the new wasm test config when the environment lacks WebAssembly support. |
test/configCases/wasm/source-phase-basic/index.js |
Test cases verifying import.source("./wasm.wat") yields a WebAssembly.Module, that it can be instantiated and invoked, and that multiple imports share the same compiled module instance. |
test/__snapshots__/Cli.basictest.js.snap |
Updates CLI option snapshots to include experiments.importSource and all module.parser.javascript*.importSource flags with consistent descriptions. |
test/Defaults.unittest.js |
Adjusts defaults snapshots to reflect experiments.importSource: false and parser importSource: false by default. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| deferImport: | ||
| /** @type {NonNullable<ExperimentsNormalized["deferImport"]>} */ | ||
| (options.experiments.deferImport), | ||
| importSource: |
There was a problem hiding this comment.
Maybe sourceImport? To be align with deferImport
|
Do you want to solve it |
Merging this PR will degrade performance by 31.43%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | benchmark "devtool-eval-source-map", scenario '{"name":"mode-development","mode":"development"}' |
968.6 KB | 1,396.4 KB | -30.64% |
| ⚡ | Memory | benchmark "many-chunks-commonjs", scenario '{"name":"mode-production","mode":"production"}' |
9.5 MB | 7 MB | +35.92% |
| ⚡ | Memory | benchmark "wasm-modules-async", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
1,420.5 KB | 916.3 KB | +55.04% |
| ❌ | Memory | benchmark "react", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
644.2 KB | 864.4 KB | -25.48% |
| ⚡ | Memory | benchmark "devtool-source-map", scenario '{"name":"mode-development","mode":"development"}' |
1.4 MB | 1 MB | +32.44% |
| ❌ | Memory | benchmark "asset-modules-source", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
267.4 KB | 390 KB | -31.43% |
Comparing magic-akari:feat/source-phase-import-wasm (ecbbe50) with main (0b60f1c)
f462aa6 to
32352c3
Compare
|
Thanks for example and other things, there remains to be added:
|
Done
I’ve derived the current test cases from the existing Wasm tests.
That’s a very interesting use case! However, since the spec is still in its early stages and many details remain unclear, I think it’s best to handle this in a separate PR. For now, introducing the |
Universal target ( |
Thanks for the clarification. You can find the implementation and new test cases under:
|
|
Thanks, code looks good, we will review this deeply soon (maybe will be included in the next release) |
6a3d326 to
8f95cdb
Compare
|
I'm working on a rebase and have tried several approaches without success. Are there any guidelines regarding |
|
@magic-akari I think we need refactor this place, ideally we need: JavascriptParser.extend(
importPhases({
source: options.experiments.sourceImport,
defer: options.experiments.defer
})
);And use |
8f95cdb to
63d9223
Compare
Thanks for the guidance! Everything is now set and this PR is ready for review. |
63d9223 to
9667819
Compare
|
@magic-akari Thanks for rebase, will review and merge soon (will be in the next minor release) |
|
@magic-akari can you make an extra one rebase and I will focus on this improvement, thanks, sorry for that |
9667819 to
68a6124
Compare
Done |
08699f7 to
aa0de2c
Compare
|
@magic-akari Looks like we need a new one rebase and we can merge, still can't understand why github doesn't provide ability to rebase branches in forks when you are working on them like a contributor 😞 ... |
aa0de2c to
de2d586
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #20364 +/- ##
==========================================
+ Coverage 90.45% 91.40% +0.94%
==========================================
Files 557 560 +3
Lines 55135 55295 +160
Branches 14554 14593 +39
==========================================
+ Hits 49875 50545 +670
+ Misses 5260 4750 -510
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
This PR is packaged and the instant preview is available (7cdc173). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@7cdc173
yarn add -D webpack@https://pkg.pr.new/webpack@7cdc173
pnpm add -D webpack@https://pkg.pr.new/webpack@7cdc173 |
Summary
This PR implements support for the TC39 source-phase-imports proposal for WebAssembly modules, enabling developers to import WebAssembly modules at the source phase using
import.source()syntax.Motivation:
Source phase imports allow importing a WebAssembly module as a
WebAssembly.Moduleobject rather than an instantiated module. This enables several important use cases:postMessageExample usage:
Implementation details:
ImportPhaseinfrastructure to supportSourcephase alongsideEvaluationandDeferRuntimeGlobals.compileWasmfor source phase compilationAsyncWasmCompileRuntimeModuleto generate runtime code for compiling WASM modulesWebAssembly.Moduleas default exportexperiments.importSourceflag for safetyWhat kind of change does this PR introduce?
Feature (
feat) - Adds support for experimental TC39 source phase imports for WebAssembly modules.Did you add tests for your changes?
Yes. Added test case in
test/configCases/wasm/source-phase-basic/covering:WebAssembly.ModuleinstanceDoes this PR introduce a breaking change?
No. This feature is:
experiments.importSourceconfiguration flag (defaults tofalse)import.source()syntaxIf relevant, what needs to be documented once your changes are merged or what have you already documented?
Documentation needed:
Configuration documentation:
experiments.importSourceflag to webpack configuration docsFeature documentation:
TypeScript types:
declarations/WebpackOptions.d.tswith JSDoctypes.d.tsRelated: