---
sidebar_position: 2
---

# SWC 插件

:::warning
**当前文档中的 SWC 功能已停止迭代**，我们更推荐使用 Rspack + SWC 的方案。

请参考[「使用 Rspack」](/guides/advanced-features/rspack-start.html)了解更多。

:::

import SWC from '@site-docs/components/swc.mdx';

<SWC />

## 适用场景

在使用 SWC 插件之前，请先了解一下 SWC 插件的适用场景和局限性，以明确你的项目是否需要使用 SWC 插件。

### Rspack 场景

如果你的项目中已经使用了 Rspack 作为打包工具，那么你不需要接入 SWC 插件，因为 Rspack 默认会使用 SWC 进行转译和压缩，各个 SWC 编译能力可以开箱即用。

如果你使用 Rspack 时配置了当前的 SWC 插件，它将不会产生任何效果。

### Babel 插件

如果你的项目需要注册一些自定义的 Babel 插件，由于 SWC 替代了 Babel 作为转译工具，因此使用 SWC 后，你将无法注册和使用 Babel 插件。

对于大部分常见的 Babel 插件，你可以在 SWC 中找到对应的替代品，比如：

- `@babel/preset-env`: 使用 [presetEnv](#presetenv) 代替。
- `@babel/preset-react`: 使用 [presetReact](#presetreact) 代替。
- `babel-plugin-import`：使用 [source.transformImport](/configure/app/source/transform-import) 代替。
- `babel-plugin-lodash`：使用 [extensions.lodash](#extensionslodash) 代替。
- `@emotion/babel-plugin`：使用 [extensions.emotion](#extensionsemotion) 代替。
- `babel-plugin-styled-components`：使用 [extensions.styledComponents](#extensionsstyledcomponents) 代替。
- `@babel/plugin-react-transform-remove-prop-types`: 使用 [reactUtils.removePropTypes](#extensionsreactutils) 代替。

如果你使用了 SWC 尚未支持的 Babel 插件能力，在切换到 SWC 编译后，将无法再使用它们。你可以到 [swc-plugins](https://github.com/web-infra-dev/swc-plugins) 仓库下通过 issues 进行反馈，我们会评估是否需要内置支持。

### 产物体积

在使用 SWC 来代替 [terser](https://github.com/terser/terser) 和 [cssnano](https://github.com/cssnano/cssnano) 进行代码压缩时，构建产物的体积可能会出现少量变化。在 JavaScript 代码压缩方面，SWC 的压缩率是优于 terser 的；在 CSS 代码压缩方面，SWC 的压缩率稍逊于 cssnano。

对于压缩工具之间的详细对比，可以参考 [minification-benchmarks](https://github.com/privatenumber/minification-benchmarks)。

## 快速开始

### 在 Modern.js 框架中使用

Modern.js 框架对 SWC 插件进行了封装，你可以通过以下方式来使用：

import EnableSWC from '@site-docs/components/enableSwc.mdx';

<EnableSWC />

That's it! 现在你可以在项目中无缝使用 SWC 的转译和压缩能力了。

## 配置

- **类型：**

```ts
type PluginConfig =
  | ObjPluginConfig
  | ((
      config: ObjPluginConfig,
      utils: { mergeConfig: typeof lodash.merge; setConfig: typeof lodash.set },
    ) => ObjPluginConfig | void);

// SwcOptions 为 swc 配置项 https://swc.rs/docs/configuration/compilation
interface ObjPluginConfig extends SwcOptions {
  presetReact?: ReactConfig;
  presetEnv?: EnvConfig;
  jsMinify?: boolean | JsMinifyOptions;
  cssMinify?: boolean | CssMinifyOptions;
  extensions?: Extensions;
  overrides?: Overrides;
}
```

插件配置在 SWC 配置的基础上，为了简化部分深层配置和为提高开发体验，进行了部分拓展，例如当使用对象形式配置时，可以使用 `presetReact` 以及 `presetEnv` 快速配置 react 以及语法降级相关功能，另外不属于插件特有的配置也会直接透传给 swc。

当使用函数形式配置时，则会传入插件内部产出的默认配置，可以对其进行修改或返回新的配置。

### presetReact

- **类型：** SWC 中的 [react](https://swc.rs/docs/configuration/compilation#jsctransformreact) 配置。

对标 `@babel/preset-react`。传入的值会与默认配置进行合并。

插件默认会自动根据你的 `react` 版本确定 `runtime` 字段，如果 `react` 版本大于 17.0.0，会设置成 `automatic`，否则设置成 `classic`。

### presetEnv

- **类型：** SWC 中的 [presetEnv](https://swc.rs/docs/configuration/supported-browsers#options)。

对标 `@babel/preset-env`。传入的值会与默认配置进行合并。
默认配置为:

```ts
{
  targets: '', // 自动从项目中获取 browserslist
  mode: 'usage',
}
```

### jsMinify

- **类型：** `boolean` 或者 [terser 中的 compress 配置](https://terser.org/docs/api-reference.html#compress-options)。
- **默认值：** `{ compress: {}, mangle: true }`。

如果配置 `false` 将不会使用 SWC 的压缩能力，配置 `true` 会启用默认压缩配置，如果配置是对象，则会与默认配置进行合并。

### cssMinify

- **类型：** `boolean`
- **默认值：** `true`

是否启用 SWC 对 CSS 文件进行压缩，若启用会使得 CSS 压缩性能提高，但压缩率会略微降低。

### overrides

- **类型：**

```ts
interface Overrides extends SwcOptions {
  test: RegExp;
  include: RegExp[];
  exclude: RegExp[];
}
```

- **默认值：** `undefined`

对指定文件运用另外的配置。例如需要对 `foo.ts` 的语法降级成 ie 11，则可以如下配置：

```ts
{
  test: /foo.ts/,
  env: { targets: 'ie 11' }
}
```

该配置会与默认配置进行合并，并且不会影响到其他文件。

### extensions

- **类型：** `Object`

`extensions` 包含了从 Babel 移植过来的一些插件能力。

#### extensions.reactUtils

- **类型：**

```ts
type ReactUtilsOptions = {
  autoImportReact?: boolean;
  removeEffect?: boolean;
  removePropTypes?: {
    mode?: 'remove' | 'unwrap' | 'unsafe-wrap';
    removeImport?: boolean;
    ignoreFilenames?: string[];
    additionalLibraries?: string[];
    classNameMatchers?: string[];
  };
};
```

一些用于 `React` 的工具，包括以下配置项:

**reactUtils.autoImportReact**

- **类型：** `boolean`

自动引入 `React`, `import React from 'react'`，用于 `jsx` 转换使用 `React.createElement`。

**reactUtils.removeEffect**

- **类型：** `boolean`

移除 `useEffect` 调用。

**reactUtils.removePropTypes**

- **类型：**

```ts
type RemovePropTypesOptions = {
  mode?: 'remove' | 'unwrap' | 'unsafe-wrap';
  removeImport?: boolean;
  ignoreFilenames?: string[];
  additionalLibraries?: string[];
  classNameMatchers?: string[];
};
```

移除 `React` 组件在运行时的类型判断。移植自 [@babel/plugin-react-transform-remove-prop-types](https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types)。

相应配置和 `@babel/plugin-react-transform-remove-prop-types` 插件保持一致。

#### extensions.lodash

- **类型：**

```ts
type LodashOptions = {
  cwd?: string;
  ids?: string[];
};
```

- **默认值：**

```ts
const defaultOptions = {
  cwd: process.cwd(),
  ids: ['lodash', 'lodash-es'],
};
```

移植自 [babel-plugin-lodash](https://github.com/lodash/babel-plugin-lodash)，用于自动将 Lodash 的引用转换为按需引入，从而减少打包后的 Lodash 代码大小。

```ts
// Input
import { get, throttle } from 'lodash';

// Output
import get from 'lodash/get';
import throttle from 'lodash/throttle';
```

#### extensions.styledComponents

- **类型：**

```ts
boolean | {
  displayName?: boolean; // 默认开发模式开启, 生产模式关闭,
  ssr?: boolean; // 默认开启
  fileName?: boolean; // 默认开启
  topLevelImportPaths?: string[]; // 默认空
  meaninglessFileNames?: string[]; // 默认为 ["index"].
  cssProp?: boolean; // 默认开启
  namespace?: string; // 默认空
};
```

由 Next.js 团队移植自 [babel-plugin-styled-components](https://github.com/styled-components/babel-plugin-styled-components)。

#### extensions.emotion

- **类型：**

```ts
boolean | {
  sourceMap?: boolean, // 默认开启
  autoLabel?: 'never' | 'dev-only' | 'always', // 默认 'dev-only'
  // 默认 '[local]'.
  // 允许的值为: `[local]` `[filename]` 以及 `[dirname]`
  // 只有当 autoLabel 设置成 'dev-only' 或者 'always' 才会生效.
  // 该配置允许你定义结果标签的格式，该格式的组成是字符串以及可以由方括号包裹字符串作为变量
  // 例如对于 "my-classname--[local]"，其中的 [local] 会被替换成相应的变量
  labelFormat?: string,
  // 默认值 undefined.
  // 该配置允许让编译器知道哪一个引入需要进行转换，所以如果你重导出了 emotion
  // 的导出，你仍然可以使用该插件进行转换
  importMap?: {
    [packageName: string]: {
      [exportName: string]: {
        canonicalImport?: [string, string],
        styledBaseImport?: [string, string],
      }
    }
  },
},
```

由 Next.js 团队移植自 [@emotion/babel-plugin](https://www.npmjs.com/package/@emotion/babel-plugin)。

#### extensions.pluginImport

:::tip
Modern.js 提供了 [source.transformImport](/configure/app/source/transform-import) 配置项，因此你不需要手动配置 `extensions.pluginImport`。
:::

移植自 [babel-plugin-import](https://github.com/umijs/babel-plugin-import)，配置选项保持一致。

一些配置可以传入函数，例如 `customName`，`customStyleName` 等，这些 JavaScript 函数会由 Rust 通过 Node-API 调用，这种调用会造成一些性能劣化。

简单的函数逻辑其实可以通过模版语言来代替，因此`customName`，`customStyleName` 等这些配置除了可以传入函数，也可以传入字符串作为模版来代替函数，提高性能。

我们以下面代码为例说明:

```ts
import { MyButton as Btn } from 'foo';
```

添加以下配置：

```ts
PluginSWC({
  extensions: {
    pluginImport: [
      {
        libraryName: 'foo',
        customName: 'foo/es/{{ member }}',
      },
    ],
  },
});
```

其中的 `{{ member }}` 会被替换为相应的引入成员，转换后:

```ts
import Btn from 'foo/es/MyButton';
```

可以看出配置 `customName: "foo/es/{{ member }}"` 的效果等同于配置 `` customName: (member) => `foo/es/${member}`  ``，但是不会有 Node-API 的调用开销。

这里使用到的模版是 [handlebars](https://handlebarsjs.com)，模版配置中还内置了一些有用的辅助工具，还是以上面的导入语句为例，配置成：

```ts
PluginSWC({
  extensions: {
    pluginImport: [
      {
        libraryName: 'foo',
        customName: 'foo/es/{{ kebabCase member }}',
      },
    ],
  },
});
```

会转换成下面的结果:

```ts
import Btn from 'foo/es/my-button';
```

除了 `kebabCase` 以外还有 `camelCase`，`snakeCase`，`upperCase`，`lowerCase` 可以使用。

## 限制

不支持 `@babel/plugin-transform-runtime` 以及其他自定义的 Babel 插件。
