How does withWorkflow handle webpack and turbopack?
How does the Next.js plugin (withWorkflow) handle both webpack and turbopack? What's the integration pattern for both bundlers?
This gives me a comprehensive understanding. Let me summarize the integration pattern:
##How withWorkflow Handles Webpack and Turbopack
The Next.js plugin (withWorkflow) in packages/next/src/index.ts handles both bundlers through a unified loader approach with a single shared SWC transformation. Here's the integration pattern:
###1. Unified Loader Architecture
Both webpack and turbopack use the same loader (packages/next/src/loader.ts), which:
- Uses
@swc/coreto transform source files - Applies the
@workflow/swc-pluginSWC plugin - Transforms files containing
"use workflow","use step"directives, or custom serialization patterns
###2. Turbopack Configuration (Lines 67-109)
// configure the loader if turbopack is being used
if (!nextConfig.turbopack) {
nextConfig.turbopack = {};
}
if (!nextConfig.turbopack.rules) {
nextConfig.turbopack.rules = {};
}
// For each file extension:
for (const key of ['*.tsx', '*.ts', '*.jsx', '*.js', '*.mjs', '*.mts', '*.cjs', '*.cts']) {
nextConfig.turbopack.rules[key] = {
// Next.js 16+ supports content-based conditions for efficiency
...(supportsTurboCondition ? {
condition: {
all: [
// Exclude generated workflow route files
{ not: { path: /[/\\]\.well-known[/\\]workflow[/\\]/ } },
// Only process files with workflow directives or serde patterns
{ content: /(use workflow|use step|from\s+(['"])@workflow\/serde\2|Symbol\.for...)/ },
],
},
} : {}),
loaders: [...existingLoaders, loaderPath],
};
}
Key Turbopack features:
- Uses
turbopack.rulesconfiguration with file extension patterns - In Next.js 16+, uses content-based conditions (
condition.content) to only transform relevant files - Path exclusion (
condition.not.path) to skip generated workflow files - Preserves any existing user-defined rules/loaders
###3. Webpack Configuration (Lines 112-131)
// configure the loader for webpack
const existingWebpackModify = nextConfig.webpack;
nextConfig.webpack = (...args) => {
const [webpackConfig] = args;
// Add loader rule at the end (webpack applies bottom-to-top)
webpackConfig.module.rules.push({
test: /.*\.(mjs|cjs|cts|ts|tsx|js|jsx)$/,
loader: loaderPath,
});
// Chain with user's existing webpack config
return existingWebpackModify ? existingWebpackModify(...args) : webpackConfig;
};
Key Webpack features:
- Uses the
webpackconfig function hook pattern - Adds loader rule via
module.rules.push()(runs bottom-to-top, so runs before built-in SWC) - Chains with existing webpack config if user has one
- Unlike turbopack, doesn't use content matching at the bundler level - the loader itself handles pattern detection
###4. The Shared Loader (loader.ts)
Both bundlers use the same loader which:
-
Pattern Detection: Uses
detectWorkflowPatterns()to check for:"use workflow"/"use step"directives@workflow/serdeimportsSymbol.for('workflow-serialize')patterns
-
Skip Logic: Returns early (no transform) for:
- Generated workflow files (
.well-known/workflow/) - SDK internal files without directives
- Generated workflow files (
-
SWC Transform: Applies the SWC plugin in
'client'mode which:- Generates workflow/step IDs
- Replaces function bodies with stubs that call the runtime
- Emits JSON manifests for discovery
###5. Build-Time Processing
In addition to the loader transform, withWorkflow also runs a NextBuilder (lines 133-163) that:
- Scans
pages,app,src/pages,src/appdirectories - Bundles workflow and step functions for server-side execution
- Generates route handlers at
.well-known/workflow/v1/ - Creates the unified manifest
###Key Differences Summary
| Aspect | Turbopack | Webpack |
|---|---|---|
| Config Location | nextConfig.turbopack.rules | nextConfig.webpack() |
| File Matching | Extension-based rules (*.ts) | Regex test (`/.(ts |
| Content Filtering | At bundler level (v16+) | Inside loader |
| Rule Addition | Merge with existing rules | Push to module.rules |
The beauty of this design is that both bundlers use the identical loader and SWC plugin, ensuring consistent transformation regardless of which bundler Next.js uses.
END OF POST