Changelog: swc v1.2.50


TypeScript metadata for unions (#1421)#

Now swc emits proper metadata for union of string literals, like in codes like

import "reflect-metadata";
const COL_KEY = Symbol("col");
const column = () => {
return (object: any, key: string) => {
Reflect.defineMetadata(COL_KEY, "value", object, key);
class User {
@column() currency!: "usd" | "eur" | "yen";
console.log(Reflect.getMetadata("design:type", User.prototype, "currency"));

Previously, the code printed undefined when compiled with swc which is wrong. It is now String.

No duplicated use strict (#1423)#

For code like

"use strict";
import { arrayUtilities } from "necessary";
const { second } = arrayUtilities;
const elements = [1, 2, 3],
secondElement = second(elements);

swc now emits

"use strict";
var _necessary = require("necessary");
var second = _necessary.arrayUtilities.second;
var elements = [1, 2, 3],
secondElement = second(elements);

In previous versions, the 'use strict' directive was emitted twice.

\r\n in template literals (#1450)#

Previously swc miscompiled the code below.

import { MultipartReader } from "[email protected]/mime/multipart.ts";
import { StringReader } from "[email protected]/io/readers.ts";
// carriage returns added for running on POSIX, not needed if on windows
const content = `--------------------------366796e1c748a2fb\r
Content-Disposition: form-data; name="payload"\r
Content-Type: text/plain\r
const boundary = "------------------------366796e1c748a2fb";
const stringReader = new StringReader(content);
const multipartReader = new MultipartReader(stringReader, boundary);
const formData = await multipartReader.readForm();
for (const entry of formData.entries()) {
console.log("entry", entry);
console.log("formdata", formData);

\r\n in template literals was previously parsed as \n, but it's now parsed as \r\n.

bundler: Many improvements (#1427)#

The bundler of the swc can now handle much more codes. Various bugs are reported via deno and those are all fixed.

New option to prserve class name (#1279)#

To bypass restrictions of rust, swc uses a trick named span hygiene. It modifies identifier at the end to prevent conflict between identifiers. But this operation can be problematic if the name of class matters.

For example,

class MyClass {
prop = 1;
function Decorator(): PropertyDecorator {
return () => undefined;
class MyDecoratedClass {
@Decorator() prop = 1;

depends on the class name.

swc now has an option to preserve class name. See the documentation for .swcrc.

Fix for switch statements in loops (#1415)#

When targeting es3 or es5, swc broke some codes with switch statements in for loops like

export function test(items) {
const infoMap = new WeakMap();
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
let info;
switch (item.type) {
case "branch1":
info = item.method1();
case "branch2":
info = item.method2();
case "branch3":
info = item.method3(
Object.fromEntries( => [item.alias ??, getInfo(t)])
throw new Error("boom");
infoMap.set(item, info); // important
function getInfo(item) {
if (!infoMap.has(item)) {
throw new Error("no info yet");
return infoMap.get(item);

It is now fixed.

New features#

.swcrc option to enable sourcemps. (#1309)#

Previously generating sourcemap required calling apis with proper option. But now swc has an option to enable sourcemap. See the documentation for .swcrc.

@babel/preset-modules (#1439)#

Thanks to @devongovett, swc now has an option to enable bugfix transforms, just like preset-modules of babel.

bugfix/transform-async-arrows-in-class, bugfix/transform-edge-default-parameters and bugfix/transform-tagged-template-caching is added.

Changelog: swc v1.2.48


Parsing of generics (#1405)#

The parser of swc failed to parse below code if tsx is enabled.

function foo<P extends RouteParams = RP, S extends State = RS>(
name: string,
path: string,
...middleware: RouterMiddleware<P, S>[]
): Router<P extends RP ? P : P & RP, S extends RS ? S : S & RS>;

This is reported by a deno team member via discord and now it's fixed.

await (await foo)() (#1395)#

swc now doesn't break code like below.

await(await foo)();

Catch parameters (#1413)#

swc previously miscompiled code like below.

var e = 1;
try {
throw 2;
} catch {

This was the case because the hygiene passs didn't do caclulated renaming operations while handling a catch clause. It's now fixed.

Bundler bugfixes#

Correct handling of export defaults (#1373)#

Previously the bundler miscompiled some export-default-ed functions if it depends on hoisting.

Affected code looks like

import log from "./log.js";
export default async function cli() {
log("It works!");

but it's now fixed.

Performacne improvements#

Parser performance improvement (#1411)#

This pr was intended to reduce wasted memry space and it resulted in 10% performance boost of the parser.

New features#

New jsx transforms (#1408)#

Config api is almost identical with the one of babel.

preset-env with custom config path (#1374)#

Thanks to @mcnicholls, swc now supports using .browserlistrc or package.json in any path for determining environment.

See docs for more details.


Reduction of binary size (#1418)#

Thanks to @Brooooooklyn, the binary size of swc is reduced.

Changelog: swc v1.2.47


bundler: No more infinite loop (#1369)#

This was reported by a Deno user at It was hard to debug because there was no test case, but fix was simple.

bundler: Refinsed cjs support (#1349)#

You can now use regenerator with spack, the bundler for the swc project.

Previously some cjs modules are incorrectly compiled.

bundler: Support for node js builtin modules (#1349)#

The bundler now supports importing / reexporting node js builtin modules. It currently does not touch such imports / exports.

bundler: Various bugfixes (#1349, )#

Some more bugs are fixed.

sourceMappingURL is respected (#1236)#

swc now respectes //# sourceMappingURL= in the input file.

sourceMappingURL relative to input file (#1255)#

swc now resolves source maps from two location (to prevent regression). It will try relative path first, and tries to resolve from root if source map isn't found.

Second check is to preserve breaking existing tools.

parser: Generator function with jsx enabled (#1299)#

swc can now parse code like

export const iteratorMapGenerator = function* <T, R>(
values: IterableIterator<T>,
execute: IteratorExecution<T, R>
): IterableIterator<R> {};


js api: Respect isModule: false (#1258)#

swc now supports parsing a file as script. It would be useful if you are using non-strict mode codes.

cjs: Fix for export default-ed classes (#1213)#

Previously swc miscompiled

import foo from "foo";
class OK {
constructor() {
export default class NotOK {
constructor() {


"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
exports.default = void 0;
var _foo = _interopRequireDefault(require("foo"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule
? obj
: {
default: obj,
class OK {
constructor() {
class NotOK {
constructor() {
console.log(foo); // <- this is wrong
exports.default = NotOK;

which is wrong because of the line commented. It's now fixed.

typescript: Support for type-only namespaces (#1361)#

swc now correctly removes the namespace TestClass from the code below.

export class TestClass {
public testMethod(args: TestClass.TestArgs) {
return args.param1;
declare namespace TestClass {
export interface TestArgs {
param1: boolean;

New features#

bundler: Support for json modules (#1368)#

Reported as axios import error, but actually it was a bug related to importing json files.

New platform support#

windows ia32 (#1367)#

Thanks to @Brooooooklyn, swc now supports windows-ia32.

Changelog: swc v1.2.46


Enums in namespaces (#1340)#

swc had support for namespaces since v1.2.46. But the support was not complete and enums were not handled properly. v1.2.46 adds support for enums, so the code below works as expected.

namespace Test {
export enum DummyValues {
A = "A",
B = "B",

Private class properties (#1353)#

Previously, swc didn't compile some of accesses to private properties of classes. This is fixed as of v1.2.46, by visiting all children in the ast visitor.

Block scoping (#1353)#

Preivously, swc miscompiled some of loops including

function combineOverlappingMatches(matches) {
let hasOverlaps = false;
for (let i = matches.length - 1; i >= 0; i--) {
let currentMatch = matches[i];
let overlap = matches.find((match) => {
return (
match !== currentMatch && match.itemsType === currentMatch.itemsType
if (overlap) {
hasOverlaps = true;
matches.splice(i, 1);
if (hasOverlaps) {

This is now fixed by making block statement transparent in point of view of the block scoping visitor. As this fix applies to all block statements, swc would not break such loops.

This in complex async arrow (#1353)#

Previously, swc errornously changed this in the asynchronous arrow function below.

class A {
val = "1";
async foo() {
try {
return await (async (x) => x + this.val)("a"); // this is undefined
// return await Promise.all(['a', 'b'].map(async (x) => x + this.val)); // this is undefined
} catch (e) {
throw e;
new A().foo();

This is fixed as of v1.2.46. Now swc uses quite general logic for handling such code, and I expect it not to break asynchronous arrows anymore.

bundler: Reserved word as an ident (#1346)#

As the bundler of swc is falttening bundler just like rollup, it had some code to handle difference of identifier context. For example, instanceof in the code below is valid,

import * as foo from "./util";
const x = foo.instanceof;

while instanceof in the code below is invalid.

const instanceof = function () {
// ...

The bundler renames instanceof in below to something different, but it had a bug related to object patterns in assignments. This is now fixed and the bundler will not break code using reserved works in object patterns.

New feautres#

Support for legacy decorators on methods with computed keys (#1347)#

swc now supports typescript code like

class Foo {
[bar]() {}

which uses decorator with computed keys.

Support for TypeScript 4.2 (#1330)#

swc now supports parsing and transcompiling typescript 4.2 files.

Changelog: swc v1.2.45


Escapes in string literals (#1227)#

This was a regression caused by some works related to span (and sourcemaps). I fixed it and swc can emit es5 code as before.

Panic on aliased reexports (#1307)#

This is a regresssion caused by promoting namespaced re-exports like (export * as foo from 'foo') to es2020 from esnext. This is now fixed.

Private class properties (#1306)#

swc previously miscompiled

class Animal {
readonly #name: string;
constructor(name: string) {
this.#name = name;
public noise() {
return this.#name.toUpperCase();

but now swc compiles it correctly. Actaully the fix was addition of one line.

Preserve imports (#1134)#

This is also a regression, but it's now fixed.


Support for typescript namespace (#1325)#

swc now supports typescript namespace.

Changelog: swc v1.2.44


Various dce bugs (#1301)#

The dead code elimination pass of swc was too aggressive and it broke some code. It's now overly conservative, and I'll improve it over time.

Small bundler rework (#1318)#

The swc project uses very creatitve (and strange) approach to handle scoping issue because rust does not allow two mutable reference to same data. Previously, the bundler of swc tried to do something smart. I patched it to depend on the approach I mentioned and as a result, almost all bugs are fixed.

Changelog: swc v1.2.42


bundler: Handle of export * correctly. (#1264)#

Previously the bundler of [swc][] did not exclude default while handling export *. This is wrong and it's fixed. As a result, you can now export default while using export * from './foo'

export { default } from "./foo";
export * from "./foo";

The code above now works.

bundler: Fix statement ordering. (#1264)#

Order of statements are now preserved. Previously, the bundler only considers about dependancy, but it resulted in some bugs so I patched it to preserve original order.

bundler: Group statements from same module. (#1264)#

While flattening all statements, the bundler now prefers statements from same module. It will help debugging the bundle, as related statements live at simillar place.

fixer: Handle assignmnet in new exprssion correctly. (#1264)#

Previously [swc][] errornously removed parenthesis from const a = new (a = b);. It resulted in const a = new a = b;, which has different meaning.

fixer: Handle comma expression in new exprssion correctly. (#1264)#

Previously [swc][] errornously removed parenthesis from const a = new (a, b);. It resulted in const a = new a, b; , which has different meaning. This is now fixed.

bundler: Prevent duplicate variable. (#1296)#

This bug occurrs when export * as foo is mixed with export * from '' more than three times, and with import. It's now fixed and bundler bundles it correctly.

bundler: Handle keywords. (#1296)#

EcmaScript allows using keywords as an identifier in some places. As the bundler flatten statements, it made a variable named instanceof when it met export { foo as instanceof }.

But this is a bug and it's now renamed to something other.

dce: Prevent tags in tagged template literals. (#1296)#

The dce was too aggresive and it removed tagged template literals. It now preserves as tagged template literals can have side effect.


If you are using some old target, you are not affected.

dce: Handle throw statement correctly. (#1296)#

Again, it errornously removed something used by throw statement. I patched it to mark argument as used correctly.

Changelog: swc v1.2.41


Codegen of string literals (#1287)#

The code generator of swc tries to preserve the original input, and it resulted in some bugs. It's now fixed by a huge breaking change - storing quote info in the string literal.

Note that this patch is also included in the new version of deno, which is also published today.

Normalization of \r\n in template literals (#1286)#

Thanks to @Liamolucko, swc now follows spec more closely.

\r\n in template literals are now normlized as \n.

Span of decalred items (#1282)#

Now span of various declared nodes like class declarations or function declarations include span of declare token. Due to how codegen determine codegen print comments, I expect this fix to make codegen print comments correctly.

No hang on invalid character (#1274)#

Previously, the lexer of swc hangs on invalid input. This was because it did not update the position of the character in case of error. The fix was trivial, but it's important for some environments like deno shell, which gets input from stdin.

Parse ?? correctly (#1270)#

Some tokens were not parsed correctly on ecmascript mode.

New package#

I wish this was a package about TypeScript type checkers, but I am still working hard on it still working hard. Instead, we introduce a package that allows swc to run on the web.

@swc/wasm-web is a new package which can be used for the web.

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.