swc: Circular class imports break when upgrading 1.2.205 -> 1.2.206

Describe the bug

When upgrading from 1.2.205 to 1.2.206, SWC changes how it compiles exports.

In version 1.2.205:

Screenshot 2022-06-27 at 21 36 27

In version 1.2.206:

Screenshot 2022-06-27 at 21 49 11

I have a bunch of model classes in my codebase, which of course circularly reference each other.

To avoid circularity problems, the ORM I use uses the “late binding” hack popular in the JavaScript community, referencing bindings with anonymous functions, e.g. @OneToMany(() => Book) instead of just @OneToMany(Book). However, this seems to no longer work after the compilation change of SWC 1.2.206.

Input code

// Person.ts
import { Book } from "./Book";

export class Person {
    books = new Collection<Book>();
}

// Book.ts
import { Person } from "./Person";

export class Book {
    author?: Person;
}

Config

{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": false,
      "decorators": true,
      "dynamicImport": true
    },
    "transform": {
      "legacyDecorator": true,
      "decoratorMetadata": true
    },
    "target": "es2018",
    "baseUrl": "src",
    "paths": {
      "lib/*": ["lib/*"],
      "app/*": ["app/*"],
      "fixtures/*": ["fixtures/*"],
      "test-utils/*": ["test-utils/*"]
    }
  },
  "module": {
    "type": "commonjs"
  }
}

Playground link

No response

Expected behavior

Running this code after building it, or running it with node -r @swc/register should work.

Actual behavior

Running the code after building it, or running it with node -r @swc/register, produces this error:

$ NODE_ENV=script node -r @swc/register src/fixtures/command.ts reapply
/Users/kelley/mywheels/api/src/app/core/models/Fleet.ts:6
    get: ()=>Fleet,
             ^

ReferenceError: Cannot access 'Fleet' before initialization
    at Object.get [as Fleet] (/Users/kelley/mywheels/api/src/app/core/models/Fleet.ts:16:14)
    at Object.<anonymous> (/Users/kelley/mywheels/api/src/app/core/models/FleetContractType.ts:13:16)
    at Module._compile (node:internal/modules/cjs/loader:1103:14)
    at Module._compile (/Users/kelley/mywheels/api/node_modules/pirates/lib/index.js:99:24)
    at Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
    at Object.newLoader [as .ts] (/Users/kelley/mywheels/api/node_modules/pirates/lib/index.js:104:7)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
error Command failed with exit code 1.

Version

1.2.206

Additional context

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 16
  • Comments: 35 (16 by maintainers)

Most upvoted comments

For those using TypeORM, using the Relation type resolved the issue for me https://typeorm.io/#relations-in-esm-projects

@magic-akari

It turns out that indeed, the Relation<> trick, as described in https://github.com/swc-project/swc/issues/5047#issuecomment-1188874962, indeed also works for MikroORM.

(And in fact, in most situations our codebase already uses MikroORM’s IdentifiedReference<>, which effectively also solves it, I just apparently hadn’t applied it everywhere yet in our codebase, and hence kept running into the bug.)

Thanks for helping!

I see and ah… I think I got the exact cause. This seems like #1457

Oh, that’s a terrible reason. If someone really doesn’t want hoisted import, there’s a TS syntax import x = require() for them.

Yes, @Nesci28 is correct that that defineProperty change per 1.2.206 causes the bug (whether indirectly or not).

I made a small repo with a reproduction of the bug, as it occurs in our codebase, that uses MikroORM (and reflect-metadata):

Indeed, I’m not sure “whose bug” this is. Whether it’s swc, MikroORM, or reflect-metadata. But the combination breaks.

I’d love to resolve the situation though, because I’m currently stuck on swc 1.2.205 😃

Same issue in a NestJS project with Circular dependancies in the DI. swc/core version to 1.2.205 does not have that bug.

Manually droping the lines:

Object.defineProperty(exports, "TestClass", {
    enumerable: true,
    get: ()=>TestClass
});

Below the line:

let TestClass = class TestClass {
...
}

Fixes the issue.

Is this issue still active? we pinned the version to 1.2.205 due to this bug which we are using TypeOrm with swc

check https://github.com/swc-project/swc/issues/5047#issuecomment-1188874962