Skip to content

Turbopack: Add importModule() support to webpack loaders#89630

Merged
sokra merged 24 commits intocanaryfrom
claude/add-webpack-loader-test-kVsLq
Mar 19, 2026
Merged

Turbopack: Add importModule() support to webpack loaders#89630
sokra merged 24 commits intocanaryfrom
claude/add-webpack-loader-test-kVsLq

Conversation

@sokra
Copy link
Member

@sokra sokra commented Feb 7, 2026

What?

Adds support for this.importModule() in Turbopack's webpack loader compatibility layer. This API allows webpack loaders to dynamically import and execute modules (CJS, ESM, TypeScript, JSON, WebAssembly, etc.) during the build process, matching webpack's native this.importModule() behavior.

Why?

Webpack loaders like @vanilla-extract/webpack-plugin, val-loader, and custom loaders use this.importModule() to:

  • Load configuration files that are themselves modules with dependencies
  • Execute code at build time to generate derived source
  • Process dependency chains (e.g., a TS config that imports a CJS module that requires JSON)

Without this, loaders relying on importModule() cannot work with Turbopack.

How?

Architecture

The implementation follows a request/response pattern between the Node.js loader runner and the Rust-side Turbopack compiler:

  1. Loader calls this.importModule(request) in the Node.js loader runner
  2. IPC message sent to Rust with the module request and lookup path
  3. Rust resolves the module using Turbopack's full resolver (with aliases, loader rules, etc.)
  4. Rust builds a Node.js bundle containing the module and all its dependencies using Turbopack's code generation pipeline
  5. Bundle chunks sent back to Node.js via IPC
  6. Node.js evaluates the bundle in-memory using vm.compileFunction with a minimal CJS module system

This approach leverages Turbopack's full module graph, so imported modules benefit from the same resolution (aliases, custom loaders, TypeScript support, etc.) as regular imports.

Key Changes

Rust side (turbopack-node/src/transforms/webpack.rs):

  • New ImportModule IPC response message type
  • Resolves modules through Turbopack's esm_resolve with full AssetContext
  • Generates a complete Node.js bundle (runtime + chunks + entry) for the imported module
  • Tracks file dependencies for proper cache invalidation

SourceTransform trait (turbopack-core/src/source_transform.rs):

  • Added asset_context parameter to SourceTransform::transform() so transforms can access the full asset context for module resolution and bundling
  • Updated all implementors: JsonSourceTransform, TextSourceTransform, BytesSourceTransform, MdxTransform, PostCssTransform, WebpackLoaderItems

Node.js runtime (turbopack-node/js/src/transforms/):

  • webpack-loaders.ts: Implements this.importModule() on the loader context, sends IPC request and evaluates the returned bundle
  • webpack-loaders-runtime.ts: In-memory CJS module evaluator that loads bundle chunks using vm.compileFunction, with support for:
    • Relative and absolute path resolution within the bundle
    • External package delegation to real Node.js require()
    • Patched fs.createReadStream for in-memory WebAssembly binary assets

New ImportModule reference subtype (turbopack-core/src/reference_type.rs):

  • Added EcmaScriptModulesReferenceSubType::ImportModule to mark references created by importModule for proper handling in the module graph

Test Coverage

Comprehensive e2e test (test/e2e/app-dir/webpack-loader-import-module/) covering:

  • TypeScript modules with CJS and ESM dependencies
  • CJS dependency chains (TS → CJS → JSON)
  • ESM .mjs modules with shared dependencies
  • Resolve aliases (alias-dataalias-data.mjs via resolveAlias config)
  • Custom loader rules (.custom-data files processed by text-to-export-loader)
  • Transitive dependencies through aliases and custom loaders
  • Turbopack-only features: new URL() asset references, WebAssembly imports (add.wasm), and dynamic import() within importModule targets

All tests pass for both webpack and Turbopack modes.

@nextjs-bot nextjs-bot added created-by: Turbopack team PRs by the Turbopack team. tests Turbopack Related to Turbopack with Next.js. labels Feb 7, 2026
@codspeed-hq
Copy link

codspeed-hq bot commented Feb 7, 2026

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing claude/add-webpack-loader-test-kVsLq (6a1a742) with canary (62393b0)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Feb 7, 2026

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 455ms 455ms ▁▁█▁▁
Cold (Ready in log) 439ms 438ms ▁▁▇▁▁
Cold (First Request) 1.154s 1.104s ▂▁█▂▁
Warm (Listen) 456ms 456ms ▁▁█▁▁
Warm (Ready in log) 444ms 445ms ▁▁█▁▁
Warm (First Request) 342ms 346ms ▁▁█▁▁
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 455ms 456ms █▁▁▁█
Cold (Ready in log) 436ms 435ms ▁▁▁▁▁
Cold (First Request) 1.853s 1.856s ████▃
Warm (Listen) 456ms 456ms ▅▅▅▅▁
Warm (Ready in log) 436ms 435ms ▁▁▁▁▁
Warm (First Request) 1.874s 1.857s ▇██▇▃

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 3.852s 3.828s ▁▁█▁▁
Cached Build 3.755s 3.868s ▁▁█▁▁
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 14.228s 14.408s ▄▄▄▄▃
Cached Build 14.386s 14.313s ▅▅▄▄▄
node_modules Size 484 MB 484 MB █████
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
0-a3rz67ec0it.js gzip 157 B N/A -
0~lwfcrlb4v_9.css gzip 115 B 115 B
00h0nz7r436~l.js gzip 13.3 kB N/A -
00ivb_iunbucu.js gzip 13 kB N/A -
02ku7edzc_wf7.js gzip 450 B N/A -
03~yq9q893hmn.js gzip 39.4 kB 39.4 kB
037mxw~u2_79t.js gzip 154 B N/A -
08wow2p6zxy.b.js gzip 7.61 kB N/A -
092lcb3fqrrf9.js gzip 8.52 kB N/A -
0aj~xs1l1g8tg.js gzip 8.53 kB N/A -
0gob4q88vgv3u.js gzip 156 B N/A -
0h35gmp9u328z.js gzip 8.54 kB N/A -
0h6fkavebp.iz.js gzip 8.47 kB N/A -
0hcg0snee_9wv.js gzip 156 B N/A -
0i8jiw50w3l38.js gzip 154 B N/A -
0ino_yf1k3h6k.js gzip 10.4 kB N/A -
0kkeoe2n.293z.js gzip 160 B N/A -
0l~j-k_rjuult.js gzip 70.8 kB N/A -
0mc16gv2x1bet.js gzip 13.7 kB N/A -
0mcszt6vwd60_.js gzip 154 B N/A -
0mgzv7x.0y719.js gzip 169 B N/A -
0moy~uao4dl.m.js gzip 9.19 kB N/A -
0n5ln2l5jgra~.js gzip 152 B N/A -
0ovzdapbcjgc7.js gzip 65.7 kB N/A -
0p2fwrxw124by.js gzip 160 B N/A -
0q50rtpusjy90.js gzip 2.28 kB N/A -
0smgy2grrrlka.js gzip 8.58 kB N/A -
0t1dzhdfh0txh.js gzip 215 B 215 B
0vt7pofxnk8in.js gzip 10.1 kB N/A -
0zid7o0-vupvp.js gzip 225 B N/A -
1030wmumq.hbq.js gzip 156 B N/A -
11yo3xfd6b147.js gzip 12.9 kB N/A -
13.84hqxl_1p7.js gzip 9.76 kB N/A -
13ddjl2tc8beg.js gzip 153 B N/A -
14_hwphcs58-s.js gzip 48.6 kB N/A -
1554wr-t7p6z-.js gzip 8.55 kB N/A -
15pd.z8aymtma.js gzip 155 B N/A -
15tjst79~qy3_.js gzip 1.46 kB N/A -
15z_v00ne4ud0.js gzip 8.47 kB N/A -
17d_m3p4j9w6r.js gzip 5.62 kB N/A -
17yu~3yiu7d2m.js gzip 8.52 kB N/A -
turbopack-0-..rr~~.js gzip 4.15 kB N/A -
turbopack-01..zoj7.js gzip 4.16 kB N/A -
turbopack-01..cl9..js gzip 4.16 kB N/A -
turbopack-0d..r9ub.js gzip 4.15 kB N/A -
turbopack-0f..1w4v.js gzip 4.15 kB N/A -
turbopack-0g..5lrl.js gzip 4.16 kB N/A -
turbopack-0l..d~my.js gzip 4.14 kB N/A -
turbopack-0l..aco3.js gzip 4.16 kB N/A -
turbopack-0p..4qy0.js gzip 4.17 kB N/A -
turbopack-0p..y0cg.js gzip 4.16 kB N/A -
turbopack-0q..cwm4.js gzip 4.15 kB N/A -
turbopack-0z..uyvd.js gzip 4.16 kB N/A -
turbopack-10..ov9~.js gzip 4.16 kB N/A -
turbopack-15..lg62.js gzip 4.16 kB N/A -
0_.49f9yku.5j.js gzip N/A 48.6 kB -
01fz~yk-xpt_j.js gzip N/A 155 B -
03q~t68gnhli5.js gzip N/A 151 B -
03t__~.5lvgeu.js gzip N/A 5.62 kB -
04d6ll75jqx3r.js gzip N/A 9.19 kB -
04ohz21fsta_x.js gzip N/A 155 B -
0583exyh-yhc7.js gzip N/A 9.76 kB -
072lv63r8dcz~.js gzip N/A 8.58 kB -
075t9dxgbf0m8.js gzip N/A 13.7 kB -
0aayvzj0bc0sv.js gzip N/A 65.7 kB -
0ar1~bwpezfgw.js gzip N/A 13.3 kB -
0b8f8fliy73oo.js gzip N/A 156 B -
0bf-.01jgmps6.js gzip N/A 155 B -
0bh~qxl7qejt_.js gzip N/A 156 B -
0c99mq1ez2bke.js gzip N/A 450 B -
0cq-cmde_ws6u.js gzip N/A 8.47 kB -
0ejf9o-j2g.v8.js gzip N/A 161 B -
0fbbsxh94xk-..js gzip N/A 153 B -
0fwf102w10o9~.js gzip N/A 8.52 kB -
0g1-kfhbbj91x.js gzip N/A 153 B -
0gtmn.q_j1v5r.js gzip N/A 10.4 kB -
0h5~v-tahitcf.js gzip N/A 10.1 kB -
0jvqf.i8i3nyb.js gzip N/A 168 B -
0l3rjj4_ye7_a.js gzip N/A 159 B -
0nclq9z6yzzm5.js gzip N/A 1.46 kB -
0nzumcogektg7.js gzip N/A 8.55 kB -
0p5sjual.nuis.js gzip N/A 13 kB -
0p88ggrxiy7bp.js gzip N/A 7.6 kB -
0s.c-cn5eebrx.js gzip N/A 8.47 kB -
0tna7lg6q4zne.js gzip N/A 12.9 kB -
0votdfxr5fb5u.js gzip N/A 2.28 kB -
0ykl9bs_qj.5..js gzip N/A 8.52 kB -
0zfen0tnxp4gh.js gzip N/A 8.55 kB -
101h7adwwavut.js gzip N/A 70.8 kB -
10wkq1h9jzkg..js gzip N/A 225 B -
11bj8iuigzpg6.js gzip N/A 150 B -
11jeywtke9fil.js gzip N/A 154 B -
149ndfh8zfcaz.js gzip N/A 8.53 kB -
turbopack-0~..8_c3.js gzip N/A 4.15 kB -
turbopack-0~..u794.js gzip N/A 4.15 kB -
turbopack-00..vw48.js gzip N/A 4.16 kB -
turbopack-07.._bvj.js gzip N/A 4.14 kB -
turbopack-08..mj...js gzip N/A 4.16 kB -
turbopack-0b..lw-4.js gzip N/A 4.16 kB -
turbopack-0f..y01v.js gzip N/A 4.15 kB -
turbopack-0p..3gzv.js gzip N/A 4.16 kB -
turbopack-0s..kjmy.js gzip N/A 4.17 kB -
turbopack-0y..8oe7.js gzip N/A 4.16 kB -
turbopack-0y..ft~1.js gzip N/A 4.15 kB -
turbopack-10..noez.js gzip N/A 4.15 kB -
turbopack-11..x5rm.js gzip N/A 4.15 kB -
turbopack-17..oaih.js gzip N/A 4.15 kB -
Total 463 kB 463 kB ✅ -24 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 711 B 714 B
Total 711 B 714 B ⚠️ +3 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 433 B 426 B 🟢 7 B (-2%)
Total 433 B 426 B ✅ -7 B

📦 Webpack

Client

Main Bundles
Canary PR Change
5528-HASH.js gzip 5.54 kB N/A -
6280-HASH.js gzip 60.4 kB N/A -
6335.HASH.js gzip 169 B N/A -
912-HASH.js gzip 4.59 kB N/A -
e8aec2e4-HASH.js gzip 62.7 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB
main-app-HASH.js gzip 257 B 254 B 🟢 3 B (-1%)
main-HASH.js gzip 39.3 kB 39.2 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
262-HASH.js gzip N/A 4.59 kB -
2889.HASH.js gzip N/A 169 B -
5602-HASH.js gzip N/A 5.55 kB -
6948ada0-HASH.js gzip N/A 62.7 kB -
9544-HASH.js gzip N/A 61.1 kB -
Total 234 kB 235 kB ⚠️ +672 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 194 B 194 B
_error-HASH.js gzip 183 B 180 B 🟢 3 B (-2%)
css-HASH.js gzip 331 B 330 B
dynamic-HASH.js gzip 1.81 kB 1.81 kB
edge-ssr-HASH.js gzip 256 B 256 B
head-HASH.js gzip 351 B 352 B
hooks-HASH.js gzip 384 B 383 B
image-HASH.js gzip 580 B 581 B
index-HASH.js gzip 260 B 260 B
link-HASH.js gzip 2.51 kB 2.51 kB
routerDirect..HASH.js gzip 320 B 319 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 315 B 315 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.98 kB 7.98 kB ✅ -1 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 125 kB 125 kB
page.js gzip 269 kB 268 kB
Total 394 kB 394 kB ✅ -315 B
Middleware
Canary PR Change
middleware-b..fest.js gzip 616 B 614 B
middleware-r..fest.js gzip 156 B 155 B
middleware.js gzip 43.7 kB 44 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 45.4 kB 45.6 kB ⚠️ +227 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 715 B 718 B
Total 715 B 718 B ⚠️ +3 B
Build Cache
Canary PR Change
0.pack gzip 4.28 MB 4.27 MB 🟢 7.13 kB (0%)
index.pack gzip 110 kB 111 kB
index.pack.old gzip 111 kB 111 kB
Total 4.5 MB 4.49 MB ✅ -6.16 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 333 kB 333 kB
app-page-exp..prod.js gzip 181 kB 181 kB
app-page-tur...dev.js gzip 333 kB 333 kB
app-page-tur..prod.js gzip 181 kB 181 kB
app-page-tur...dev.js gzip 329 kB 329 kB
app-page-tur..prod.js gzip 179 kB 179 kB
app-page.run...dev.js gzip 330 kB 330 kB
app-page.run..prod.js gzip 179 kB 179 kB
app-route-ex...dev.js gzip 76.1 kB 76.1 kB
app-route-ex..prod.js gzip 51.8 kB 51.8 kB
app-route-tu...dev.js gzip 76.1 kB 76.1 kB
app-route-tu..prod.js gzip 51.8 kB 51.8 kB
app-route-tu...dev.js gzip 75.7 kB 75.7 kB
app-route-tu..prod.js gzip 51.6 kB 51.6 kB
app-route.ru...dev.js gzip 75.7 kB 75.7 kB
app-route.ru..prod.js gzip 51.5 kB 51.5 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 43.4 kB 43.4 kB
pages-api-tu..prod.js gzip 33 kB 33 kB
pages-api.ru...dev.js gzip 43.3 kB 43.3 kB
pages-api.ru..prod.js gzip 33 kB 33 kB
pages-turbo....dev.js gzip 52.7 kB 52.7 kB
pages-turbo...prod.js gzip 38.6 kB 38.6 kB
pages.runtim...dev.js gzip 52.7 kB 52.7 kB
pages.runtim..prod.js gzip 38.6 kB 38.6 kB
server.runti..prod.js gzip 62.4 kB 62.4 kB
Total 2.95 MB 2.95 MB ⚠️ +4 B
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/6a1a7428caebf4b0858e558bd20590b71faebe85/next

@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Feb 7, 2026

Tests Passed

@sokra sokra force-pushed the claude/add-webpack-loader-test-kVsLq branch from 7a28fa8 to a686d4d Compare February 9, 2026 08:26
Copy link
Member Author

sokra commented Feb 9, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@sokra sokra force-pushed the claude/add-webpack-loader-test-kVsLq branch 4 times, most recently from c3eece4 to 372aa7f Compare February 11, 2026 15:24
@sokra sokra changed the title Add importModule() support to webpack loaders Turbopack: Add importModule() support to webpack loaders Feb 12, 2026
@sokra sokra marked this pull request as ready for review February 12, 2026 10:46
@sokra sokra requested review from bgw and mischnic February 12, 2026 10:46
@sokra sokra force-pushed the claude/add-webpack-loader-test-kVsLq branch from 21e5326 to 2d5430f Compare February 16, 2026 09:39
@sokra sokra requested a review from lukesandberg February 16, 2026 09:43
@sokra sokra force-pushed the claude/add-webpack-loader-test-kVsLq branch from e86ca83 to 782da5a Compare February 26, 2026 17:35
@sokra sokra requested a review from lukesandberg March 3, 2026 10:31
Copy link
Contributor

@lukesandberg lukesandberg left a comment

Choose a reason for hiding this comment

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

see my open question about error semantics

@sokra sokra requested a review from lukesandberg March 8, 2026 08:20
@sokra sokra force-pushed the claude/add-webpack-loader-test-kVsLq branch from 782da5a to 035be1d Compare March 12, 2026 11:16
@sokra sokra force-pushed the claude/add-webpack-loader-test-kVsLq branch from a74ad88 to 40989f7 Compare March 17, 2026 20:49
Test verifies that a custom webpack loader can use `this.importModule()`
to compile and execute another module at build time and use its exports
to generate loader output.

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
claude and others added 23 commits March 19, 2026 08:00
Turbopack does not support the this.importModule() loader API yet,
so wrap the describe block with IS_TURBOPACK_TEST to skip it.

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
Adds support for the webpack loader API `this.importModule()` in
Turbopack's webpack loader compatibility layer.

Rust side (turbopack-node):
- Add `ImportModule` request/response variants to the loader IPC protocol
- Resolve the requested module via Turbopack's resolver
- Read the source content and return it as compiled code
- Track the file as a dependency for invalidation

JS side (webpack-loaders.ts):
- Add `importModule` method to the loader context
- Execute the returned code in a CommonJS wrapper using Node.js `vm`
- Support both callback and Promise API (matching webpack's signature)

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
Add test coverage for importModule with modules that have their own
dependencies:
- CJS module (cjs-dep.js) that require()s a JSON file (metadata.json)
- ESM module (esm-dep.mjs) with named exports
- config-data.js now imports cjs-dep to exercise transitive deps

Also update the Turbopack importModule implementation:
- Return the resolved file path in the IPC response so the JS side
  can detect ESM modules by extension
- Handle ESM modules (.mjs / export syntax) via native dynamic import()
  instead of the CommonJS vm wrapper

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
Convert importModule test dependencies (config-data, cjs-dep) from
JavaScript to TypeScript to test proper module compilation. On the Rust
side, resolve the requested module using the project's resolve options,
then process it through Turbopack's compilation pipeline (SWC for TS,
JSON parsing, etc.) via AssetContext::process(). Build a module graph
from the resolved module and generate EcmascriptChunkItemContent for
each module in the graph.

On the JS side, implement a mini Turbopack runtime with module cache
and __turbopack_context__ (supporting .r, .i, .s, .v, .n, .x, etc.)
to execute the compiled module factories, similar to webpack's
executeModule.

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
- Add esm-dep import to config-data.ts alongside new URL() and WebAssembly usage
- Create config-data.mjs as an ESM variant also using URL/WebAssembly APIs
- Import config-data.mjs via importModule instead of importing esm-dep directly
- Verify all new values in both webpack and Turbopack test modes

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
… subtype

- Add EcmaScriptModulesReferenceSubType::ImportModule variant for
  importModule resolution/processing in webpack loaders
- Add .q() (export URL), .R() (resolve module path), .w() (sync wasm
  instantiation), and .a() (async module handler) to the mini Turbopack
  runtime in webpack-loaders.ts
- Strip `await` keywords from generated factory code so wasm loader
  modules execute synchronously in the importModule context
- Add resolveProjectPath helper to walk up directories and find the
  Turbopack project root for path resolution
- Create url-wasm-data.ts/.mjs test fixtures importing add.wasm and
  using new URL('./image.png', import.meta.url)
- Test wasm/URL features in Turbopack mode; webpack mode gracefully
  falls back since its importModule doesn't support wasm loading

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
Replace the await-stripping hack with real async module handling:
- Compute AsyncModuleInfo from the module graph and pass it to
  content_with_async_module_info so the code generator emits proper
  ctx.a() wrappers for async modules
- Implement ctx.a() with handleAsyncDependencies that tracks async
  module promises via a Symbol and awaits them before resolution
- Make ctx.w() async using WebAssembly.instantiate instead of sync
  WebAssembly.Module/Instance
- Add ctx.l() for dynamic import support
- Add import("./module.js") to url-wasm-data.ts and url-wasm-data.mjs
  test fixtures

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
- Extract the mini Turbopack runtime from webpack-loaders.ts into
  webpack-loaders-runtime.ts with a single executeModules() entry point
- Deduplicate module ID regex into MODULE_ID_PATH_RE constant and
  resolveModuleIdPath() helper
- Extract ensureModule() to eliminate boilerplate in ctx.s/ctx.v/ctx.n
- Remove redundant require('fs') (already imported at top level)
- Run cargo fmt on webpack.rs

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
In production mode, Turbopack's code generation produces different
patterns than dev mode for async modules, import.meta.url, and
dynamic imports. This commit adds the missing pieces:

- Send `has_top_level_await` from Rust to JS so the mini runtime can
  wrap async module code with the ctx.a() handler (mirroring what
  module_factory() does in item.rs)
- Add ctx.P (resolve absolute path) for import.meta.url patterns
- Add ctx.A (async loader) for production-mode dynamic imports, with
  fallback to ctx.l when the loader wrapper module isn't available
- Add wasm.d.ts for TypeScript type checking in production builds

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
- Remove dead `error` field from ModuleObj interface
- Move `vm` and `url` to top-level imports (consistent with `fs`/`path`)
- Simplify wasm.d.ts to generic wildcard declaration
- Clarify doc comment on has_top_level_await field

https://claude.ai/code/session_01UiNWDNTAGcLmV49FTipUzt
…s/loader support

Thread the module_asset_context through SourceTransform::transform so
that webpack loader importModule calls can resolve aliases and apply
custom loader rules. Previously importModule used a minimal
evaluate_context that lacked the user's resolveAlias and loader
configurations.

- Add asset_context parameter to SourceTransform trait and all impls
- Pass module_asset_context at the call site in process_default_internal
- Use asset_context in ImportModule handler's PlainResolveOrigin
- Add test fixtures for resolveAlias and custom loader rules
…ntime

The importModule mini-runtime was missing the ctx.U (relativeURL)
constructor needed when UrlRewriteBehavior::Relative is active (default
for App Router server contexts). This caused new URL('./image.png',
import.meta.url) patterns to silently fail in webpack loaders.

Also fixed ctx.q to set module.exports directly instead of setting a
default property, matching the full Turbopack runtime's exportUrl
behavior.
…nsform

The SourceTransform trait was updated to require an asset_context parameter
but these two implementations were not updated, causing compilation failures.
Convert 4 importModule calls from absolute paths to relative paths to
test that this.importModule() correctly resolves relative to the
transformed module (app/file.test-file.ts), not the loader file.
Instead of sending individual module code strings via IPC and using a
custom 431-line mini runtime to execute them, use Turbopack's existing
Node.js chunking pipeline (root_entry_chunk_group_asset) to generate a
proper bundle with the real runtime. The JS side evaluates the bundle
with a simple ~100-line in-memory CJS module loader using vm.compileFunction.

This eliminates the duplicated runtime helpers (esmBindings, interopEsm,
asyncModule, etc.) and ensures importModule uses the exact same runtime
behavior as the production build.

Binary assets (WASM, images) are sent base64-encoded and served through
a patched fs module for in-memory createReadStream access.
Two call sites in turbopack/src/lib.rs needed the new asset_context
parameter added to SourceTransform::transform().
Provide the processed source file as the issue source for the
esm_resolve call in the ImportModule handler, consistent with
all other issue sources in the file.
The SourceTransform trait on this branch requires an asset_context
parameter, but json_source_transform.rs (added on canary in #89631)
uses the old 2-parameter signature. Add the parameter to fix the
compilation error when CI merges canary into this branch.
Update root_entry_chunk_group_asset call to use ChunkGroup::Entry enum
variant instead of Vc::cell, matching the updated chunking_context API.
Add comment explaining webpack backward-compat for absolute import paths.

Co-Authored-By: Claude <noreply@anthropic.com>
@sokra sokra force-pushed the claude/add-webpack-loader-test-kVsLq branch from 40989f7 to 6a1a742 Compare March 19, 2026 08:03
@sokra sokra merged commit bdb2f2c into canary Mar 19, 2026
288 of 291 checks passed
@sokra sokra deleted the claude/add-webpack-loader-test-kVsLq branch March 19, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Turbopack team PRs by the Turbopack team. tests Turbopack Related to Turbopack with Next.js.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants