mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
## 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-data` → `alias-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. --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Tobias Koppers <[email protected]>
44 lines
1.1 KiB
JavaScript
44 lines
1.1 KiB
JavaScript
import {
|
|
title,
|
|
items,
|
|
cjsGreeting,
|
|
version,
|
|
esmLabel,
|
|
imageUrl,
|
|
wasmAddResult,
|
|
dynamicValue,
|
|
mjsTitle,
|
|
mjsEsmLabel,
|
|
mjsImageUrl,
|
|
mjsWasmAddResult,
|
|
mjsDynamicValue,
|
|
aliasValue,
|
|
aliasDepLabel,
|
|
customDataValue,
|
|
consumedValue,
|
|
} from './file.test-file'
|
|
|
|
export default function Page() {
|
|
return (
|
|
<div>
|
|
<p id="title">{title}</p>
|
|
<p id="items">{items}</p>
|
|
<p id="cjs-greeting">{cjsGreeting}</p>
|
|
<p id="version">{version}</p>
|
|
<p id="esm-label">{esmLabel}</p>
|
|
<p id="image-url">{imageUrl}</p>
|
|
<p id="wasm-add-result">{wasmAddResult}</p>
|
|
<p id="dynamic-value">{dynamicValue}</p>
|
|
<p id="mjs-title">{mjsTitle}</p>
|
|
<p id="mjs-esm-label">{mjsEsmLabel}</p>
|
|
<p id="mjs-image-url">{mjsImageUrl}</p>
|
|
<p id="mjs-wasm-add-result">{mjsWasmAddResult}</p>
|
|
<p id="mjs-dynamic-value">{mjsDynamicValue}</p>
|
|
<p id="alias-value">{aliasValue}</p>
|
|
<p id="alias-dep-label">{aliasDepLabel}</p>
|
|
<p id="custom-data-value">{customDataValue}</p>
|
|
<p id="consumed-value">{consumedValue}</p>
|
|
</div>
|
|
)
|
|
}
|