fix(builders): override sideEffects: false for discovered workflow/step/serde entries#1598
Conversation
…p/serde entries When node_modules packages include "sideEffects": false in their package.json, esbuild drops bare imports from the virtual-entry.js file. This is incorrect because the SWC compiler transform injects side-effectful registration code (workflow IDs, step IDs, class serialization) into these modules. Fix: return the resolved path alongside sideEffects: true from the onResolve handler so esbuild uses the plugin's resolution result instead of re-reading the package.json.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: f9809bf The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
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 |
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 25 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express workflow with 50 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.all with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.race with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro workflow with 10 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 25 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 50 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 10 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 25 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 50 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) stream pipeline with 5 transform steps (1MB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro 10 parallel streams (1MB each)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) fan-out fan-in 10 streams (1MB each)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests🌍 Community Worlds (60 failed)mongodb (3 failed):
redis (2 failed):
turso (55 failed):
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
✅ 📋 Other
|
sideEffects: false for discovered workflow/step/serde entries
There was a problem hiding this comment.
Pull request overview
This PR updates the @workflow/builders SWC esbuild plugin to prevent esbuild from tree-shaking away bare imports of discovered workflow/step/serde entry modules when upstream packages declare "sideEffects": false, ensuring SWC-injected registration side effects (workflow IDs, step IDs, class serialization) still run.
Changes:
- Add a
sideEffectEntriesoption to the SWC esbuild plugin and override resolution withsideEffects: truefor matching discovered entries. - Wire
sideEffectEntriesinto the three bundle creation sites (steps, workflows, client). - Add a dedicated test suite validating preserved/dropped bare imports and warning behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| packages/builders/src/swc-esbuild-plugin.ts | Adds sideEffectEntries support and returns { path, sideEffects: true } for discovered entries to prevent tree-shaking of bare imports. |
| packages/builders/src/swc-esbuild-plugin.test.ts | Adds tests covering sideEffectEntries behavior across bundled/non-bundled scenarios and warning suppression. |
| packages/builders/src/base-builder.ts | Passes discovered step/workflow/client + serde entry paths into sideEffectEntries for each bundle type. |
| .changeset/wise-inks-ripe.md | Publishes a patch changeset describing the sideEffects override behavior change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…mlink compatibility Extract withRealpaths() helper and use it for both normalizedEntriesToBundle and sideEffectEntries at all three bundle sites. This ensures the sideEffects override works correctly under pnpm/workspace symlinked layouts where enhanced-resolve may return realpaths that differ from the original discovered file paths.
VaguelySerious
left a comment
There was a problem hiding this comment.
AI review: no blocking issues
…y sideEffectEntries is set When entriesToBundle is not set (workflow/client bundles), only top-level import statements need the sideEffects override — transitive imports from deep within the bundle are not bare imports and don't need resolution. Skip enhanced-resolve for non-import-statement kinds to reduce overhead.
…1-refresh * origin/main: (21 commits) Fix node-module-error plugin matching identifiers in multi-line comments (#1554) fix(swc-plugin): use binding name for class expression method registrations (#1599) fix(builders): override `sideEffects: false` for discovered workflow/step/serde entries (#1598) [world-vercel] align header names to `x-vercel-workflow-*` convention (#1602) [docs] Add vercel world consumer function security documentation (#1543) Make `start()` types `unknown` when `deploymentId` is provided (#1367) fix(next): stop force-setting WORKFLOW_PUBLIC_MANIFEST=1 during next dev (#1597) Version Packages (beta) (#1593) [docs] Tidy world API docs and document new stream helpers (#1581) Rename 'Workflow Development Kit' / 'DevKit' to 'Workflow SDK' (#1595) [world] Use zod/v4 in queue files to match @workflow/world schemas (#1588) [ai] Fix fatal stream errors surfacing as [object Object] (#1589) [web] Fix server crash on unmatched routes (#1590) docs: rename 'Complex Example' to 'Instance Methods as Steps' (#1592) Version Packages (beta) (#1563) [core] Extend flow route duration to "max" and fail runs where replay takes too long (#1567) fix: check target run capabilities before encrypting hook payloads (#1572) [core] Combine initial run fetch, event fetch, and run_started event creation (#1569) [docs] Split World API docs into sub-pages, update skill.md (#1457) [nitro] Preserve workflow step registration side effects (#1386) ... # Conflicts: # skills/workflow/SKILL.md
Summary
"sideEffects": falsein theirpackage.jsonsideEffectEntriesoption to the SWC esbuild plugin that returns{ path, sideEffects: true }from theonResolvehandler for discovered entries, overriding the package.json flagDetails
When the virtual-entry.js uses bare side-effect imports (
import 'package-name') for modules discovered to contain workflow/step/serde directives, esbuild's tree-shaking respects"sideEffects": falsefrom the package'spackage.jsonand silently drops these imports. This caused:Ignoring this import because "..." was marked as having no side effectsThe fix uses esbuild's plugin
onResolveAPI to return{ path: resolvedPath, sideEffects: true }for discovered entry files. Both thepathandsideEffectsmust be returned together — returning only{ sideEffects: true }without a path causes esbuild to fall through to its own resolver, which re-readspackage.jsonand re-applies thesideEffects: falseflag.The override is applied in three bundle creation sites:
Tests
6 new tests covering:
sideEffectEntriesis provided (with and withoutentriesToBundle)sideEffectEntriesis NOT provided (baseline behavior)ignored-bare-importwarnings produced for overridden entriessideEffects: falseare also handledsideEffects: falserespected (targeted override)