Skip to content

fix: set useDefineForClassFields to false in tsup swc config#82

Merged
isatiso merged 2 commits intomainfrom
fix/tsup-class-fields-lowering
Apr 13, 2026
Merged

fix: set useDefineForClassFields to false in tsup swc config#82
isatiso merged 2 commits intomainfrom
fix/tsup-class-fields-lowering

Conversation

@isatiso
Copy link
Copy Markdown
Owner

@isatiso isatiso commented Apr 13, 2026

Problem

tsup's swcPlugin (activated by emitDecoratorMetadata: true) intercepts all .ts files via an esbuild onLoad hook and transforms them with SWC instead of esbuild. The plugin hardcodes jsc.target: "es2022" and does not pass useDefineForClassFields: false, so SWC preserves native ES class field syntax.

This causes class field initializers to execute before constructor parameter assignments at runtime:

var HttpServer = class {
  config_data;                                          // undefined
  port = this.config_data.get('http.port');              // TypeError!
  constructor(injector, config_data) {
    this.config_data = config_data;                     // too late
  }
}

Fix

Add a root-level tsup.config.ts that sets swc.jsc.transform.useDefineForClassFields: false. tsup's config resolution (via joycon) walks up from each package directory, so all packages automatically pick up this config.

After the fix, the output correctly moves field initializers into the constructor body, after parameter assignments:

var HttpServer = class {
  constructor(injector, config_data) {
    this.injector = injector;
    this.config_data = config_data;
    this.sockets = new Set();
    this.port = this.config_data.get('http.port');       // works
    this.keepalive_timeout = this.config_data.get('http.server.keepalive_timeout');
    this.terminate_timeout = this.config_data.get('http.server.terminate_timeout') ?? 4000;
  }
}

Verification

  • pnpm -r build passes, all packages use the root config
  • pnpm -r test passes (1 pre-existing network-dependent failure in rabbitmq unrelated to this change)

isatiso and others added 2 commits April 13, 2026 05:37
tsup's swcPlugin (enabled by emitDecoratorMetadata) bypasses esbuild's
TypeScript handling and hardcodes jsc.target to es2022, which preserves
native class field syntax. This causes class field initializers to run
before constructor parameter assignments, breaking classes that depend
on injected constructor parameters (e.g. HttpServer.port accessing
this.config_data.get() before config_data is assigned).

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Releases:
  @tarpit/barbeque@2.0.1
  @tarpit/cli@2.0.1
  @tarpit/config@2.0.1
  @tarpit/content-type@2.0.1
  @tarpit/core@2.0.1
  @tarpit/cron@2.0.1
  @tarpit/dora@2.0.1
  @tarpit/http@2.1.1
  @tarpit/judge@2.0.1
  @tarpit/mongodb@2.0.2
  @tarpit/negotiator@2.0.1
  @tarpit/rabbitmq@2.0.2
  @tarpit/schedule@2.0.1
  @tarpit/transformer@2.0.1
  @tarpit/type-tools@2.0.1
@isatiso isatiso force-pushed the fix/tsup-class-fields-lowering branch from e4fa1da to 8c4fe80 Compare April 13, 2026 05:53
@isatiso isatiso merged commit 26c3d4f into main Apr 13, 2026
15 checks passed
@isatiso isatiso deleted the fix/tsup-class-fields-lowering branch April 13, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant