1. Project Overview & Quickstart (swc-project/LICENSE)
LICENSE
Open-source repository swc-project/LICENSE
Repository Details
- Repository: swc-project/LICENSE
- Primary Language: Code
Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.
2. Official Technical Reference & Guides (swc-project/docs)
File: README.md
SWC Website
Built Using
File: apps/website/docs/blog/perf-swc-vs-babel.mdx
image: https://user-images.githubusercontent.com/9113740/140682117-5e9f460e-c8f1-447c-88a0-e75f80779260.png
import Authors, { Author } from '@components/Authors';
Performance Comparison of SWC and Babel
JavaScript is single-threaded. The JS thread is not a good place to do heavy computation. Let's talk about babel and swc, which are both computation-heavy.
Synchronous Benchmark
Let's do a benchmark for the single-core workload. Note this uses transformSync, which is rarely useful in the wild.
[transform]
swc (es3) x 616 ops/sec ยฑ4.36% (88 runs sampled)
swc (es2015) x 677 ops/sec ยฑ2.01% (90 runs sampled)
swc (es2016) x 1,963 ops/sec ยฑ0.45% (93 runs sampled)
swc (es2017) x 1,971 ops/sec ยฑ0.35% (94 runs sampled)
swc (es2018) x 2,555 ops/sec ยฑ0.35% (93 runs sampled)
swc-optimize (es3) x 645 ops/sec ยฑ0.40% (90 runs sampled)
babel (es5) x 34.05 ops/sec ยฑ1.15% (58 runs sampled)SWC is very fast. Although swc (es3) does more work than babel (es5), swc (es3) is faster than babel (es5).
Real-World Benchmark
transformSync and transformFileSync are rarely used in the real world, as it blocks the current thread. await Promise.all() is frequently used as is better than:
for (const promise of promises) {
await promise;
}Let's create a benchmark for actual real-world usage using Promise.all().
Ideal Case
First, I created a benchmark for the ideal case. It invokes [n] promises at once where n is the number of physical CPU cores. See node-swc repository for the full code.
const os = require("os");
const cpuCount = os.cpus().length;
const SOURCE = `
// See the link above
`;
const SUITES = [
// ...
// See the link above
];
const arr = [];
for (let i = 0; i < cpuCount / 2; i++) {
arr.push(0);
}
console.info(`CPU Core: ${cpuCount}; Parallelism: ${arr.length}`);
console.info(
`Note that output of this benchmark should be multiplied by ${arr.length} as this test uses Promise.all`
);
SUITES.map(args => {
const [name, requirePath, fn] = args;
const func = fn.bind(null, require(requirePath));
bench(name, async done => {
await Promise.all(arr.map(v => func()));
done();
});
});I ran benchmarks on my old desktop. It has E3-v1275 and 24GB of ram. The output below is copied as-is from benchmark output.
CPU Core: 8; Parallelism: 4
Note that output of this benchmark should be multiplied by 4 as this test uses Promise.all
[multicore]
swc (es3) x 426 ops/sec ยฑ3.75% (73 runs sampled)
swc (es2015) x 422 ops/sec ยฑ3.57% (74 runs sampled)
swc (es2016) x 987 ops/sec ยฑ2.53% (75 runs sampled)
swc (es2017) x 987 ops/sec ยฑ3.44% (75 runs sampled)
swc (es2018) x 1,221 ops/sec ยฑ2.46% (77 runs sampled)
swc-optimize (es3) x 429 ops/sec ยฑ1.94% (82 runs sampled)
babel (es5) x 6.82 ops/sec ยฑ17.18% (40 runs sampled)Now, we need to multiply it by 4, as we do 4 operations per iteration.
swc (es3) x 1704 ops/sec ยฑ3.75% (73 runs sampled)
swc (es2015) x 1688 ops/sec ยฑ3.57% (74 runs sampled)
swc (es2016) x 3948 ops/sec ยฑ2.53% (75 runs sampled)
swc (es2017) x 3948 ops/sec ยฑ3.44% (75 runs sampled)
swc (es2018) x 4884 ops/sec ยฑ2.46% (77 runs sampled)
swc-optimize (es3) x 1716 ops/sec ยฑ1.94% (82 runs sampled)
babel (es5) x 27.28 ops/sec ยฑ17.18% (40 runs sampled)This is an actual result.
The performance of babel (es5) is dropped. Async is not free. Even though, 34.05 ops/sec => 27.28 ops/sec is much better than I expected.
Benchmark for Many Operations
I modified the benchmark file slightly to make it creates 100 promises per iteration.
CPU Core: 8; Parallelism: 100
Note that output of this benchmark should be multiplied by 100 as this test uses Promise.all
[multicore]
swc (es3) x 21.99 ops/sec ยฑ1.83% (54 runs sampled)
swc (es2015) x 19.11 ops/sec ยฑ3.39% (48 runs sampled)
swc (es2016) x 55.80 ops/sec ยฑ6.97% (71 runs sampled)
swc (es2017) x 62.59 ops/sec ยฑ2.12% (74 runs sampled)
swc (es2018) x 81.08 ops/sec ยฑ5.22% (75 runs sampled)
swc-optimize (es3) x 18.60 ops/sec ยฑ2.13% (50 runs sampled)
babel (es5) x 0.32 ops/sec ยฑ19.10% (6 runs sampled)It must be multiplied by 100 as above.
swc (es3) x 2199 ops/sec ยฑ1.83% (54 runs sampled)
swc (es2015) x 1911 ops/sec ยฑ3.39% (48 runs sampled)
swc (es2016) x 5580 ops/sec ยฑ6.97% (71 runs sampled)
swc (es2017) x 6259 ops/sec ยฑ2.12% (74 runs sampled)
swc (es2018) x 8108 ops/sec ยฑ5.22% (75 runs sampled)
swc-optimize (es3) x 1860 ops/sec ยฑ2.13% (50 runs sampled)
babel (es5) x 32 ops/sec ยฑ19.10% (6 runs sampled)Why does the performance of SWC not drop drastically? The secret is Node.js. Node.js internally manages a worker thread pool, and SWC runs on it. Thus, even though you create 100 promises at once, the number of worker threads is much smaller than it.
Conclusion
| name | 1 core, sync | 4 promises | 100 promises |
|---|---|---|---|
| swc (es3) | 616 ops/sec | 1704 ops/sec | 2199 ops/sec |
| swc (es2015) | 677 ops/sec | 1688 ops/sec | 1911 ops/sec |
| swc (es2016) | 1963 ops/sec | 3948 op s/sec | 5580 ops/sec |
| swc (es2017) | 1971 ops/sec | 3948 ops/sec | 6259 ops/sec |
| swc (es2018) | 2555 ops/sec | 4884 ops/sec | 8108 ops/sec |
| swc-optimize (es3) | 645 ops/sec | 1716 ops/sec | 1860 ops/sec |
| babel (es5) | 34.05 ops/sec | 27.28 ops/sec | 32 ops/sec |
swc scales well, as it does almost all work in the worker thread. From the fact that the throughput of 100 promises was better than 4 promises, we can conclude that the worker thread pool of Node.js utilizes hyperthreading.
swc scales up with the number of cpu cores. Promise.all is enough for scaling.
File: apps/website/docs/blog/swc-1.mdx
image: https://user-images.githubusercontent.com/9113740/140682117-5e9f460e-c8f1-447c-88a0-e75f80779260.png
import Authors, { Author } from '@components/Authors';
Introducing SWC 1.0
What is SWC?
swc (speedy web compiler) is a super-fast JavaScript compiler.
What can SWC do?
It can transpile TypeScript / jsx / ECMAScript 2019 to browser-compatible JavaScript.
// input.js
class Foo {
set foo(v) {}
}
class Bar extends Foo {
get bar1() {}
async bar2() {}
}How fast is SWC?
It's 16x - 20x faster than babel even on single-core synchronous benchmark. Note that actual performance gap is larger because swc works on worker thread while babel works on event loop thread.
Installation
You can install swc with
# if you use npm
npm i -D @swc/core
# if you use yarn
yarn add -D @swc/coreSee Getting Started for more details.
What's included in SWC 1.0?
Swc implements almost all babel plugins. As of 1.0.0, swc can understand various dialects of ecmascript and compiles them into es5.
ECMAScript 2019 support
.swcrc:
{
"jsc": {
"parser": {
"syntax": "ecmascript"
}
}
}React (with jsx)
.swcrc:
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": true
}
}
}TypeScript support
Swc can also compile typescript / tsx to ecmascript. Note that it does not type-check at the time of writing. Type checking is tracked at #126.
.swcrc:
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false
}
}
}See docs for more details.
Various Transforms
es3
- member-expression-literals
- property-literals
- reserved-words
es2015
- arrow-functions
- block-scoped-functions
- block-scoping
- classes
- computed-properties
- destructuring
- duplicate-keys
- for-of
- function-name
- instanceof
- literals
- new-target
- parameters
- shorthand-properties
- spread
- sticky regex (
yflag) - template-literals
- typeof-symbol
es2016
- exponentiation-operator
es2017
- async-to-generator
es2018
- object-rest-spread
react
- jsx
Migrating from Babel
@babel/core
Run npm i --save-dev @swc/core or yarn add --dev @swc/core.
Swc enables all passes by default. So if you are using only standard ecmascript, you can just delete .babelrc and change babel.transform() to swc.transform().
See usage docs and migration docs for more details.
Also note that swc does not support custom plugin yet.
@babel/cli
Run npm i --save-dev @swc/core @swc/cli or yarn add --dev @swc/core @swc/cli to install. CLI apis of @swc/cli is almost equivalent to it of @babel/cli. So if you are using standard ecmascript, you can just replace npx babel to npx swc. If it results in an error, please report an error.
See usage docs and migration docs for more details. Also note that swc does not support custom plugins yet.
File: apps/website/docs/docs/configuration/bundling.mdx
import Callout from '@components/Callout';
Bundling Configuration
Please use one of the bundlers instead.
SWC is able to bundle multiple JavaScript or TypeScript files into one.
This feature is currently named spack, but will be renamed to swcpack in v2. spack.config.js will be deprecated for swcpack.config.js.
View a basic example of bundling.
Configuration
You can configure bundling using spack.config.js with similar options to webpack. In the future, we are exploring a webpack compatible plugin system.
module.exports = {
entry: {
web: __dirname + "/src/index.ts",
},
output: {
path: __dirname + "/lib",
},
};Note: CommonJS is currently required. In the future, ES Modules will be supported.
If you want auto-completion or type checking for configuration, you can wrap the export with a config function from @swc/core/spack. It's an identity function with type annotation.
const { config } = require("@swc/core/spack");
module.exports = config({
entry: {
web: __dirname + "/src/index.ts",
},
output: {
path: __dirname + "/lib",
},
});mode
Possible values: production, debug, none.
Currently this value is not used, but it will behave similarly to webpack.
entry
Determines the entry of bundling. You may specify a file or a map of bundle name to file path.
Note: Currently this should be absolute path. You can use
__dirnameto create one.In the future, SWC will support using relative paths and will resolve files relative to
spack.config.js.
output
You can change destination directory of the bundler using output.
const { config } = require("@swc/core/spack");
module.exports = config({
output: {
path: __dirname + "/lib",
// Name is optional.
name: "index.js",
},
});options
Used to control the behavior of SWC. This field is optional.
File: apps/website/docs/docs/configuration/compilation.mdx
import Callout from '@components/Callout';
{
"$schema": "https://swc.rs/schema.json",
}Compilation
Compilation works out of the box with SWC and does not require customization. Optionally, you can override the configuration. Here are the defaults:
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false,
"dynamicImport": false,
"privateMethod": false,
"functionBind": false,
"exportDefaultFrom": false,
"exportNamespaceFrom": false,
"decorators": false,
"decoratorsBeforeExport": false,
"topLevelAwait": false,
"importMeta": false,
"preserveAllComments": false
},
"transform": null,
"target": "es5",
"loose": false,
"externalHelpers": false,
// Requires v1.2.50 or upper and requires target to be es2016 or upper.
"keepClassNames": false
},
"isModule": false
}env
SWC has an alternative for preset-env.
You can
- Set target browsers
- Use
browserslist - Control transforms
with env entry.
Note that this does not work with jsc.target.
`env.targets`
Possible values:
- Query:
string
Example:
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
},
"externalHelpers": true
},
"env": {
"targets": "Chrome >= 48"
}
}Queries:
string[]Version per browser:
Map<String, Version>
Example of targeting chrome 79.
{
"env": {
"targets": {
"chrome": "79",
}
},
}`env.mode`
Optional.
Possible values: "usage" | "entry" | false, defaults to false.
`env.debug`
Optional.
Type: Bool
Enable debug logging.
`env.dynamicImport`
Optional.
Type: Bool
`env.loose`
Optional.
Type: Bool
Enable loose mode. See jsc.loose also.
`env.skip`
Optional.
Type: string[]
`env.include`
Optional.
Type: string[]
Feature or module to include.
Example of enabling async/yield transform while targeting chrome 79.
{
"env": {
"targets": {
"chrome": "79",
},
"include": [
"transform-async-to-generator",
"transform-regenerator",
],
},
}`env.exclude`
Optional.
Type: string[]
Feature or module to exclude.
`env.coreJs`
Optional.
Type: string
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": true
}
},
"env": {
"mode": "usage",
"coreJs": "3.26.1"
}
}`env.path`
Optional.
Currently noop.
`env.shippedProposals`
Optional.
Type: Bool
`env.forceAllTransforms`
Optional.
Type: Bool
`env.bugfixes`
Optional.
Type: Bool
Enable bugfix passes.
jsc.externalHelpers
{
"jsc": {
"externalHelpers": true
}
}The output code may depend on helper functions to support the target environment. By default, a helper function is inlined into the output files where it is required.
By setting "externalHelpers": true SWC will inject import statements for these helpers from the @swc/helpers package instead of inlining them.
While bundling, this option will greatly reduce your file size.
You must add @swc/helpers as a dependency in addition to @swc/core.
By default, SWC will inject helpers methods with ESM style imports.
So if your file is a commonjs module for example, then SWC will still inject ESM import statements for the helper methods and you'll have a file with mixed ESM and CommonJS imports which can lead to runtime errors.
Setting "isModule": "unknown" will make SWC infer the module type of each file by reading it's import/export syntax and inject the helper imports with esm/commojs import statements accordingly.
jsc.parser
typescript
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"decorators": false,
"dynamicImport": false
}
}
}flow
{
"jsc": {
"parser": {
"syntax": "flow",
"jsx": false,
"all": false,
"requireDirective": false,
"enums": false,
"decorators": false,
"components": false,
"patternMatching": false
}
}
}See the Flow guide for @swc/core, CLI, and .swcrc
examples.
ecmascript
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false,
"dynamicImport": false,
"privateMethod": false,
"functionBind": false,
"classPrivateProperty": false,
"exportDefaultFrom": false,
"exportNamespaceFrom": false,
"decorators": false,
"decoratorsBeforeExport": false,
"importMeta": false
}
}
}jsc.target
Starting from @swc/core v1.0.27, you can specify the target environment by using the field.
{
"jsc": {
// Disable es3 / es5 / es2015 transforms
"target": "es2016"
}
}jsc.loose
Starting from @swc/core v1.1.4, you can enable "loose" transformations by enabling jsc.loose which works like babel-preset-env loose mode.
{
"jsc": {
"loose": true
}
}jsc.transform
{
"jsc": {
"transform": {
"react": {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
"throwIfNamespace": true,
"development": false,
"useBuiltins": false
},
"optimizer": {
"globals": {
"vars": {
"__DEBUG__": "true"
}
}
}
}
}
}jsc.transform.legacyDecorator
You can use the legacy (stage 1) class decorators syntax and behavior.
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"decorators": true
},
"transform": {
"legacyDecorator": true
}
}
}jsc.transform.decoratorMetadata
This feature requires v1.2.13+.
If you are using typescript and decorators with emitDecoratorMetadata enabled, you can use swc for faster iteration:
{
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
}
}
}jsc.transform.react
jsc.transform.react.runtime
Possible values: automatic, classic, preserve.
Decides which runtime to use when transforming JSX.
- Defaults to
'classic'. - Use
runtime: 'automatic'to use a JSX runtime module (e.g.react/jsx-runtimeintroduced in React 17). This is the modern approach introduced in React 17+ that eliminates the need to manually import React in every file that uses JSX.
{
"jsc": {
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}- Use
runtime: 'classic'to use the traditional JSX transform that relies onReact.createElementcalls - with this option, you must ensure thatReactis in scope when using JSX.
{
"jsc": {
"transform": {
"react": {
"runtime": "classic"
}
}
}
}- Use
runtime: 'preserve'to leave JSX syntax unchanged without transforming it.
{
"jsc": {
"transform": {
"react": {
"runtime": "preserve"
}
}
}
}jsc.transform.react.importSource
- Defaults to
react. - When using
runtime: automatic, determines the runtime library to import. - This option can be overrided with
@jsxImportSource foo.
jsc.transform.react.pragma
- Defaults to
React.createElement. - When using
runtime: classic, replaces the function used when compiling JSX expressions. - This option can be overrided with
@jsx foo.
jsc.transform.react.pragmaFrag
- Defaults to
React.Fragment - Replace the component used when compiling JSX fragments.
- This option can be overrided with
@jsxFrag foo.
jsc.transform.react.throwIfNamespace
Toggles whether or not to throw an error if an XML namespaced tag name is used. For example: <f:image />
Though the JSX spec allows this, it is disabled by default since React's JSX does not currently have support for it.
jsc.transform.react.development
Toggles debug props __self and __source on elements generated from JSX, which are used by development tooling such as React Developer Tools.
This option is set automatically based on the Webpack mode setting when used with swc-loader. See Using swc with webpack.
jsc.transform.react.useBuiltins
Use Object.assign() instead of _extends. Defaults to false.
jsc.transform.react.refresh
Enable react-refresh related transform. Defaults to false as it's considered experimental.
Pass refresh: true to enable this feature, or an object with the following:
interface ReactRefreshConfig {
refreshReg: String;
refreshSig: String;
emitFullSignatures: boolean;
}jsc.transform.constModules
{
"jsc": {
"transform": {
"constModules": {
"globals": {
"@ember/env-flags": {
"DEBUG": "true"
},
"@ember/features": {
"FEATURE_A": "false",
"FEATURE_B": "true"
}
}
}
}
}
}Then, source code like:
import { DEBUG } from "@ember/env-flags";
import { FEATURE_A, FEATURE_B } from "@ember/features";
console.log(DEBUG, FEATURE_A, FEATURE_B);is transformed to:
console.log(true, false, true);jsc.transform.optimizer
The SWC optimizier assumes:
- It's a module or wrapped in an iife.
- Accessing (get) global variables does not have a side-effect. It is the same assumption as the google closure compiler.
- You don't add fields to literals like a numeric literal, regular expression or a string literal.
- Files are served as gzipped.
SWC will not focus on reducing the size of non-gzipped file size.
Setting this to undefined skips optimizer pass.
jsc.transform.optimizer.simplify
Requires
v1.2.101+
You can set this to false to use inline_globals while skipping optimizations.
{
"jsc": {
"transform": {
"optimizer": {
"simplify": false,
"globals": {
"vars": {
"__DEBUG__": "true"
}
}
}
}
}
}jsc.transform.optimizer.globals
Requires
v1.2.101+
vars- Variables to inline.typeofs- If you set{ "window": "object" },typeof windowwill be replaced with"object".
{
"jsc": {
"transform": {
"optimizer": {
"globals": {
"vars": {
"__DEBUG__": "true"
}
}
}
}
}
}Then, you can use it like npx swc '__DEBUG__' --filename input.js.
jsc.transform.optimizer.jsonify
Requires
v1.1.1+
minCost- If cost of parsing a pure object literal is larger than this value, the object literal is converted toJSON.parse('{"foo": "bar"}'). Defaults to 1024.
{
"jsc": {
"transform": {
"optimizer": {
"jsonify": {
"minCost": 0
}
}
}
}
}This will change all pure object literals to JSON.parse("").
jsc.keepClassNames
Requires
v1.2.50+and target to be es2016 or higher
Enabling this option will make swc preserve original class names.
jsc.paths
Requires
v1.2.62+
Syntax is identical as it of tsconfig.json: learn more.
Requires jsc.baseUrl. See below.
jsc.baseUrl
The path must be specified as an absolute path.
jsc.minify
Requires
v1.2.67+
See the documentation for minification for more details.
jsc.experimental
jsc.experimental.keepImportAttributes
Preserve import attributes.
This is experimental because import attributes are not covered by ecmascript specifications yet.
jsc.experimental.plugins
It follows resolving rule of node.js,.
Specify the plugin name like
{
"jsc": {
"experimental": {
"plugins": [
["@swc/plugin-styled-jsx", {}]
]
}
}
}styled-jsx works because it's published as @swc/plugin-styled-jsx.
jsc.experimental.cacheRoot
Specify the location where SWC stores its intermediate cache files.
Currently only transform plugin uses this. If not specified, SWC will create .swc directories.
{
"jsc": {
"experimental": {
"cacheRoot": "node_modules/.cache/swc"
}
}
}jsc.preserveAllComments
Indicate that all comments should be preserved during compilation. Comments from source may be shifted in order to preserve thier relative location from source
to compiled output.
This feature is useful for transpilation that requires comments remain relatively close to the source: e.g. files under test with istanbul-ignore coverage
annotations.
jsc.transform.useDefineForClassFields
Possilbe values:
true:
class Foo {
init = 3;
}
console.log(Foo.init)will be compiled as
class Foo {
constructor(){
// Helper
_defineProperty(this, "init", 3);
}
}
console.log(Foo.init);false:
class Foo {
init = 3;
}
console.log(Foo.init)will be compiled as
class Foo {
constructor(){
this.init = 3;
}
}
console.log(Foo.init);jsc.transform.decoratorVersion
Starting from v1.3.47, you can use stage 3 decorator.
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"decorators": true
},
"transform": {
"decoratorVersion": "2022-03"
}
}
}Possible values:
"2021-12"(default)
Legacy decorator.
"2022-03"
Initial Stage 3 decorator.
"2023-11"
TC39 decorator with proposal updates as of November 2023.
jsc.output
jsc.output.charset
Possible values: utf8, ascii.
This can be used to keep the output ascii-only.
Multiple Entries
Requires
v1.0.47+
[
{
"test": ".*\\.js$",
"module": {
"type": "commonjs"
}
},
{
"test": ".*\\.ts$",
"module": {
"type": "amd"
}
}
]This makes SWC compile JavaScript files as CommonJS modules and compile TypeScript files as AMD modules.
Note that test option can be used to transcompile only typescript files, like
{
"test": ".*\\.ts$",
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"decorators": true,
"dynamicImport": true
}
}
}test
Type: Regex / Regex[]
{
"test": ".*\\.ts$",
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"decorators": true,
"dynamicImport": true
}
}
}exclude
Type: Regex / Regex[]
{
"exclude": [".*\\.js$", ".*\\.map$"],
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"decorators": true,
"dynamicImport": true
}
}
}sourceMaps
Requires
v1.2.50+
Enable source map by adding sourceMaps: true or sourceMaps: 'inline' to the .swcrc.
{
"sourceMaps": true
}inlineSourcesContent
Requires
v1.2.101+
Defaults to true. If you want to make swc store contents of files into sourcemap, you can set inlineSourcesContent to true.
{
"sourceMaps": true,
"inlineSourcesContent": true
}isModule
Possible values: true, false, "unknown"
Used to treat input as a module or script.
If this is set to unknown, it will be Module if it's esm and Script otherwise.
File: apps/website/docs/docs/configuration/minification.mdx
import Callout from "@components/Callout";
Minification
- swcMinify with the Terser Webpack Plugin
- SWC Minify Webpack Plugin which is a standalone tool if you only want minification.
Assumptions
SWC Minifier makes small amount of assumptions to make the minification efficient. This is similar to other minifiers.
.toString()and.valueOf()don't have side effects, and for built-in objects they have not been overridden.undefined,NaNandInfinityhave not been externally redefined.arguments.callee,arguments.callerandFunction.prototype.callerare not used.
These are not valid in strict mode.
The code doesn't expect the contents of
Function.prototype.toString()orError.prototype.stackto be anything in particular.Getting and setting properties on a plain object does not cause other side effects (using
.watch()orProxy).Object properties can be added, removed and modified (not prevented with
Object.defineProperty(),Object.defineProperties(),Object.freeze(),Object.preventExtensions()orObject.seal()).document.allis notnull.Assigning properties to a class doesn't have side effects and does not throw.
Acesssing declared top-level identifiers do not have side effects.
If you declare a variable on globalThis using a getter with side-effects, SWC Minifier will likely break it.
- TDZ violation does not exist.
TDZ violation will be ignored and not preserved.
- Arithmetic expression may not have side effects.
This assumption means that you can't rely on the exception thrown by the JS engine when adding a bigint and a number.
const a = 1n;
const b = 1;
const c = a + b;If you execute this code, the JS engine will throw an exception. But SWC Minifier will ignore it.
Related: terser - compiler assumptions
Starting with v1.2.67, you can configure SWC to minify your code by enabling minify in your .swcrc file:
{
// Enable minification
"minify": true,
// Optional, configure minification options
"jsc": {
"minify": {
"compress": {
"unused": true
},
"mangle": true
}
}
}Configuration
Note about comments
If you set jsc.minify.compress to true or {}, SWC will remove all comments starting from v1.11.11.
Previous versions will preserve only license comments.
If you don't want this, modify jsc.minify.format.
`jsc.minify.compress`
Type: boolean | object.
Similar to the compress option of terser.
{
"jsc": {
"minify": {
"compress": true // equivalent to {}
}
}
}arguments, defaults tofalse.arrows, defaults totrue.booleans, defaults totrue.booleans_as_integers, defaults tofalse.collapse_vars, defaults totrue.comparisons, defaults totrue.computed_props, defaults totrue.conditionals, defaults totrue.dead_code, defaults totrue.defaults, defaults totrue.directives, defaults totrue.drop_console, defaults tofalse.drop_debugger, defaults totrue.ecma, defaults to5.evaluate, defaults totrue.global_defs, defaults to{}.hoist_funs, defaults tofalse.hoist_props, defaults totrue.hoist_vars, defaults tofalse.ie8, Ignored.if_return, defaults totrue.inline, defaults totrue.join_vars, defaults totrue.keep_classnames, defaults tofalse.keep_fargs, defaults tofalse.keep_infinity, defaults tofalse.loops, defaults totrue.negate_iife, defaults totrue.passes, defaults to0, which means no limit.properties, defaults totrue.pure_getters, defaults to ``.pure_funcs, defaults to[]. Type is an array of string.reduce_funcs, defaults tofalse.reduce_vars, defaults totrue.sequences, defaults totrue.side_effects, defaults totrue.switches, defaults totrue.top_retain, defaults to ``.toplevel, defaults totrue.typeofs, defaults totrue.unsafe, defaults tofalse.unsafe_arrows, defaults tofalse.unsafe_comps, defaults tofalse.unsafe_Function, defaults tofalse.unsafe_math, defaults tofalse.unsafe_symbols, defaults tofalse.unsafe_methods, defaults tofalse.unsafe_proto, defaults tofalse.unsafe_regexp, defaults tofalse.unsafe_undefined, defaults tofalse.unused, defaults totrue.module, Ignored. Currently, all files are treated as module.
`jsc.minify.mangle`
Type: boolean | object.
Similar to the mangle option of terser.
{
"jsc": {
"minify": {
"mangle": true // equivalent to {}
}
}
}props, Defaults tofalse, andtrueis identical to{}.topLevel, Defaults totrue. Aliased astoplevelfor compatibility withterser.keepClassNames, Defaults tofalse. Aliased askeep_classnamesfor compatibility withterser.keepFnNames, Defaults tofalse.keepPrivateProps, Defaults tofalse. Aliased askeep_private_propsfor compatibility withterser.reserved, Defaults to[]ie8, Ignored.safari10, Defaults tofalse.
`jsc.minify.mangle.properties`
Type: object.
Similar to the mangle properties option of terser.
{
"jsc": {
"minify": {
"mangle": {
"properties": {
"reserved": ["foo", "bar"],
"undeclared": false,
"regex": "rust regex"
}
}
}
}
}reserved: Don't use these names as properties.undeclared: Mangle properties even if it's not declared.regex: Mangle properties only if it matches this regex
`jsc.minify.format`
These properties are mostly not implemented yet, but it exists to support passing terser config to swc minify without modification.
asciiOnly, Defaults tofalse. Implemented asv1.2.184and aliased asascii_onlyfor compatibility withterser.beautify, Defaults tofalse. Currently noop.braces, Defaults tofalse. Currently noop.comments, Defaults tosome.falseremoves all comments'some'preserves some comments'all'preserves all comments
ecma, Defaults to 5. Currently noop.indentLevel, Currently noop and aliases asindent_levelfor compatibility withterser.indentStart, Currently noop and aliases asindent_startfor compatibility withterser.inlineScript, Aliases asinline_scriptfor compatibility withterserand requires@swc/[email protected]or later.keepNumbers, Currently noop and aliases askeep_numbersfor compatibility withterser.keepQuotedProps, Currently noop and aliases askeep_quoted_propsfor compatibility withterser.maxLineLen, Currently noop, and aliases asmax_line_lenfor compatibility withterser.preamble, Supported sincev1.3.66.quoteKeys, Currently noop and aliases asquote_keysfor compatibility withterser.quoteStyle, Currently noop and aliases asquote_stylefor compatibility withterser.preserveAnnotations, Currently noop and aliases aspreserve_annotationsfor compatibility withterser.safari10, Currently noop.semicolons, Currently noop.shebang, Currently noop.webkit, Currently noop.wrapIife, Currently noop and aliases aswrap_iifefor compatibility withterser.wrapFuncArgs, Currently noop and aliases aswrap_func_argsfor compatibility withterser.
@swc/core Usage
swc.minify(code, options)
This API is asynchronous and all of parsing, minification, and code generation will be done in background thread. The options argument is same as jsc.minify object. For example:
import swc from "@swc/core";
const { code, map } = await swc.minify(
"import foo from '@src/app'; console.log(foo)",
{
compress: false,
mangle: true,
}
);
expect(code).toMatchInlineSnapshot(`"import a from'@src/app';console.log(a);"`);Returns Promise<{ code: string, map: string }>.
swc.minifySync(code, options)
This API exists on @swc/core, @swc/wasm, @swc/wasm-web.
import swc from "@swc/core";
const { code, map } = swc.minifySync(
"import foo from '@src/app'; console.log(foo)",
{
compress: false,
mangle: true,
module: true,
}
);
expect(code).toMatchInlineSnapshot(`"import a from'@src/app';console.log(a);"`);Returns { code: string, map: string }.
APIs for WebAssembly
Replacing Terser
You can reduce build time and override Terser without needing a library to update their dependencies through yarn resolutions. Example package.json would include:
{
"resolutions": { "terser": "npm:@swc/core" }
}This will use the SWC minifier instead of Terser for all nested dependencies. Ensure you remove your lockfile and re-install your dependencies.
$ rm -rf node_modules yarn.lock
$ yarnFile: apps/website/docs/docs/configuration/modules.mdx
import Callout from '@components/Callout';
{
"$schema": "https://swc.rs/schema.json",
}Modules
SWC can transpile your code using ES Modules to CommonJS or UMD/AMD. By default, module statements will remain untouched.
CommonJS
To emit a CommonJS module, change the type in .swcrc:
{
"$schema": "https://swc.rs/schema.json",
"module": {
"type": "commonjs",
// These are defaults.
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
}
}ES6
To emit a ES6 module, change the type in .swcrc:
{
"module": {
"type": "es6",
// These are defaults.
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
}
}AMD
To emit an AMD module, change the type in .swcrc:
{
"module": {
"type": "amd",
// Optional. If specified, swc emits named AMD module.
"moduleId": "foo",
// These are defaults.
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
}
}UMD
To emit an UMD module, change the type in .swcrc:
{
"module": {
"type": "umd",
"globals": {},
// These are defaults.
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
}
}Shared Options
These options are shared by commonjs / es6 / umd / amd inside .swcrc:
{
"module": {
// You can specify "commonjs", "es6", "amd", "umd"
"type": "commonjs",
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false,
"ignoreDynamic": false,
"preserveImportMeta": false,
"outFileExtension": "js",
}
}strict
Defaults to false. By default, when using exports with SWC, a non-enumerable __esModule property is exported. In some cases, this property is used to determine if the import is the default export or if it contains the default export.
To prevent the __esModule property from being exported, you can set the strict option to true.
strictMode
Defaults to true. If true, swc emits 'use strict' directive.
lazy
Defaults to false. This option changes Babel's compiled import statements to be lazily evaluated when their imported bindings are used for the first time. This can improve the initial load time of your module because evaluating dependencies upfront is sometimes entirely unnecessary. This is especially the case when implementing a library module.
The value of lazy has a few possible effects:
false- No lazy initialization of any imported module.true- Do not lazy-initialize local./fooimports, but lazy-initfoodependencies. Local paths are much more likely to have circular dependencies, which may break if loaded lazily, so they are not lazy by default, whereas dependencies between independent modules are rarely cyclical.Array<string>- Lazy-initialize all imports with source matching one of the given strings.
The two cases where imports can never be lazy are:
import "foo";
Side-effect imports are automatically non-lazy since their very existence means that there is no binding to later kick-off initialization.export from "foo"
Re-exporting all names requires up-front execution because otherwise there is no
way to know what names need to be exported.
noInterop
Defaults to false. By default, when using exports with swc a non-enumerable __esModule property is exported.
This property is then used to determine if the import is the default export or if it contains the default export.
In cases where the auto-unwrapping of default is not needed, you can set the noInterop option to true to avoid the usage of the interopRequireDefault helper (shown in inline form above).
ignoreDynamic
If set to true, dynamic imports will be preserved.
preserveImportMeta
If set to true, import.meta will be preserved.
importInterop
Possible values:
swc(alias:babel)nodenone
Defaults to none if noInterop is true, and swc otherwise.
resolveFully
When set to true, fully resolves module import file paths, including any that end with index.js.
outFileExtension
when supplied with resolveFully, this will change the extension of any locally resolved file paths to
be the one specified. You should use this extension to match the --out-file-extension option for @swc/cli.
File: apps/website/docs/docs/configuration/react-compiler.mdx
React Compiler
Requires
@swc/corev1.15.43 or later.
SWC includes a native implementation of the React Compiler transform. Enable it
with jsc.transform.reactCompiler. The transform runs before SWC transforms
JSX, so it works with the existing jsc.transform.react configuration.
Getting started
Set reactCompiler to true to use the default configuration:
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"reactCompiler": true,
"react": {
"runtime": "automatic"
}
}
}
}The same configuration can be passed to the transform and transformSync
APIs:
import { transformSync } from "@swc/core";
const output = transformSync(source, {
jsc: {
parser: {
syntax: "typescript",
tsx: true,
},
transform: {
reactCompiler: true,
react: {
runtime: "automatic",
},
},
},
});Set reactCompiler to false or omit it to disable the transform.
Runtime target
The compiled output imports a memoization runtime. Select the runtime that
matches the React version used by the application:
target |
Runtime import | Setup |
|---|---|---|
"19" (default) |
react/compiler-runtime |
Provided by React 19. |
"17" or "18" |
react-compiler-runtime |
Install react-compiler-runtime as an application dependency. |
For example, a React 18 application can use:
pnpm add react-compiler-runtime{
"jsc": {
"transform": {
"reactCompiler": {
"target": "18"
}
}
}
}Options
Pass an object instead of true to override individual compiler options.
Unspecified fields retain their defaults.
{
"jsc": {
"transform": {
"reactCompiler": {
"compilationMode": "infer",
"panicThreshold": "none",
"target": "19",
"outputMode": "client",
"isDev": false
}
}
}
}compilationMode
Type: "infer" | "syntax" | "annotation" | "all"
Default: "infer"
Controls which functions the compiler attempts to compile:
"infer"identifies React components and hooks using naming and code
conventions."syntax"compiles declared component and hook syntax, as well as functions
with an opt-in directive."annotation"compiles only functions with an opt-in directive such as"use memo"."all"attempts to compile every function.
panicThreshold
Type: "none" | "critical_errors" | "all_errors"
Default: "none"
Controls which compiler errors stop the transform. With the default value,
functions that cannot be compiled are left unchanged instead of failing the
transform.
target
Type: "17" | "18" | "19"
Default: "19"
Selects the React runtime version. See Runtime target for the
corresponding runtime dependency.
noEmit
Type: boolean
Default: false
Analyzes the source and reports diagnostics without emitting React Compiler
transformations. This also selects lint output behavior.
outputMode
Type: "client" | "ssr" | "lint"
Default: "client"
Selects client, server-side rendering, or lint output behavior. noEmit: true
also uses lint output behavior.
ignoreUseNoForget
Type: boolean
Default: false
Compiles functions even when they contain a standard opt-out directive such as"use no memo" or "use no forget".
flowSuppressions
Type: boolean
Default: true
Treats matching Flow suppression comments as opt-outs from compilation.
enableReanimated
Type: boolean
Default: false
Enables compatibility with react-native-reanimated worklets.
isDev
Type: boolean
Default: false
Enables development-mode compiler output.
eslintSuppressionRules
Type: string[]
Specifies ESLint rule names whose suppression comments opt a function out of
compilation.
{
"jsc": {
"transform": {
"reactCompiler": {
"eslintSuppressionRules": ["react-hooks/exhaustive-deps"]
}
}
}
}customOptOutDirectives
Type: string[]
Replaces the standard opt-out directives with a custom list of function
directives that opt out of compilation.
{
"jsc": {
"transform": {
"reactCompiler": {
"customOptOutDirectives": ["use no optimize"]
}
}
}
}gating
Type: { source: string; importSpecifierName: string }
Emits both the original and compiled functions and selects between them by
calling an imported gating function.
{
"jsc": {
"transform": {
"reactCompiler": {
"gating": {
"source": "./feature-flags",
"importSpecifierName": "isReactCompilerEnabled"
}
}
}
}
}This configuration imports isReactCompilerEnabled from ./feature-flags and
uses its return value to choose the compiled or original function.
dynamicGating
Type: { source: string }
Enables per-function gating with a "use memo if(identifier)" directive. The
identifier is imported from source and called as the gate for that function.
{
"jsc": {
"transform": {
"reactCompiler": {
"dynamicGating": {
"source": "./feature-flags"
}
}
}
}
}export function FilteredList() {
"use memo if(isFilteredListCompilerEnabled)";
// ...
}environment.enableFunctionOutlining
Type: boolean
Default: true
Requires
@swc/corev1.15.47 or later.
Controls whether anonymous functions that do not capture local variables are
extracted into top-level helper functions. The environment object currently
exposes only this option.
{
"jsc": {
"transform": {
"reactCompiler": {
"environment": {
"enableFunctionOutlining": false
}
}
}
}
}File: apps/website/docs/docs/configuration/supported-browsers.mdx
import Callout from '@components/Callout';
Supported Browsers
Starting with v1.1.10, you can now use browserslist to automatically configure supported browsers.
Usage
{
"env": {
"targets": {
"chrome": "79"
},
"mode": "entry",
"coreJs": "3.22"
}
}Options
targets
string | Array<string> | { [string]: string }, defaults to {}.
Describes the environments you support/target for your project. This can either be a browserslist-compatible query (with limitations):
{
"env": {
"targets": "> 0.25%, not dead"
}
}Or an object of minimum environment versions to support:
{
"env": {
"targets": {
"chrome": "58",
"ie": "11"
}
}
}Example environments:
chromeoperaedgefirefoxsafariieiosandroidnodeelectron
If targets is not specified, SWC uses browserslist to get target information.
path
string, defaults to current directory.pathspecifies the directory to load thebrowserslistmodule and any browserslist configuration files. For example,.browserslistrcorbrowserslistfield in package.json. This can be useful if your build system isn't in the root of your project.
mode
string, defaults toundefined.- Possible values:
usage,entry,undefined(this matchesuseBuiltInsfrom Babel)
skip
Define ES features to skip to reduce bundle size. For example, your .swcrc could be:
{
"env": {
"skip": ["core-js/modules/foo"]
}
}coreJs
string, defaults toundefined.coreJsspecifies the version ofcore-jsto use, can be any core-js versions supported by swc. E.g.,"3.22".
The option has an effect when used alongside mode: "usage" or mode: "entry". It is recommended to specify the minor version (E.g. "3.22") otherwise "3" will be interpreted as "3.0" which may not include polyfills for the latest features.
When coreJs is undefined, SWC falls back to [email protected].
Additional Options
debug: (boolean) defaults tofalse.dynamicImport: (boolean) defaults tofalse.loose: (boolean) defaults tofalse. Enable loose transformations for any plugins that allow them.include: (string[]) can be acore-jsmodule (es.math.sign) or an SWC pass (transform-spread).exclude: (string[]) can be acore-jsmodule (es.math.sign) or an SWC pass (transform-spread).shippedProposals: (boolean) defaults tofalse.forceAllTransforms: (boolean) defaults tofalse. Enable all possible transforms.
File: apps/website/docs/docs/configuration/swcrc.mdx
import Callout from '@components/Callout';
{
"$schema": "https://swc.rs/schema.json",
}Configuring SWC
SWC can be configured with an .swcrc file.
Compilation
Compilation works out of the box with SWC and does not require customization. Optionally, you can override the configuration. Here are the defaults:
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false,
"dynamicImport": false,
"privateMethod": false,
"functionBind": false,
"exportDefaultFrom": false,
"exportNamespaceFrom": false,
"decorators": false,
"decoratorsBeforeExport": false,
"topLevelAwait": false,
"importMeta": false
},
"transform": null,
"target": "es5",
"loose": false,
"externalHelpers": false,
// Requires v1.2.50 or upper and requires target to be es2016 or upper.
"keepClassNames": false
},
"minify": false
}Read more about configuring compilation.
Supported Browsers
Starting with v1.1.10, you can now use browserslist to automatically configure supported browsers.
Usage
First, install browserslist. Then, update your .swcrc:
{
"env": {
"targets": {
"chrome": "79"
},
"mode": "entry",
"coreJs": "3.22"
}
}Read more about configuring supported browsers.
Modules
Read more about configuring modules.
Minification
Starting with v1.2.67, you can configure SWC to minify your code by enabling minify in your .swcrc file:
{
"minify": true
}Read more about configuring the JavaScript minifier.