fix(swc-plugin): rewrite anonymous export default class to const declaration#1601
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.
…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.
🦋 Changeset detectedLatest commit: f0af6af The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests🌍 Community Worlds (59 failed)mongodb (2 failed):
redis (2 failed):
turso (55 failed):
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
✅ 📋 Other
|
There was a problem hiding this comment.
Pull request overview
Updates the SWC workflow transform to ensure default-exported classes have a module-scope binding name when the plugin needs to emit registration code (serde and/or step/workflow method registrations), avoiding runtime ReferenceError for anonymous export default class { ... } patterns.
Changes:
- Add transformer logic to defer and rewrite anonymous default class exports into
const <name> = class <name> { ... }; export default <name>;. - Ensure named default class exports set the binding name so generated registration code consistently references the in-scope identifier.
- Add/extend fixtures and spec documentation describing the new rewriting behavior.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/swc-plugin-workflow/transform/src/lib.rs | Implements deferred rewrite for anonymous default class exports and binding-name tracking for default-exported classes. |
| packages/swc-plugin-workflow/spec.md | Documents anonymous default class export rewriting behavior and expected output. |
| .changeset/tall-owls-glow.md | Publishes a patch changeset describing the fix. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/input.js | Adds fixture input for named export default class MyService { ... } with serde + step. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/output-step.js | Expected step-mode output for named default class export. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/output-workflow.js | Expected workflow-mode output for named default class export. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/output-client.js | Expected client-mode output for named default class export. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/input.js | Adds fixture input for anonymous export default class { ... } with serde + step. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/output-step.js | Expected step-mode output showing const-decl rewrite and correct registrations. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/output-workflow.js | Expected workflow-mode output showing const-decl rewrite and correct registrations. |
| packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/output-client.js | Expected client-mode output showing const-decl rewrite and correct registrations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…s handling - Remove dead Expr::Class handler in visit_mut_export_default_expr (SWC wraps parenthesized form in Expr::Paren, so it never fires) - Extract class_needs_binding_rewrite() helper, eliminating duplicated detection logic and unnecessary body clones - Add debug_assert for mutual exclusivity of default_workflow_exports and default_class_exports - Clarify spec.md on self-name behavior difference between serde and step-only classes
📊 Benchmark Results
workflow with no steps💻 Local Development
workflow with 1 step💻 Local Development
workflow with 10 sequential steps💻 Local Development
workflow with 25 sequential steps💻 Local Development
workflow with 50 sequential steps💻 Local Development
Promise.all with 10 concurrent steps💻 Local Development
Promise.all with 25 concurrent steps💻 Local Development
Promise.all with 50 concurrent steps💻 Local Development
Promise.race with 10 concurrent steps💻 Local Development
Promise.race with 25 concurrent steps💻 Local Development
Promise.race with 50 concurrent steps💻 Local Development
workflow with 10 sequential data payload steps (10KB)💻 Local Development
workflow with 25 sequential data payload steps (10KB)💻 Local Development
workflow with 50 sequential data payload steps (10KB)💻 Local Development
workflow with 10 concurrent data payload steps (10KB)💻 Local Development
workflow with 25 concurrent data payload steps (10KB)💻 Local Development
workflow with 50 concurrent data payload steps (10KB)💻 Local Development
Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
stream pipeline with 5 transform steps (1MB)💻 Local Development
10 parallel streams (1MB each)💻 Local Development
fan-out fan-in 10 streams (1MB each)💻 Local Development
SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
Summary
ReferenceErrorwhenexport default class { ... }(anonymous) has serde or step methodsexport default class { ... }→const __defaultClass = class __defaultClass { ... }; export default __defaultClass;current_class_binding_namefor named default class exports (export default class Foo { ... }) so registration uses the class nameStacked on #1599
Details
When an anonymous class is exported as the default export:
The SWC plugin generates registration code at module scope that needs to reference the class by name (e.g.,
registerStepFunction("...", ClassName.prototype["process"])). Without a binding name, the fallback was"AnonymousClass"which doesn't exist as a variable — causing aReferenceErrorat runtime.Fix
Two changes in the SWC plugin:
visit_mut_export_default_decl(DefaultDecl::Class): Detects anonymous classes with serde/step methods, generates a unique name (__defaultClass), setscurrent_class_binding_name, and defers the rewrite tovisit_mut_module_items.visit_mut_export_default_expr(Expr::Class): Same logic for the expression form (e.g.,export default (class { ... })).visit_mut_module_items: Processes the deferred rewrites — removes the originalexport default, insertsconst __defaultClass = class __defaultClass { ... };, and re-exports withexport default __defaultClass;.Named default class exports (
export default class Foo { ... }) don't need rewriting —Foois already in scope. The fix just setscurrent_class_binding_nameso the transformer usesFooconsistently.Tests
6 new tests (2 fixtures × 3 modes):
export-default-class-anonymous: Anonymous default class with serde + step method → verifies rewrite toconst __defaultClass = class __defaultClass { ... }; export default __defaultClass;with correct registration codeexport-default-class-named: Named default class (export default class MyService { ... }) → verifies no rewrite needed,MyServiceused for all registrations144 total SWC tests pass (138 existing + 6 new).