Changelog: swc v1.2.40


design;type for typescript enums (#1248)#

Decorator metadata can be used to create very convenient tools. swc now supports it more correctly.

For example, the code below works with swc@1.2.40+.

enum MyEnum {
x = "xxx",
y = "yyy",
class Xpto {
value!: MyEnum;
function Decorator() {
return function (...args) {};

regenerator: ternary with await (#1228)#

Previously, swc miscompiled the code below because regenerator had a bug.

const source = Math.random() < 2 ? "matilda" : "fred";
const details = {
_id: "1",
async function request(path) {
return `success:${path}`;
(async () => {
const obj =
source === "matilda"
? details
: await request(`/${details._id}?source=${source}`);
console.log({ obj });

I tried hard to mimic its logic as much as possible, but original codebase depends on dynamic nature of javascript and requires shared &mut, so logic differs even if it's a port.

Anyway, the code above works properly with swc@1.2.40.

bundler: Circular imports in wrapped module (#1234)#

import * as path from "[email protected]/path/mod.ts";
const { a, } = { a: 3, b: "bar" };
console.log(a, rest, path);

Previously the swc-based bundlers (deno bundle and spack) had a bug which occurs

  • Module path is loaded as a wrapped module.
  •[email protected]/path/mod.ts has circular imports internally.

This is now fixed and such code works well.

bundler: export * as alias (#1234)#

I got the bug report from deno issue tracker, and fixed it by preserving export info.


bundler: fix deglobbing logic (#1234)#

Previously, deglobbing logic was to aggressive so that it replaces log.handlers.FileHandler into FileHandler.

It was because previous logic only checked the originating module. This is fixed by comparing symbols.

import * as log from "";
export async function myCLI(): Promise<void> {
await log.setup({
handlers: {
file: new log.handlers.FileHandler("DEBUG", {
filename: "my.log",
console: new log.handlers.ConsoleHandler("INFO"),
loggers: {
default: {
level: "DEBUG",
handlers: ["console", "file"],
if (import.meta.main) {

codegen: unicode escape sequnces (#1242)#

There was a bug which ignores the value part in unicode escapes while printing result code.

bundler: importing a module multiple time (#1242)#

import Head from "";
import * as Head2 from "";
console.log(Head, Head2);

I was a bit curious about the usecase, but I fixed it to be spec-compilant and it works well.

bundler: export default in reexport (#1245)#

Previously the bundler broke code below,

import * as c from "[email protected]/mod.ts";
const s = "one FINE day";
console.log("camel:", c.camelCase(s));

because[email protected]/mod.ts is defined as

export { default as camelCase } from "./camelCase.ts";
export { default as constantCase } from "./constantCase.ts";
export { default as dotCase } from "./dotCase.ts";
export { default as headerCase } from "./headerCase.ts";
export { default as lowerCase } from "./lowerCase.ts";
export { default as lowerFirstCase } from "./lowerFirstCase.ts";
export { default as normalCase } from "./normalCase.ts";
export { default as paramCase } from "./paramCase.ts";
export { default as pascalCase } from "./pascalCase.ts";
export { default as pathCase } from "./pathCase.ts";
export { default as sentenceCase } from "./sentenceCase.ts";
export { default as snakeCase } from "./snakeCase.ts";
export { default as swapCase } from "./swapCase.ts";
export { default as titleCase } from "./titleCase.ts";
export { default as upperCase } from "./upperCase.ts";
export { default as upperFirstCase } from "./upperFirstCase.ts";

The bundler can now handle those kinds of reexports properly.

bundler: export specifiers without alias (#1246)#

Reported with

The bundler didn't handled it because while checking for reexports, the bundler modifies ast so that reexport specifier without alias does not exist. But in some circumstances, the bundler injects export specifiers without alias to maintain semantic of a module.

bundler: improve sorting (#1246)#

Reported with

The bug was that, the code below was not treated as an initializer of globalContext.

if (typeof window !== "undefined") {
globalContext = window;
} else if (typeof self !== "undefined") {
globalContext = self;
} else {
globalContext = {};

The fix was trivial as there were already utilities to detect initialization.

bundler: top-level-await flattening (#1246)#

// main.ts
import { log } from "./deps.ts";
await log.setup({});
// deps.ts
export * as log from "./log.ts";
// log.ts
export async function setup() {}
await setup();

This resulted in a bug because the bundler assumed wrapped module is not async.

At first glance, it seems hard to fix, but there's a simple trick.

const log_ts = await(async function () {
export async function setup() {}
await setup();

This fixes the issue.

codegen: multiline jsx texts (#1241)#

Affected code looks like

function Component() {
return React.createElement("div", {
name: "A

async_to_generator: async method (#1241)#

class Service {
async is(a: string): Promise<boolean> {
return a.toUpperCase() === a;

should be

class Service {
is(a) {
return _asyncToGenerator(function* () {
yield Promise.resolve();
return a.toUpperCase() === a;

but it was transpiled as

class Service {
is(a) {
return _asyncToGenerator(function* (a) {
yield Promise.resolve();
return a.toUpperCase() === a;

which has wrong a in the parameter. This is fixed by simply removing all parameters.

regenerator: no useless statements (#1241)#

I made a mistake while proting regenerator pass. The mistake resulted in bit larger code, and very hard time debugging.

Now I find the cause of case number being not same as one of regenerator, so future bugs will be easily fixed.

regenerator: track finally properly (#1241)#

Again, regenerator pass depends on shared mutable state hardly, and I made mistake while porting it. Fixing it required some dirty code because we have to modify a value stored in a list after inserting it, but it's in managable range.