├── .github
└── workflows
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── .node-version
├── .npmignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── eslint.config.js
├── package.json
├── pnpm-lock.yaml
├── src
├── directive.ts
├── index.ts
└── options.ts
├── test
├── __snapshots__
│ └── index.ts.snap
├── fixtures
│ ├── custom-tsconfig
│ │ ├── index.jsx
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── decorators
│ │ └── index.ts
│ ├── directive-include-use-client
│ │ └── index.tsx
│ ├── directive-merge-use-client
│ │ ├── bar.ts
│ │ ├── foo.ts
│ │ └── index.tsx
│ ├── directive-split-entry
│ │ ├── client.ts
│ │ └── server.ts
│ ├── disable-reading-tsconfig
│ │ ├── .swcrc
│ │ ├── index.jsx
│ │ └── tsconfig.json
│ ├── extensions
│ │ ├── index.mts
│ │ └── module.cts
│ ├── issue-58
│ │ ├── index.ts
│ │ └── package.json
│ ├── jsconfig-custom-jsx-factory
│ │ ├── index.tsx
│ │ └── jsconfig.json
│ ├── legacy-decorators
│ │ ├── index.ts
│ │ └── tsconfig.json
│ ├── load-json
│ │ ├── foo.json
│ │ └── index.js
│ ├── load-jsx-tsx
│ │ ├── foo.tsx
│ │ ├── index.js
│ │ └── some.util.ts
│ ├── minify
│ │ ├── foo.tsx
│ │ └── index.js
│ ├── react-17-jsx-transform
│ │ ├── index.tsx
│ │ ├── tsconfig.compiled.json
│ │ └── tsconfig.react-jsx.json
│ ├── resolve-index
│ │ ├── foo
│ │ │ └── index.tsx
│ │ └── index.js
│ ├── rollup-commonjs
│ │ ├── bar.js
│ │ ├── foo.js
│ │ └── index.js
│ ├── simple
│ │ ├── bar.mjs
│ │ ├── foo.tsx
│ │ └── index.js
│ ├── standalone-minify
│ │ └── index.js
│ ├── tsconfig-base-url-only-relative-issue-63
│ │ ├── src
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── tsconfig-baseurl-paths
│ │ ├── src
│ │ │ ├── components
│ │ │ │ └── a.ts
│ │ │ ├── index.ts
│ │ │ └── lib
│ │ │ │ └── b.ts
│ │ └── tsconfig.json
│ ├── tsconfig-custom-jsx-factory
│ │ ├── index.tsx
│ │ └── tsconfig.json
│ ├── tsconfig-extends
│ │ ├── index.jsx
│ │ ├── jsconfig.custom.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── tsconfig-full-path
│ │ ├── foo
│ │ │ └── bar
│ │ │ │ └── tsconfig.json
│ │ ├── index.jsx
│ │ └── tsconfig.json
│ ├── tsconfig-jsconfig
│ │ ├── index.tsx
│ │ ├── jsconfig.json
│ │ └── tsconfig.json
│ ├── tsconfig-paths
│ │ ├── src
│ │ │ ├── components
│ │ │ │ └── a.ts
│ │ │ ├── index.ts
│ │ │ └── lib
│ │ │ │ └── b.ts
│ │ └── tsconfig.json
│ └── tsconfig-resolve-to-nearest-tsconfig
│ │ ├── bar
│ │ ├── index.tsx
│ │ └── tsconfig.json
│ │ ├── foo
│ │ ├── index.jsx
│ │ └── tsconfig.json
│ │ ├── index.jsx
│ │ └── tsconfig.json
└── index.ts
├── tools
└── build.ts
└── tsconfig.json
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Automatic Publish
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | publish:
8 | name: Publish
9 | runs-on: ubuntu-latest
10 | permissions:
11 | contents: read
12 | id-token: write
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: pnpm/action-setup@v4
16 | - uses: actions/setup-node@v4
17 | with:
18 | node-version-file: '.node-version'
19 | check-latest: true
20 | cache: 'pnpm'
21 | registry-url: 'https://registry.npmjs.org'
22 | - run: npm install -g npm && pnpm install
23 | - name: Publish
24 | run: |
25 | if git log -1 --pretty=%B | grep "^release: [0-9]\+\.[0-9]\+\.[0-9]\+$";
26 | then
27 | pnpm publish --provenance --access public
28 | elif git log -1 --pretty=%B | grep "^release: [0-9]\+\.[0-9]\+\.[0-9]\+";
29 | then
30 | pnpm publish --tag next --provenance --access public
31 | else
32 | echo "Not a release, skipping publish"
33 | fi
34 | env:
35 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Lint & Test
2 | on:
3 | push:
4 | pull_request:
5 | types: [opened, synchronize]
6 | jobs:
7 | test:
8 | name: Lint & Test
9 | strategy:
10 | matrix:
11 | node-version: [18, 20, 22]
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: pnpm/action-setup@v4
16 | - name: Setup node
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | check-latest: true
21 | cache: 'pnpm'
22 | registry-url: 'https://registry.npmjs.org'
23 | - run: pnpm install
24 | - if: matrix.node-version == 22
25 | run: pnpm run lint
26 | - run: pnpm run test
27 | - run: pnpm run build
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | types
3 | dist
4 | .temp
5 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .eslintrc.json
2 | test
3 | tools
4 | tsconfig.json
5 | CHANGELOG.md
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.12.1
2 |
3 | - Fix: respect legacy decorators. See [#67](https://github.com/SukkaW/rollup-plugin-swc/pull/67/)
4 |
5 | ## 0.12.0
6 |
7 | - The minimum required version has been bumped from 12 to 16.
8 | - Now when TypeScript 5 is detected in your projects will `rollup-plugin-swc3` enable decorators by default (with decorator version `2022-03`), otherwise it is depended by `experimentalDecorators` from `tsconfig.json` (and decorator version `2021-12` will be used). See [#65](https://github.com/SukkaW/rollup-plugin-swc/pull/65/)
9 |
10 | ## 0.11.2
11 |
12 | - New feature `viteMinify` to use swc's minification in Vite.
13 | - When used, it will disable Vite's built-in minification and use swc's minification instead.
14 |
15 | ```js
16 | import { defineConfig } from 'vite';
17 | import { viteMinify } from 'rollup-plugin-swc3'
18 |
19 | export default defineConfig({
20 | plugins: [
21 | viteMinify({
22 | // swc's minify option here
23 | // mangle: {}
24 | // compress: {}
25 | }),
26 | ],
27 | })
28 | ```
29 |
30 | ## 0.11.1
31 |
32 | - Fix [#63](https://github.com/SukkaW/rollup-plugin-swc/issues/63)
33 | - Previously, `rollup-plugin-swc3` only handled relative `compilerOptions.baseUrl` when `compilerOptions.paths` is specified. It is fixed in [#64](https://github.com/SukkaW/rollup-plugin-swc/pull/64), now `rollup-plugin-swc3` will handle relative `compilerOptions.baseUrl` as long as it is specified.
34 |
35 | ## 0.11.0
36 |
37 | - Fix [#58](https://github.com/SukkaW/rollup-plugin-swc/issues/58)
38 | - `rollup-plugin-swc3` now will only perform module resolution inside the files specified in `include` and `exclude` options.
39 | - Replace `rollup-swc-preserve-directives` with `rollup-preserve-directives`
40 |
41 | ## 0.10.4
42 |
43 | - Bump `rollup-swc-preserve-directives` to the latest version
44 |
45 | ## 0.10.3
46 |
47 | - Pass correct options to `@swc/core` (https://github.com/SukkaW/rollup-plugin-swc/pull/54 by @kdy1)
48 |
49 | ## 0.10.2
50 |
51 | - Add warning message when tsconfig is invalid
52 | - Add rollup 4 official support
53 |
54 | ## 0.10.1
55 |
56 | - Fix #41
57 | - `rollup-plugin-swc3` now will always provide `baseUrl` (resolve to an absolute path) to swc as long as the `paths` is specified in the `tsconfig.json` or `jsconfig.json` being read.
58 |
59 | ## 0.10.0
60 |
61 | - Fix #41
62 | - `rollup-plugin-swc3` now will resolve `baseUrl` to an absolute path against the `tsconfig.json` and `jsconfig.json` being read.
63 | - This is to align with the behavior change of the `swc`: https://github.com/swc-project/swc/issues/7799 and https://github.com/swc-project/swc/issues/7800.
64 |
65 | ## 0.9.1
66 |
67 | - The support for `'use client'` and `'use server'` has been separated into a standalone rollup plugin [`rollup-swc-preserve-directives`](https://github.com/huozhi/rollup-plugin-swc-preserve-directives), maintained by [@huozhi](https://github.com/huozhi) and me. The previous `preserveUseDirective` named export is retained for the backward compatibility.
68 |
69 | ## 0.9.0
70 |
71 | - Add support for bundling library for React Server Component with the proper `'use client'` and `'use server'` directives handling:
72 | - Merge duplicated directives in the final bundles
73 | - Multiple output chunks will have their own separated directives, useful when bundling client only code and server only code in different bundles.
74 | - Not enabled by default. manually opt-in by changing two lines of code in your `rollup.config.js`:
75 |
76 | ```diff
77 | // rollup.config.js
78 | // Import `preserveUseDirective` from `rollup-plugin-swc3`...
79 | - import { swc } from 'rollup-plugin-swc3';
80 | + import { swc, preserveUseDirective } from 'rollup-plugin-swc3';
81 |
82 | export default {
83 | input: 'xxxx',
84 | output: {},
85 | plugins: [
86 | swc(),
87 | + preserveUseDirective()
88 | ];
89 | }
90 | ```
91 |
92 | ## 0.8.2
93 |
94 | - Enable CI auto publish release + npm provenance
95 |
96 | ## 0.8.1
97 |
98 | - Fix TypeScript declaration of `include` and `exclude` option (#32)
99 |
100 | ## 0.8.0
101 |
102 | - Add new option `extensions`.
103 | - Along with `include` / `exclude`, this provides a granular way to specify the files that will be processed by the plugin.
104 | - For extensionless imports the plugin will search and resolve files for extensions in the order specified.
105 |
106 | ## 0.7.0
107 |
108 | - Add Rollup 3.0.0 support.
109 | - `rollup-plugin-swc` now supports both Rollup 2 and Rollup 3.
110 |
111 | ## 0.6.0
112 |
113 | - Supports `extends` from `tsconfig.json`/`jsconfig.json`.
114 | - Supports passing a full path of a `tsconfig.json`/`jsconfig.json` file to `tsconfig` option.
115 | - When finding the nearest `tsconfig.json`/`jsconfig.json` from the source file that is currently being transpiled, `rollup-plugin-swc`'s behavior is now aligned with `tsc`.
116 |
117 | ## 0.5.0
118 |
119 | - `rollup-plugin-swc` now also respects `jsx` option from `tsconfig.json` when no corresponding swc option is provided.
120 | - `jsxImportSource` from `tsconfig.json` will be passed to swc's `jsc.transform.react.importSource`
121 | - if `tsconfig.json` specifies `jsx: react-jsx` or `jsx: react-jsxdev`, `rollup-plugin-swc` will set `jsx.tramsform.react.runtime` to `automatic`, otherwise it will be `classic`.
122 | - Currently, swc doesn't support preserving JSX, and will always transpile JSX into javascript code.
123 | - `rollup-plugin-swc` will also set `jsx.tramsform.react.development` to `true` if `tsconfig.json` specifies `jsx: react-jsxdev`.
124 |
125 | ## 0.4.2
126 |
127 | - Remove unused dependency (@huozhi #20)
128 |
129 | ## 0.4.1
130 |
131 | - Fix some minor issues.
132 |
133 | ## 0.4.0
134 |
135 | - Automatically pass rollup's file `id` to swc's `filename` option.
136 | - It should help swc find the `.swcrc`, and also enables some other swc's functionality
137 | - Automatically mapping `.ts/.tsx` to `.mjs/.js/.cjs/.jsx`.
138 | - When using native ESM, import path requires `.js/.jsx` extension for TypeScript with `"moduleResolution": "Node16"`. So rollup-plugin-swc will now try all possible extensions.
139 | - E.g. if you write `import Foo from 'foo.jsx'`, rollup-plugin-swc will search for `foo.ts`, `foo.tsx`, `foo.mjs`, `foo.js`, `foo.jsx`.
140 | - PRs are welcome if you want to make rollup-plugin-swc more spec compliant.
141 |
142 | ## 0.3.0
143 |
144 | - Completely disable swc minify during rollup's `transform` phase.
145 | - Now all minify will be done in rollup's `renderChunk` phase, which is a one-pass process, resulting in even faster build performance.
146 | - Remove the workaround for rollup's virtual module that is introduced in 0.1.2 (https://github.com/SukkaW/rollup-plugin-swc/pull/1)
147 | - swc has fixed the issue, and the corresponding test case has been added in https://github.com/swc-project/swc/pull/4255
148 | - The `peerDependencies` of swc has been updated to `>=1.2.165`. You will need to bump the version of swc to 1.2.165 or higher after this release.
149 |
150 | ## 0.2.0
151 |
152 | - Standalone minify plugin
153 | - Support reading `baseUrl` and `paths` from your `tsconfig.json` (and `jsconfig.json`).
154 | - These fields will be passed to swc directly. They won't affect how rollup resolve your imports. Please use other plugins to resolve your imports' aliases (e.g., add [rollup-plugin-typescript-paths](https://www.npmjs.com/package/rollup-plugin-typescript-paths) or [rollup-plugin-tsconfig-paths](https://www.npmjs.com/package/rollup-plugin-tsconfig-paths) before `@rollup/plugin-node-resolve`).
155 |
156 | ## 0.1.4
157 |
158 | - Add `.mjs` extension support
159 | - Export a `default` for use with rollup's `--configPlugin`
160 |
161 | ## 0.1.3
162 |
163 | - Fix a bug caused by the workaround introduced in 0.1.2
164 |
165 | ## 0.1.2
166 |
167 | - Fix transform failed when rollup virtual module is involved.
168 | - https://rollupjs.org/guide/en/#conventions
169 | - https://github.com/SukkaW/rollup-plugin-swc/pull/1
170 | - https://github.com/swc-project/swc/issues/2853
171 | - Support read default config from [`jsconfig.json`](https://code.visualstudio.com/docs/languages/jsconfig) as well
172 | - `jsconfig.json` will be ignored if `tsconfig.json` and `jsconfig.json` both exists.
173 |
174 | ## 0.1.1
175 |
176 | - Add `.npmignore` to reduce the size of the package.
177 | - Use `deepmerge` to merge plugin options config with your given `tsconfig.json`.
178 |
179 | ## 0.1.0
180 |
181 | The first release.
182 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Sukka
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 | rollup-plugin-swc
5 |
6 | [SWC](https://swc.rs/) is an extensible Rust-based platform for the next generation of fast developer tools. This plugin is designed to replace `rollup-plugin-typescript2`, `@rollup/plugin-typescript`, `@rollup/plugin-babel` and `rollup-plugin-terser` for you.
7 |
8 | **New:** Building library for React Server Component support is added in `0.9.0`! `'use client'` and `'use server'` directives now are handled properly, without triggering rollup warnings. [Start using `'use client'` and `'use server'` in your library by adding two lines in your `rollup.config.js`](#react-server-component-directives-use-client-and-use-server)
9 |
10 | > Since `0.9.1` the support for `'use client'` and `'use server'` has been separated into a standalone rollup plugin [`rollup-preserve-directives`](https://github.com/huozhi/rollup-preserve-directives), the previous `preserveUseDirective` named export is retained for the backward compatibility.
11 |
12 | ## Comparison
13 |
14 | | | [sukkaw/rollup-plugin-swc](https://github.com/SukkaW/rollup-plugin-swc) | [mentaljam/rollup-plugin-swc](https://github.com/mentaljam/rollup-plugin-swc) | [nicholasxjy/rollup-plugin-swc2](https://github.com/nicholasxjy/rollup-plugin-swc2) | [@rollup/plugin-swc](https://github.com/rollup/plugins/tree/master/packages/swc) |
15 | | ---------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
16 | | `minify` your bundle in one pass[^1] | ✅ | 🛑 | 🛑 | 🛑 |
17 | | Standalone `swcMinify` plugin | ✅ | 🛑 | 🛑 | 🛑 |
18 | | Config Intellisense[^2] | ✅ | 🛑 | 🛑 | 🛑 |
19 | | Reads your `tsconfig.json` and `jsconfig.json` | ✅ | 🛑 | 🛑 | 🛑 |
20 | | ESM export | ✅ | 🟡[^3] | 🛑 | ✅ |
21 | | TypeScript declarations | ✅ | ✅ | ✅ | ✅ |
22 | | Has testing | ✅ | 🛑 | 🛑 | ✅ |
23 |
24 | [^1]: If minify is called in Rollup's `transform` phase, every individual module processed will result in a minify call. However, if minify is called in Rollup's `renderChunk` phase, the minify will only be called once in one whole pass before Rollup generates bundle, results in a faster build.
25 | [^2]: Autocompletion and type checking in your IDE
26 | [^3]: `mentaljam/rollup-plugin-swc` has both `main` and `module` fields in `package.json`, but has🛑`exports` field.
27 |
28 | ## Install
29 |
30 | ```bash
31 | $ npm i @swc/core rollup-plugin-swc3
32 | # If you prefer yarn
33 | # yarn add @swc/core rollup-plugin-swc3
34 | # If you prefer pnpm
35 | # pnpm add @swc/core rollup-plugin-swc3
36 | ```
37 |
38 | ## Usage
39 |
40 | ```js
41 | // rollup.config.js
42 | import { swc } from 'rollup-plugin-swc3';
43 |
44 | export default {
45 | input: 'xxxx',
46 | output: {},
47 | plugins: [
48 | swc({
49 | // All options are optional
50 | include: /\.[mc]?[jt]sx?$/, // default
51 | exclude: /node_modules/, // default
52 | tsconfig: 'tsconfig.json', // default
53 | // tsconfig: false, // You can also prevent `rollup-plugin-swc` from reading tsconfig.json, see below
54 | // And add your swc configuration here!
55 | // "filename" will be ignored since it is handled by rollup
56 | jsc: {}
57 | }),
58 | ];
59 | }
60 | ```
61 |
62 | If you want autocompletion in your IDE or type check:
63 |
64 | ```js
65 | import { swc, defineRollupSwcOption } from 'rollup-plugin-swc3';
66 |
67 | export default {
68 | input: 'xxxx',
69 | output: {},
70 | plugins: [
71 | swc(defineRollupSwcOption({
72 | // ... There goes the plugin's configuration
73 | })),
74 | ];
75 | }
76 |
77 | // or
78 | /** @type {import('rollup-plugin-swc3').PluginOptions} */
79 | const swcPluginConfig = {}
80 | ```
81 |
82 | ### `exclude`
83 |
84 | - Type: `string | RegExp | Array`
85 | - Default: `/node_modules/`
86 |
87 | A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patterns, which specifies the files in the build the plugin should *ignore*.
88 |
89 | ### `extensions`
90 |
91 | - Type: `string[]`
92 | - Default: `['.ts', '.tsx', '.mjs', '.js', '.cjs', '.jsx']`
93 |
94 | Specifies the files in the build the plugin should operate on. Also, the plugin will search and resolve files for extensions in the order specified for extensionless imports.
95 |
96 | ### `include`
97 |
98 | - Type: `string | RegExp | Array`
99 | - Default: `/\.[mc]?[jt]sx?$/`
100 |
101 | A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patterns, which specifies the files in the build the plugin should operate on.
102 |
103 | ### `tsconfig`
104 |
105 | - Type: `string | false | undefined`
106 | - Default: `'tsconfig.json'`
107 |
108 | `rollup-plugin-swc` will read your `tsconfig.json` or [`jsconfig.json`](https://code.visualstudio.com/docs/languages/jsconfig) for default values if your doesn't provide corresponding swc options:
109 |
110 | - The configuration your passed to `rollup-plugin-swc` will always have the highest priority (higher than `tsconfig.json`/`jsconfig.json`).
111 | - `rollup-plugin-swc` uses [`get-tsconfig`](https://www.npmjs.com/package/get-tsconfig) to find the `tsconfig.json`/`jsconfig.json` for the file currently being transpiled.
112 | - You can also provide a custom filename (E.g. `tsconfig.rollup.json`, `jsconfig.compile.json`) to `tsconfig` option, and `rollup-plugin-swc` will find and resolve the *nearest* file with that filename.
113 | - You can also provide an absolute path (E.g. `/path/to/your/tsconfig.json`) to `tsconfig` option, and `rollup-plugin-swc` will only use the provided path as a single source of truth.
114 | - You can prevent `rollup-plugin-swc` from reading `tsconfig.json`/`jsconfig.json` by setting `tsconfig` option to `false`.
115 | - `jsconfig.json` will be ignored if `tsconfig.json` and `jsconfig.json` both exist.
116 | - The `extends` of `tsconfig.json`/`jsconfig.json` is ~~not supported~~ now supported.
117 | - `compilerOptions.target` will be passed to swc's `jsc.target`.
118 | - `compilerOptions.jsxImportSource`, `compilerOptions.jsxFactory`, and `compilerOptions.jsxFragmentFactory` will be passed to swc's `jsc.transform.react.importSource`, `jsc.transform.react.pragma` and `jsc.transform.react.pragmaFrag`.
119 | - When `compilerOptions.jsx` is either `react-jsx` or `react-jsxdev`, swc's `jsc.transform.react.runtime` will be `automatic`, otherwise it will be `classic`.
120 | - `compilerOptions.jsx: react-jsxdev` will also set swc's `jsc.transform.react.development` to `true`.
121 | - `compilerOptions.jsx: preserve` will be ignored. swc will always transpile your jsx into javascript code.
122 | - `compilerOptions.baseUrl` and `compilerOptions.paths` will be passed to swc's `jsc.baseUrl` and `jsc.paths` directly. They won't affect how rollup resolve your imports. If you have encounted any issue during bundling, please use other plugins to resolve your imports' aliases (e.g., add [rollup-plugin-typescript-paths](https://www.npmjs.com/package/rollup-plugin-typescript-paths) or [rollup-plugin-tsconfig-paths](https://www.npmjs.com/package/rollup-plugin-tsconfig-paths) before `@rollup/plugin-node-resolve`).
123 | - `compilerOptions.importHelpers` will be passed to swc's `jsc.externalHelpers`. You will have to have `@swc/helpers` avaliable in your project when enabled.
124 | - `compilerOptions.experimentalDecorators` and `compilerOptions.emitDecoratorMetadata` will be passed to swc's `jsc.parser.decorators` and `jsc.transform.decoratorMetadata`.
125 | - `compilerOptions.esModuleInterop` will always be **ignored**, as swc requires `module.type` to exist when `module.noInterop` is given.
126 |
127 | ### Standalone Minify Plugin
128 |
129 | If you only want to use `swc` to minify your bundle:
130 |
131 | ```js
132 | import { minify } from 'rollup-plugin-swc3'
133 |
134 | export default {
135 | plugins: [
136 | minify({
137 | // swc's minify option here
138 | // mangle: {}
139 | // compress: {}
140 | }),
141 | ],
142 | }
143 | ```
144 |
145 | If you want autocompletion in your IDE or type check:
146 |
147 | ```js
148 | import { minify, defineRollupSwcMinifyOption } from 'rollup-plugin-swc3'
149 |
150 | export default {
151 | plugins: [
152 | minify(
153 | defineRollupSwcMinifyOption({
154 | // swc's minify option here
155 | // mangle: {}
156 | // compress: {}
157 | })
158 | ),
159 | ],
160 | }
161 |
162 | // or
163 | /** @type {import('@swc/core').JsMinifyOptions} */
164 | const swcMinifyConfig = {}
165 | ```
166 |
167 | If you are are using Vite and you do not want to use `terser` or `esbuild` for minification, `rollup-plugin-swc3` also provides a standalone minify plugin designed for Vite:
168 |
169 | ```js
170 | import { defineConfig } from 'vite';
171 | import { viteMinify } from 'rollup-plugin-swc3'
172 |
173 | export default defineConfig({
174 | plugins: [
175 | viteMinify({
176 | // swc's minify option here
177 | // mangle: {}
178 | // compress: {}
179 | }),
180 | ],
181 | })
182 | ```
183 |
184 | ### React Server Component directives (`'use client'` and `'use server'`)
185 |
186 | ~~Since version `0.9.0`, the support for `'use client'` and `'use server'` has been added:~~
187 |
188 | > The support for `'use client'` and `'use server'` has been separated into a standalone rollup plugin [`rollup-preserve-directives`](https://github.com/huozhi/rollup-preserve-directives), maintained by [@huozhi](https://github.com/huozhi) and me. The previous `preserveUseDirective` named export is retained for the backward compatibility.
189 |
190 | ```bash
191 | # npm
192 | npm install -D rollup-preserve-directives
193 | # yarn
194 | yarn add -D rollup-preserve-directives
195 | # pnpm
196 | pnpm add -D rollup-preserve-directives
197 | ```
198 |
199 | ```js
200 | // rollup.config.js
201 | import { swc } from 'rollup-plugin-swc3';
202 | import preserveDirectives from 'rollup-preserve-directives';
203 |
204 | export default {
205 | input: 'xxxx',
206 | output: {},
207 | plugins: [
208 | swc(),
209 | // And add `preserveDirectives` plugin after the `swc` plugin
210 | preserveDirectives()
211 | ];
212 | }
213 | ```
214 |
215 | And that is it!
216 |
217 | `preserveDirectives` supports:
218 |
219 | - Merging duplicated directives in the output bundles
220 |
221 | ```js
222 | // src/foo.js
223 | 'use sukka';
224 | 'use foxtail';
225 |
226 | export const foo = 'foo';
227 |
228 | // src/bar.js
229 | 'use sukka';
230 | export const bar = 'bar';
231 |
232 | // src/index.js
233 | export { foo } from './foo';
234 | export { bar } from './bar';
235 |
236 | // rollup.config.js
237 | export default {
238 | input: 'src/index.js',
239 | output: { file: 'dist/index.js' }
240 | plugins: [swc(), preserveDirectives()]
241 | }
242 |
243 | // dist/index.js
244 | 'use sukka';
245 | 'use foxtail';
246 |
247 | const foo = 'foo';
248 | const bar = 'bar';
249 |
250 | export { foo, bar };
251 | ```
252 |
253 | - When bundle React Client Component and React Server Component separately, mutiple entries will have their own separated and correct directives:
254 |
255 | ```js
256 | // src/client.js
257 | 'use client';
258 | import { useState } from 'react';
259 | export const Foo = () => { useState('client-only code') };
260 |
261 | // src/server.js
262 | 'use server';
263 | import 'fs';
264 | export const bar = 'server only code';
265 |
266 | // rollup.config.js
267 | export default {
268 | // let rollup bundle client and server separately by adding two entries
269 | input: {
270 | client: 'src/client.js',
271 | server: 'src/server.js'
272 | },
273 | // output both client bundle and server bundle in the "dist/" directory
274 | output: { dir: 'dist/', entryFileName: '[name].js' }
275 | plugins: [swc(), preserveDirectives()]
276 | }
277 |
278 | // dist/client.js
279 | 'use client';
280 | import { useState } from 'react';
281 | const Foo = () => { useState('client-only code') };
282 | export { Foo };
283 |
284 | // dist/server.js
285 | 'use server';
286 | import 'fs';
287 | const bar = 'server only code';
288 | export { bar };
289 | ```
290 |
291 | ### Write your Rollup config in TypeScript
292 |
293 | You can write your Rollup config file in `rollup.config.ts`, and use the following command:
294 |
295 | ```sh
296 | rollup --config rollup.config.ts --configPlugin swc3
297 | ```
298 |
299 | ### TypeScript Declaration File
300 |
301 | There are serveral ways to generate declaration file:
302 |
303 | - Use `tsc` with `emitDeclarationOnly`, the slowest way but you get type checking, it doesn't bundle the `.d.ts` files.
304 | - Use `rollup-plugin-dts` which generates and bundle `.d.ts`, also does type checking. It is used by this plugin as well.
305 |
306 | ### Use with Non-react JSX
307 |
308 | You can either configure it in your `tsconfig.json` or in your `rollup.config.js`.
309 |
310 | ```js
311 | // Vue JSX
312 | import { swc, defineRollupSwcOption } from 'rollup-plugin-swc3';
313 |
314 | export default {
315 | input: 'xxxx',
316 | output: {},
317 | plugins: [
318 | swc(defineRollupSwcOption({
319 | jsc: {
320 | experimental: {
321 | plugins: [['swc-plugin-vue-jsx', {}]] // npm i swc-plugin-vue-jsx
322 | }
323 | }
324 | })),
325 | ];
326 | }
327 | ```
328 |
329 | ```js
330 | // Preact
331 | import { swc, defineRollupSwcOption } from 'rollup-plugin-swc3';
332 |
333 | export default {
334 | input: 'xxxx',
335 | output: {},
336 | plugins: [
337 | swc(defineRollupSwcOption({
338 | jsc: {
339 | transform:{
340 | react: {
341 | pragma: 'h',
342 | pragmaFrag: 'Fragment'
343 | // To use preact/jsx-runtime:
344 | // importSource: 'preact',
345 | // runtime: 'automatic'
346 | }
347 | }
348 | }
349 | })),
350 | ];
351 | }
352 | ```
353 |
354 | ---
355 |
356 | **rollup-plugin-swc** © [Sukka](https://github.com/SukkaW), Released under the [MIT](./LICENSE) License.
357 | Inspired by [egoist](https://github.com/egoist)'s [rollup-plugin-esbuild](https://github.com/egoist/rollup-plugin-esbuild).
358 | Authored and maintained by Sukka with help from contributors ([list](https://github.com/SukkaW/rollup-plugin-swc/graphs/contributors)).
359 |
360 | > [Personal Website](https://skk.moe) · [Blog](https://blog.skk.moe) · GitHub [@SukkaW](https://github.com/SukkaW) · Telegram Channel [@SukkaChannel](https://t.me/SukkaChannel) · Mastodon [@sukka@acg.mn](https://acg.mn/@sukka) · Twitter [@isukkaw](https://twitter.com/isukkaw) · Keybase [@sukka](https://keybase.io/sukka)
361 |
362 |
363 |
364 |
365 |
366 |
367 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('eslint-config-sukka').sukka({
4 | node: true,
5 | ts: {
6 | tsconfigPath: './tsconfig.json'
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rollup-plugin-swc3",
3 | "version": "0.12.1",
4 | "description": "Use SWC with Rollup to transform ESNext and TypeScript code.",
5 | "homepage": "https://github.com/SukkaW/rollup-plugin-swc#readme",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/SukkaW/rollup-plugin-swc.git"
9 | },
10 | "bugs": {
11 | "url": "https://github.com/SukkaW/rollup-plugin-swc/issues"
12 | },
13 | "main": "dist/index.js",
14 | "module": "dist/index.mjs",
15 | "types": "dist/index.d.ts",
16 | "files": [
17 | "dist",
18 | "src"
19 | ],
20 | "exports": {
21 | "import": "./dist/index.mjs",
22 | "default": "./dist/index.js"
23 | },
24 | "scripts": {
25 | "prepublishOnly": "npm run clean && npm run test && npm run build",
26 | "clean": "rimraf ./dist ./test/temp",
27 | "build": "SWC_NODE_PROJECT=./tsconfig.json node -r @swc-node/register tools/build.ts",
28 | "test": "SWC_NODE_PROJECT=./tsconfig.json mocha -r @swc-node/register -r mocha-expect-snapshot test/index.ts",
29 | "lint": "eslint --format sukka ."
30 | },
31 | "author": "",
32 | "license": "MIT",
33 | "dependencies": {
34 | "@dual-bundle/import-meta-resolve": "^4.1.0",
35 | "@fastify/deepmerge": "^3.1.0",
36 | "@rollup/pluginutils": "^5.1.4",
37 | "@swc/types": "^0.1.21",
38 | "get-tsconfig": "^4.10.0",
39 | "rollup-preserve-directives": "^1.1.3"
40 | },
41 | "devDependencies": {
42 | "@eslint-sukka/node": "^6.20.0",
43 | "@rollup/plugin-commonjs": "^28.0.3",
44 | "@rollup/plugin-json": "^6.1.0",
45 | "@rollup/plugin-node-resolve": "^16.0.1",
46 | "@swc-node/register": "^1.10.10",
47 | "@swc/core": "^1.11.24",
48 | "@types/mocha": "^10.0.10",
49 | "@types/node": "^22.15.18",
50 | "@types/which": "^3.0.4",
51 | "eslint": "^9.26.0",
52 | "eslint-config-sukka": "^6.20.0",
53 | "eslint-formatter-sukka": "^6.20.0",
54 | "fdir": "^6.4.4",
55 | "memdisk": "^1.2.1",
56 | "mocha": "^11.3.0",
57 | "mocha-expect-snapshot": "^7.2.0",
58 | "picocolors": "^1.1.1",
59 | "rimraf": "^6.0.1",
60 | "rollup": "^4.40.2",
61 | "rollup-plugin-dts": "^6.2.1",
62 | "rollup2": "npm:rollup@^2.79.2",
63 | "rollup3": "npm:rollup@^3.29.5",
64 | "tinyexec": "0.3.2",
65 | "typescript": "^5.8.3",
66 | "vite": "^5.4.19",
67 | "which": "^5.0.0"
68 | },
69 | "peerDependencies": {
70 | "@swc/core": ">=1.2.165",
71 | "rollup": "^2.0.0 || ^3.0.0 || ^4.0.0"
72 | },
73 | "packageManager": "pnpm@10.11.0",
74 | "engines": {
75 | "node": ">=16"
76 | },
77 | "overrides": {
78 | "rollup": "$rollup"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/directive.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * preserveUseDirective is now a separate plugin, re-export to maintain the backward compatibility
3 | */
4 | export { default as preserveUseDirective } from 'rollup-preserve-directives';
5 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { Plugin as RollupPlugin } from 'rollup';
2 | import fs from 'node:fs';
3 | import process from 'node:process';
4 | import { extname, resolve, dirname, join } from 'node:path';
5 | import { createFilter } from '@rollup/pluginutils';
6 | import type { FilterPattern } from '@rollup/pluginutils';
7 | import type {
8 | Options as SwcOptions,
9 | JscTarget,
10 | JsMinifyOptions
11 | } from '@swc/types';
12 | import {
13 | transform as swcTransform,
14 | minify as swcMinify
15 | } from '@swc/core';
16 | import createDeepMerge from '@fastify/deepmerge';
17 |
18 | import { getOptions, checkIsLegacyTypeScript } from './options';
19 |
20 | import type { Plugin as VitePlugin } from 'vite';
21 |
22 | export type PluginOptions = {
23 | include?: FilterPattern,
24 | exclude?: FilterPattern,
25 | extensions?: string[] | undefined,
26 | /**
27 | * Use given tsconfig file instead
28 | * Disable it by setting to `false`
29 | */
30 | tsconfig?: string | false | undefined
31 | } & Omit;
32 |
33 | const INCLUDE_REGEXP = /\.[cm]?[jt]sx?$/;
34 | const EXCLUDE_REGEXP = /node_modules/;
35 |
36 | const ACCEPTED_EXTENSIONS = ['.ts', '.tsx', '.mjs', '.js', '.cjs', '.jsx'];
37 |
38 | const deepmerge = createDeepMerge({
39 | all: true,
40 | mergeArray(options) {
41 | // overwrite instead of concatenating arrays
42 | return (target, source) => options.clone(source);
43 | }
44 | });
45 |
46 | function fileExists(path: string) {
47 | return fs.promises.access(path, fs.constants.F_OK)
48 | .then(() => true)
49 | .catch(() => false);
50 | }
51 |
52 | function swc(options: PluginOptions = {}): RollupPlugin {
53 | const filter = createFilter(
54 | options.include || INCLUDE_REGEXP,
55 | options.exclude || EXCLUDE_REGEXP
56 | );
57 |
58 | const extensions = options.extensions || ACCEPTED_EXTENSIONS;
59 |
60 | const resolveFile = async (resolved: string, index = false) => {
61 | const fileWithoutExt = resolved.replace(INCLUDE_REGEXP, '');
62 |
63 | for (const ext of extensions) {
64 | const file = index ? join(resolved, `index${ext}`) : `${fileWithoutExt}${ext}`;
65 | // We need to respect the order, and we only check one file at a time, and we can return early
66 | // eslint-disable-next-line no-await-in-loop -- see above
67 | if (await fileExists(file)) return file;
68 | }
69 | return null;
70 | };
71 |
72 | const isLegacyTypeScript = checkIsLegacyTypeScript();
73 |
74 | return {
75 | name: 'swc',
76 |
77 | async resolveId(importee, importer) {
78 | // ignore IDs with null character, these belong to other plugins
79 | if (importee.startsWith('\0')) {
80 | return null;
81 | }
82 |
83 | // If the importer (the module that is importing the importee) should not be handled by this plugin,
84 | // we skip the resolution to avoid the issue like https://github.com/SukkaW/rollup-plugin-swc/issues/58
85 | if (!filter(importer)) {
86 | return null;
87 | }
88 |
89 | if (importer && importee[0] === '.') {
90 | const resolved = resolve(
91 | importer ? dirname(importer) : process.cwd(),
92 | importee
93 | );
94 |
95 | let file = await resolveFile(resolved);
96 | if (file) return file;
97 | if (!file && await fileExists(resolved) && (await fs.promises.stat(resolved)).isDirectory()) {
98 | file = await resolveFile(resolved, true);
99 | if (file) return file;
100 | }
101 | }
102 | },
103 |
104 | async transform(code: string, id: string) {
105 | if (!filter(id)) {
106 | return null;
107 | }
108 |
109 | const ext = extname(id);
110 | if (!extensions.includes(ext)) return null;
111 |
112 | const isTypeScript = ext === '.ts' || ext === '.mts' || ext === '.cts' || ext === '.tsx';
113 | const isTsx = ext === '.tsx';
114 |
115 | const tsconfigOptions
116 | = options.tsconfig === false
117 | ? {}
118 | : getOptions(this, dirname(id), options.tsconfig);
119 |
120 | const useReact17NewTransform = tsconfigOptions.jsx === 'react-jsx' || tsconfigOptions.jsx === 'react-jsxdev';
121 |
122 | const swcOptionsFromTsConfig: SwcOptions = {
123 | jsc: {
124 | externalHelpers: tsconfigOptions.importHelpers,
125 | parser: {
126 | syntax: isTypeScript ? 'typescript' : 'ecmascript',
127 | [isTypeScript ? 'tsx' : 'jsx']: isTypeScript ? isTsx : true,
128 | decorators: !isLegacyTypeScript || tsconfigOptions.experimentalDecorators
129 | },
130 | transform: {
131 | decoratorMetadata: tsconfigOptions.emitDecoratorMetadata,
132 | react: {
133 | runtime: useReact17NewTransform
134 | ? 'automatic'
135 | : 'classic',
136 | importSource: tsconfigOptions.jsxImportSource,
137 | pragma: tsconfigOptions.jsxFactory,
138 | pragmaFrag: tsconfigOptions.jsxFragmentFactory,
139 | development: tsconfigOptions.jsx === 'react-jsxdev' ? true : undefined
140 | },
141 | decoratorVersion: isLegacyTypeScript ? '2021-12' : (tsconfigOptions.experimentalDecorators ? '2021-12' : '2022-03')
142 | },
143 | target: tsconfigOptions.target?.toLowerCase() as JscTarget | undefined,
144 | baseUrl: tsconfigOptions.baseUrl,
145 | paths: tsconfigOptions.paths
146 | }
147 | };
148 |
149 | const {
150 | // @ts-expect-error -- We have to make sure that we don't pass these options to swc
151 | filename: _1, // We will use `id` from rollup instead
152 | include: _2, // Rollup's filter is incompatible with swc's filter
153 | exclude: _3,
154 | tsconfig: _4, // swc doesn't have tsconfig option
155 | extensions: _5, // swc doesn't have extensions option
156 | minify: _6, // We will disable minify during transform, and perform minify in renderChunk
157 | ...restSwcOptions
158 | } = options;
159 |
160 | const swcOption = deepmerge(
161 | swcOptionsFromTsConfig,
162 | restSwcOptions,
163 | {
164 | jsc: {
165 | minify: undefined // Disable minify on transform, do it on renderChunk
166 | },
167 | filename: id,
168 | minify: false // Disable minify on transform, do it on renderChunk
169 | }
170 | );
171 |
172 | return swcTransform(
173 | code,
174 | swcOption
175 | );
176 | },
177 |
178 | renderChunk(code: string) {
179 | if (options.minify || options.jsc?.minify?.mangle || options.jsc?.minify?.compress) {
180 | return swcMinify(code, {
181 | ...options.jsc?.minify,
182 | module: true
183 | });
184 | }
185 |
186 | return null;
187 | }
188 | };
189 | }
190 |
191 | function minify(options: JsMinifyOptions = {}): RollupPlugin {
192 | return {
193 | name: 'swc-minify',
194 |
195 | renderChunk: {
196 | order: 'post',
197 | handler(code: string) {
198 | return swcMinify(code, options);
199 | }
200 | }
201 | };
202 | }
203 |
204 | // Use `any` here to prevent Vite's type from being output in the dist dts
205 | function viteMinify(options: JsMinifyOptions = {}): any {
206 | return {
207 | name: 'vite-swc-minify',
208 | // Enforce minification after other plugins
209 | enforce: 'post',
210 | apply: 'build',
211 | config(viteOption) {
212 | if (viteOption.build?.minify) {
213 | // Disable Vite built-in minification
214 | viteOption.build.minify = false;
215 | // When build.minify is enabled, Vite will also enable build.cssMinify
216 | // But here we only want to disable Vite built-in JS minification
217 | // So we need to manually enable build.cssMinify after disabling build.minify
218 | if (!viteOption.build.cssMinify) {
219 | viteOption.build.cssMinify = true;
220 | }
221 | }
222 | return viteOption;
223 | },
224 | renderChunk: {
225 | order: 'post',
226 | handler(code) {
227 | return swcMinify(code, options);
228 | }
229 | }
230 | } satisfies VitePlugin;
231 | }
232 |
233 | function defineRollupSwcOption(option: PluginOptions) {
234 | return option;
235 | }
236 |
237 | function defineRollupSwcMinifyOption(option: JsMinifyOptions) {
238 | return option;
239 | }
240 |
241 | export default swc;
242 | export { swc, defineRollupSwcOption, minify, viteMinify, defineRollupSwcMinifyOption };
243 | export { preserveUseDirective } from './directive';
244 |
--------------------------------------------------------------------------------
/src/options.ts:
--------------------------------------------------------------------------------
1 | import { getTsconfig, parseTsconfig } from 'get-tsconfig';
2 | import { resolve } from '@dual-bundle/import-meta-resolve';
3 | import path from 'node:path';
4 | import fs from 'node:fs';
5 |
6 | import type { TsConfigJson } from 'get-tsconfig';
7 | import type { TransformPluginContext } from 'rollup';
8 | import { fileURLToPath } from 'node:url';
9 |
10 | const cache = new Map();
11 |
12 | export function getOptions(
13 | ctx: TransformPluginContext,
14 | cwd: string,
15 | tsconfig?: string
16 | ) {
17 | const cacheKey = `${cwd}:${tsconfig ?? 'undefined'}`;
18 |
19 | if (cache.has(cacheKey)) {
20 | return cache.get(cacheKey) ?? {};
21 | }
22 |
23 | if (tsconfig && path.isAbsolute(tsconfig)) {
24 | const compilerOptions = parseTsconfig(tsconfig).compilerOptions ?? {};
25 |
26 | const tsconfigDir = path.dirname(tsconfig);
27 | if (compilerOptions.paths != null || compilerOptions.baseUrl != null) {
28 | compilerOptions.baseUrl = compilerOptions.baseUrl == null
29 | ? tsconfigDir
30 | : path.resolve(tsconfigDir, compilerOptions.baseUrl);
31 | }
32 |
33 | cache.set(cacheKey, compilerOptions);
34 | return compilerOptions;
35 | }
36 |
37 | let result = getTsconfig(cwd, tsconfig || 'tsconfig.json');
38 | // Only fallback to `jsconfig.json` when tsconfig can not be resolved AND custom tsconfig filename is not provided
39 | if (!result && !tsconfig) {
40 | ctx.warn({
41 | message: 'Can\'t find tsconfig.json, trying jsconfig.json now',
42 | pluginCode: 'SWC_TSCONFIG_NOT_EXISTS'
43 | });
44 | result = getTsconfig(cwd, 'jsconfig.json');
45 | }
46 |
47 | const compilerOptions = result?.config.compilerOptions ?? {};
48 | if (
49 | (compilerOptions.paths != null || compilerOptions.baseUrl != null)
50 | && result?.path
51 | ) {
52 | const tsconfigDir = path.dirname(result.path);
53 | compilerOptions.baseUrl = compilerOptions.baseUrl == null
54 | ? tsconfigDir
55 | : path.resolve(tsconfigDir, compilerOptions.baseUrl);
56 | }
57 |
58 | cache.set(cacheKey, compilerOptions);
59 | return compilerOptions;
60 | }
61 |
62 | export function checkIsLegacyTypeScript() {
63 | try {
64 | // @ts-expect-error -- It's required to using 'import.mtea.url' but i don't want to change the tsconfig.
65 | const tsPath = resolve('typescript/package.json', import.meta.url);
66 | const { version } = JSON.parse(fs.readFileSync(fileURLToPath(tsPath), 'utf-8'));
67 | const [major] = version.split('.');
68 | // typescript 5+ enable experimentalDecorators by default so we think it's not legacy
69 | return +major < 5;
70 | } catch {
71 | return false;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/test/fixtures/custom-tsconfig/index.jsx:
--------------------------------------------------------------------------------
1 | export const foo = foo
2 |
--------------------------------------------------------------------------------
/test/fixtures/custom-tsconfig/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsxFactory": "custom"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/custom-tsconfig/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsxFactory": "h"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/decorators/index.ts:
--------------------------------------------------------------------------------
1 | function trace(value, {kind, name}) {
2 | if (kind === 'method') {
3 | return function (...args) {
4 | console.log('trace')
5 | return value.apply(this, args);
6 | };
7 | }
8 | }
9 |
10 | class People {
11 | xzy: string
12 | constructor(){
13 | this.xzy = 'xzy';
14 | }
15 | @trace
16 | test() {
17 | return this.xzy
18 | }
19 | }
20 |
21 | const p = new People();
22 |
23 | p.test();
24 |
--------------------------------------------------------------------------------
/test/fixtures/directive-include-use-client/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | export default function client() {
4 | return React.useState(null)
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/directive-merge-use-client/bar.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 | export const bar = 'sukka';
3 |
--------------------------------------------------------------------------------
/test/fixtures/directive-merge-use-client/foo.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 | "use sukka";
3 | export const foo = 'sukka';
4 |
--------------------------------------------------------------------------------
/test/fixtures/directive-merge-use-client/index.tsx:
--------------------------------------------------------------------------------
1 | export { foo } from './foo';
2 | export { bar } from './bar';
3 |
--------------------------------------------------------------------------------
/test/fixtures/directive-split-entry/client.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 | export const foo = 'client';
3 |
--------------------------------------------------------------------------------
/test/fixtures/directive-split-entry/server.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 | export const bar = 'server';
3 |
--------------------------------------------------------------------------------
/test/fixtures/disable-reading-tsconfig/.swcrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "targets": "defaults, not dead"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/disable-reading-tsconfig/index.jsx:
--------------------------------------------------------------------------------
1 | export const foo = 1;
2 |
--------------------------------------------------------------------------------
/test/fixtures/disable-reading-tsconfig/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/extensions/index.mts:
--------------------------------------------------------------------------------
1 | import { foo } from './module'
2 | console.log(foo);
3 |
--------------------------------------------------------------------------------
/test/fixtures/extensions/module.cts:
--------------------------------------------------------------------------------
1 | module.exports.foo = 'sukka';
2 |
--------------------------------------------------------------------------------
/test/fixtures/issue-58/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-expect-error -- the dependency is only installed during test
2 | import EE from 'eventemitter3';
3 |
4 | const ee = new EE();
5 | ee.on('test', () => {
6 | console.log('test');
7 | });
8 |
--------------------------------------------------------------------------------
/test/fixtures/issue-58/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "issue-58",
3 | "private": true,
4 | "devDependencies": {
5 | "eventemitter3": "^5.0.1"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/fixtures/jsconfig-custom-jsx-factory/index.tsx:
--------------------------------------------------------------------------------
1 | export const foo = foo
2 |
--------------------------------------------------------------------------------
/test/fixtures/jsconfig-custom-jsx-factory/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsxFactory": "h"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/legacy-decorators/index.ts:
--------------------------------------------------------------------------------
1 | function trace(_target: any, _name: string, descriptor: PropertyDescriptor) {
2 | const original = descriptor.value;
3 | descriptor.value = function () {
4 | console.log('trace');
5 | return original.call(this);
6 | };
7 |
8 | return descriptor;
9 | }
10 |
11 | class People {
12 | xzy: string;
13 | constructor() {
14 | this.xzy = 'xzy';
15 | }
16 |
17 | @trace
18 | test() {
19 | return this.xzy;
20 | }
21 | }
22 |
23 | const p = new People();
24 |
25 | p.test();
26 |
--------------------------------------------------------------------------------
/test/fixtures/legacy-decorators/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "experimentalDecorators": true,
5 | "emitDecoratorMetadata": true,
6 | "module": "ES2022",
7 | "baseUrl": "./"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/fixtures/load-json/foo.json:
--------------------------------------------------------------------------------
1 | {
2 | "foo": true
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/load-json/index.js:
--------------------------------------------------------------------------------
1 | import foo from './foo.json'
2 | console.log(foo)
3 |
--------------------------------------------------------------------------------
/test/fixtures/load-jsx-tsx/foo.tsx:
--------------------------------------------------------------------------------
1 | import { util } from './some.util'
2 | export default class Foo {
3 | render() {
4 | return {util}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/load-jsx-tsx/index.js:
--------------------------------------------------------------------------------
1 | import Foo from './foo.js'
2 |
3 | console.log(Foo)
4 |
--------------------------------------------------------------------------------
/test/fixtures/load-jsx-tsx/some.util.ts:
--------------------------------------------------------------------------------
1 | export const util = 42
2 |
--------------------------------------------------------------------------------
/test/fixtures/minify/foo.tsx:
--------------------------------------------------------------------------------
1 | export default class Foo {
2 | render() {
3 | return hello there!!!
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/minify/index.js:
--------------------------------------------------------------------------------
1 | import Foo from './foo'
2 | console.log(Foo)
3 |
--------------------------------------------------------------------------------
/test/fixtures/react-17-jsx-transform/index.tsx:
--------------------------------------------------------------------------------
1 | export function Foo() { return foo
}
2 |
--------------------------------------------------------------------------------
/test/fixtures/react-17-jsx-transform/tsconfig.compiled.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "jsxImportSource": "@compiled/react"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/react-17-jsx-transform/tsconfig.react-jsx.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/resolve-index/foo/index.tsx:
--------------------------------------------------------------------------------
1 | export default class Foo {
2 | render() {
3 | return hello there!!!
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/resolve-index/index.js:
--------------------------------------------------------------------------------
1 | import Foo from './foo'
2 | console.log(Foo)
3 |
--------------------------------------------------------------------------------
/test/fixtures/rollup-commonjs/bar.js:
--------------------------------------------------------------------------------
1 | exports.Bar = 'bar'
2 |
--------------------------------------------------------------------------------
/test/fixtures/rollup-commonjs/foo.js:
--------------------------------------------------------------------------------
1 | module.exports = 'foo'
2 |
--------------------------------------------------------------------------------
/test/fixtures/rollup-commonjs/index.js:
--------------------------------------------------------------------------------
1 | const Foo = require('./foo')
2 | const { Bar } = require('./bar')
3 | console.log(Foo, Bar)
4 |
--------------------------------------------------------------------------------
/test/fixtures/simple/bar.mjs:
--------------------------------------------------------------------------------
1 | const bar = 'baz'
2 | export default bar
3 |
--------------------------------------------------------------------------------
/test/fixtures/simple/foo.tsx:
--------------------------------------------------------------------------------
1 | export default class Foo {
2 | render() {
3 | return hello there!!!
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/simple/index.js:
--------------------------------------------------------------------------------
1 | import Foo from './foo'
2 | console.log(Foo)
3 | import bar from './bar'
4 | console.log(bar)
5 |
--------------------------------------------------------------------------------
/test/fixtures/standalone-minify/index.js:
--------------------------------------------------------------------------------
1 | console.log(10000);
2 | console.log('b' + 'c');
3 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-base-url-only-relative-issue-63/src/index.ts:
--------------------------------------------------------------------------------
1 | function test(s: string) {
2 | console.log(s)
3 | }
4 |
5 | test('hello swc!')
6 |
7 | export class Test {
8 | #es2022Feature = true
9 | }
10 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-base-url-only-relative-issue-63/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./src",
4 | "module": "ES2022",
5 | "target": "ES2017"
6 | },
7 | "include": ["./index.ts"],
8 | }
9 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-baseurl-paths/src/components/a.ts:
--------------------------------------------------------------------------------
1 | export const a = (input) => 'a' + input;
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-baseurl-paths/src/index.ts:
--------------------------------------------------------------------------------
1 | import { a } from '@/components/a'
2 | import { b } from '@/lib/b'
3 | console.log(a(b));
4 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-baseurl-paths/src/lib/b.ts:
--------------------------------------------------------------------------------
1 | export const b = 'b';
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-baseurl-paths/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "@/*": [
6 | "./src/*"
7 | ]
8 | },
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-custom-jsx-factory/index.tsx:
--------------------------------------------------------------------------------
1 | export const foo = foo
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-custom-jsx-factory/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsxFactory": "h"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-extends/index.jsx:
--------------------------------------------------------------------------------
1 | export const foo = foo
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-extends/jsconfig.custom.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.build.json",
3 | "compilerOptions": {}
4 | }
5 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-extends/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsxFactory": "custom"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-extends/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.build.json",
3 | "compilerOptions": {}
4 | }
5 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-full-path/foo/bar/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": { "jsxFactory": "hFoo" }
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-full-path/index.jsx:
--------------------------------------------------------------------------------
1 | export const foo = foo
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-full-path/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": { "jsxFactory": "hBar" }
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-jsconfig/index.tsx:
--------------------------------------------------------------------------------
1 | export const foo = <>foo
>
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-jsconfig/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsxFactory": "m",
4 | "jsxFragmentFactory": "React.Fragment"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-jsconfig/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | }
4 | }
5 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-paths/src/components/a.ts:
--------------------------------------------------------------------------------
1 | export const a = (input) => 'a' + input;
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-paths/src/index.ts:
--------------------------------------------------------------------------------
1 | import { a } from '@/components/a'
2 | import { b } from '@/lib/b'
3 | console.log(a(b));
4 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-paths/src/lib/b.ts:
--------------------------------------------------------------------------------
1 | export const b = 'b';
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-paths/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": [
5 | "./src/*"
6 | ]
7 | },
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-resolve-to-nearest-tsconfig/bar/index.tsx:
--------------------------------------------------------------------------------
1 | export const bar = bar
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-resolve-to-nearest-tsconfig/bar/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": { "jsxFactory": "hBar" }
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-resolve-to-nearest-tsconfig/foo/index.jsx:
--------------------------------------------------------------------------------
1 | export const foo = foo
2 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-resolve-to-nearest-tsconfig/foo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": { "jsxFactory": "hFoo" }
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-resolve-to-nearest-tsconfig/index.jsx:
--------------------------------------------------------------------------------
1 | import { foo } from './foo';
2 | import { bar } from './bar';
3 | export const baz = {foo}{bar}
4 |
--------------------------------------------------------------------------------
/test/fixtures/tsconfig-resolve-to-nearest-tsconfig/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": { "jsxFactory": "h" }
3 | }
4 |
--------------------------------------------------------------------------------
/test/index.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import fsp from 'node:fs/promises';
3 | import process from 'node:process';
4 |
5 | import { fdir as Fdir } from 'fdir';
6 |
7 | import { rollup as rollup2 } from 'rollup2';
8 | import { rollup as rollup3 } from 'rollup3';
9 | import { rollup as rollup4 } from 'rollup';
10 | import type { Plugin as RollupPlugin, ExternalOption, InputOption, RollupOutput } from 'rollup';
11 |
12 | import type { PluginOptions } from '../src';
13 | import { swc, minify, preserveUseDirective } from '../src';
14 |
15 | import json from '@rollup/plugin-json';
16 | import commonjs from '@rollup/plugin-commonjs';
17 | import { nodeResolve } from '@rollup/plugin-node-resolve';
18 |
19 | import type { JsMinifyOptions } from '@swc/core';
20 |
21 | import { create, destroy } from 'memdisk';
22 |
23 | import { sync as whichSync } from 'which';
24 | import { exec } from 'tinyexec';
25 | import { jestExpect as expect } from 'mocha-expect-snapshot';
26 | import 'mocha-expect-snapshot';
27 |
28 | function rollupInvriant(v: RollupOutput['output'][number] | undefined | null) {
29 | if (v == null) {
30 | throw new Error('Invariant failed');
31 | }
32 | if (!('code' in v)) {
33 | throw new Error('Non rollup output module found!');
34 | }
35 | return v;
36 | }
37 |
38 | async function build(rollupImpl: typeof rollup2 | typeof rollup3 | typeof rollup4,
39 | options?: PluginOptions,
40 | {
41 | input = './index.js',
42 | otherRollupPlugins = [],
43 | otherRollupPluginsAfterSwc = [],
44 | sourcemap = false,
45 | dir = '.',
46 | external
47 | }: {
48 | input?: InputOption,
49 | otherRollupPlugins?: RollupPlugin[],
50 | otherRollupPluginsAfterSwc?: RollupPlugin[],
51 | sourcemap?: boolean,
52 | dir?: string,
53 | external?: ExternalOption
54 | } = {}) {
55 | const build = await rollupImpl({
56 | input: (() => {
57 | if (typeof input === 'string') {
58 | return path.resolve(dir, input);
59 | }
60 | if (Array.isArray(input)) {
61 | return input.map((v) => path.resolve(dir, v));
62 | }
63 | return Object.entries(input).reduce>((acc, [key, value]) => {
64 | acc[key] = path.resolve(dir, value);
65 | return acc;
66 | }, {});
67 | })(),
68 | ...(
69 | rollupImpl === rollup2
70 | ? {}
71 | : { logLevel: 'silent' }
72 | ),
73 | plugins: [...otherRollupPlugins, swc(options), ...otherRollupPluginsAfterSwc] as any,
74 | external
75 | });
76 | const { output } = await build.generate({ format: 'esm', sourcemap });
77 | return output;
78 | }
79 |
80 | async function runMinify(rollupImpl: typeof rollup2 | typeof rollup3 | typeof rollup4,
81 | options: JsMinifyOptions,
82 | {
83 | input = './index.js',
84 | otherRollupPlugins = [],
85 | sourcemap = false,
86 | dir = '.'
87 | }) {
88 | const build = await rollupImpl({
89 | input: [...(Array.isArray(input) ? input : [input])].map((v) => path.resolve(dir, v)),
90 | plugins: [...otherRollupPlugins, minify(options)] as any
91 | });
92 | const { output } = await build.generate({ format: 'esm', sourcemap });
93 | return output;
94 | }
95 |
96 | function tests(
97 | rollupImpl: typeof rollup2 | typeof rollup3 | typeof rollup4,
98 | isolateDir: string,
99 | packageManager: string
100 | ) {
101 | const fixture = async (fixtureName: string) => {
102 | const fixtureDir = path.join(__dirname, 'fixtures', fixtureName);
103 | const testDir = path.join(isolateDir, 'rollup-plugin-swc', fixtureName);
104 |
105 | let requireInstall = false;
106 | const dirs = new Set();
107 | const files: Array<[from: string, to: string]> = [];
108 |
109 | const entries = await new Fdir()
110 | .withRelativePaths()
111 | .crawl(fixtureDir)
112 | .withPromise();
113 |
114 | for (const entry of entries) {
115 | const from = fixtureDir + path.sep + entry;
116 | const to = path.join(testDir, entry);
117 | const toDir = path.dirname(to);
118 |
119 | dirs.add(toDir);
120 | files.push([from, to]);
121 |
122 | if (entry === 'package.json') {
123 | // eslint-disable-next-line no-await-in-loop -- concurrent fs operation
124 | const pkg = JSON.parse(await fsp.readFile(from, 'utf8'));
125 | if (pkg.devDependencies || pkg.dependencies) {
126 | requireInstall = true;
127 | }
128 | }
129 | }
130 |
131 | await Promise.all(Array.from(dirs).map(dir => fsp.mkdir(dir, { recursive: true })));
132 | await Promise.all(files.map(([from, to]) => fsp.copyFile(from, to)));
133 |
134 | if (requireInstall) {
135 | await exec(packageManager, ['install'], { throwOnError: true, nodeOptions: { cwd: testDir } });
136 | }
137 |
138 | return testDir;
139 | };
140 |
141 | it('simple', async () => {
142 | const dir = await fixture('simple');
143 | const output = await build(rollupImpl, {}, { dir });
144 | expect(output[0]).toMatchSnapshot();
145 | });
146 |
147 | it('minify', async () => {
148 | const dir = await fixture('minify');
149 | const output = await build(rollupImpl, { minify: true, jsc: { target: 'es2022' } }, { dir });
150 | expect(output[0]).toMatchSnapshot();
151 | });
152 |
153 | it('standalone minify', async () => {
154 | const dir = await fixture('standalone-minify');
155 | const output = await runMinify(rollupImpl, {}, { dir });
156 | expect(output[0]).toMatchSnapshot();
157 | });
158 |
159 | it('resolve index.(x)', async () => {
160 | const dir = await fixture('resolve-index');
161 | const output = await build(rollupImpl, { jsc: { target: 'es2022' } }, { dir });
162 |
163 | expect(output[0]).toMatchSnapshot();
164 | });
165 |
166 | it('load json', async () => {
167 | const dir = await fixture('load-json');
168 |
169 | const output = await build(
170 | rollupImpl,
171 | {},
172 | { otherRollupPlugins: [json()], dir }
173 | );
174 |
175 | expect(output[0]).toMatchSnapshot();
176 | });
177 |
178 | it('support rollup virtual module (e.g. commonjs plugin)', async () => {
179 | const dir = await fixture('rollup-commonjs');
180 | const output = await build(
181 | rollupImpl,
182 | { jsc: { target: 'es2022' } },
183 | { otherRollupPlugins: [commonjs()], dir }
184 | );
185 | expect(output[0]).toMatchSnapshot();
186 | });
187 |
188 | it('use custom jsxFactory (h) from tsconfig', async () => {
189 | const dir = await fixture('tsconfig-custom-jsx-factory');
190 |
191 | const output = await build(rollupImpl, {}, { input: './index.tsx', dir });
192 | expect(output[0]).toMatchSnapshot();
193 | });
194 |
195 | it('use custom jsxFactory (h) from jsconfig.json', async () => {
196 | const dir = await fixture('jsconfig-custom-jsx-factory');
197 |
198 | const output = await build(rollupImpl, {}, { input: './index.tsx', dir });
199 | expect(output[0]).toMatchSnapshot();
200 | });
201 |
202 | it('react 17 jsx transform', async () => {
203 | const dir = await fixture('react-17-jsx-transform');
204 |
205 | expect((
206 | await build(rollupImpl, { tsconfig: 'tsconfig.react-jsx.json' }, { input: './index.tsx', dir, external: 'react/jsx-runtime' })
207 | )[0].code).toMatchSnapshot();
208 |
209 | expect((
210 | await build(rollupImpl, { tsconfig: 'tsconfig.compiled.json' }, { input: './index.tsx', dir, external: '@compiled/react/jsx-runtime' })
211 | )[0].code).toMatchSnapshot();
212 | });
213 |
214 | it('use tsconfig.json when tsconfig.json & jsconfig.json both exists', async () => {
215 | const dir = await fixture('tsconfig-jsconfig');
216 |
217 | const output = await build(rollupImpl, {}, { input: './index.tsx', dir });
218 | expect(output[0]).toMatchSnapshot();
219 | });
220 |
221 | it('use custom tsconfig.json', async () => {
222 | const dir = await fixture('custom-tsconfig');
223 |
224 | const output = await build(
225 | rollupImpl,
226 | { tsconfig: 'tsconfig.build.json' },
227 | { input: './index.jsx', dir }
228 | );
229 | expect(output[0]).toMatchSnapshot();
230 | });
231 |
232 | it('disable reading tsconfig.json', async () => {
233 | const dir = await fixture('disable-reading-tsconfig');
234 |
235 | const output = await build(
236 | rollupImpl,
237 | { tsconfig: false },
238 | { input: './index.jsx', dir }
239 | );
240 | expect(output[0]).toMatchSnapshot();
241 | });
242 |
243 | it('load jsx/tsx', async () => {
244 | const dir = await fixture('load-jsx-tsx');
245 |
246 | const output = await build(rollupImpl, { jsc: { target: 'es2022' } }, { dir });
247 | expect(output[0]).toMatchSnapshot();
248 | });
249 |
250 | it('tsconfig extends', async () => {
251 | const dir = await fixture('tsconfig-extends');
252 |
253 | expect((await build(
254 | rollupImpl,
255 | {},
256 | { input: './index.jsx', dir }
257 | ))[0].code).toMatchSnapshot();
258 |
259 | expect((await build(
260 | rollupImpl,
261 | { tsconfig: 'jsconfig.custom.json' },
262 | { input: './index.jsx', dir }
263 | ))[0].code).toMatchSnapshot();
264 | });
265 |
266 | it('tsconfig resolve to nearest tsconfig', async () => {
267 | const dir = await fixture('tsconfig-resolve-to-nearest-tsconfig');
268 |
269 | expect((await build(
270 | rollupImpl,
271 | {},
272 | { input: './index.jsx', dir }
273 | ))[0].code).toMatchSnapshot();
274 | });
275 |
276 | it('tsconfig - specify full path', async () => {
277 | const dir = await fixture('tsconfig-full-path');
278 |
279 | const tsconfigPath = path.resolve(dir, './foo/bar/tsconfig.json');
280 |
281 | expect((await build(
282 | rollupImpl,
283 | { tsconfig: tsconfigPath },
284 | { input: './index.jsx', dir }
285 | ))[0].code).toMatchSnapshot();
286 | });
287 |
288 | it('tsconfig - baseUrl & paths', async () => {
289 | const dir = await fixture('tsconfig-baseurl-paths');
290 |
291 | expect((await build(
292 | rollupImpl,
293 | {},
294 | { input: './src/index.ts', dir }
295 | ))[0].code).toMatchSnapshot();
296 | });
297 |
298 | it('tsconfig - paths', async () => {
299 | const dir = await fixture('tsconfig-paths');
300 |
301 | expect((await build(
302 | rollupImpl,
303 | {},
304 | { input: './src/index.ts', dir }
305 | ))[0].code).toMatchSnapshot();
306 | });
307 |
308 | it('target - include other files', async () => {
309 | const dir = await fixture('extensions');
310 |
311 | expect((await build(
312 | rollupImpl,
313 | { extensions: ['.ts', '.mts', '.cts'], tsconfig: false },
314 | { input: './index.mts', dir, otherRollupPlugins: [commonjs({ extensions: ['.cts'] })] }
315 | ))[0].code).toMatchSnapshot();
316 | });
317 |
318 | it('directive - include "use client"', async () => {
319 | const dir = await fixture('directive-include-use-client');
320 |
321 | expect((await build(
322 | rollupImpl,
323 | { tsconfig: false },
324 | { input: './index.tsx', dir, otherRollupPluginsAfterSwc: [preserveUseDirective()] }
325 | ))[0].code).toMatchSnapshot();
326 | });
327 |
328 | it('directive - merge "use client"', async () => {
329 | const dir = await fixture('directive-merge-use-client');
330 |
331 | expect((await build(
332 | rollupImpl,
333 | { tsconfig: false },
334 | { input: './index.tsx', dir, otherRollupPluginsAfterSwc: [preserveUseDirective()] }
335 | ))[0].code).toMatchSnapshot();
336 | });
337 |
338 | it('directive - only output "use client" / "use server" in the specfic entry', async () => {
339 | const dir = await fixture('directive-split-entry');
340 |
341 | const output = (await build(
342 | rollupImpl,
343 | { tsconfig: false },
344 | {
345 | input: {
346 | client: './client.ts',
347 | server: './server.ts'
348 | },
349 | dir, otherRollupPluginsAfterSwc: [preserveUseDirective()]
350 | }
351 | ));
352 |
353 | expect(rollupInvriant(output.find(i => i.fileName === 'client.js') as any).code).toMatchSnapshot();
354 | expect(rollupInvriant(output.find(i => i.fileName === 'server.js') as any).code).toMatchSnapshot();
355 | });
356 |
357 | it('issue 58 - eventemitter3', async () => {
358 | const dir = await fixture('issue-58');
359 |
360 | expect((await build(
361 | rollupImpl,
362 | { tsconfig: false },
363 | { input: './index.ts', dir, otherRollupPlugins: [nodeResolve(), commonjs()] }
364 | ))[0].code).toMatchSnapshot();
365 | });
366 |
367 | it('issue 63 - tsconfig baseUrl only + relative baseUrl', async () => {
368 | const dir = await fixture('tsconfig-base-url-only-relative-issue-63');
369 |
370 | expect((await build(
371 | rollupImpl,
372 | { tsconfig: false },
373 | { input: './src/index.ts', dir, otherRollupPlugins: [nodeResolve(), commonjs()] }
374 | ))[0].code).toMatchSnapshot();
375 | });
376 |
377 | it('detect decorator for typescript5', async () => {
378 | const dir = await fixture('decorators');
379 | expect((await build(
380 | rollupImpl,
381 | { tsconfig: false },
382 | { input: './index.ts', dir }
383 | ))[0].code).toMatchSnapshot();
384 | });
385 | it('detect legacy decorator for typescript5', async () => {
386 | const dir = await fixture('legacy-decorators');
387 | expect((await build(
388 | rollupImpl,
389 | {},
390 | { input: './index.ts', dir }
391 | ))[0].code).toMatchSnapshot();
392 | });
393 | }
394 |
395 | describe('rollup-plugin-swc3', () => {
396 | const packageManager = whichSync('pnpm', { nothrow: true }) || 'npm';
397 |
398 | const ramDiskPath = create.sync('rolluppluginswc3test', 64 * 1024 * 1024, { quiet: false });
399 |
400 | describe('swc (rollup 2)', () => {
401 | const isolateDir = path.join(ramDiskPath, 'rollup2');
402 | tests(rollup2, isolateDir, packageManager);
403 | });
404 |
405 | describe('swc (rollup 3)', () => {
406 | const isolateDir = path.join(ramDiskPath, 'rollup3');
407 | tests(rollup3, isolateDir, packageManager);
408 | });
409 |
410 | describe('swc (rollup 4)', () => {
411 | const isolateDir = path.join(ramDiskPath, 'rollup4');
412 | tests(rollup4, isolateDir, packageManager);
413 | });
414 |
415 | after((done) => {
416 | process.nextTick(() => {
417 | destroy.sync(ramDiskPath, { quiet: false });
418 | done();
419 | });
420 | });
421 | });
422 |
--------------------------------------------------------------------------------
/tools/build.ts:
--------------------------------------------------------------------------------
1 | import { builtinModules } from 'node:module';
2 | import process from 'node:process';
3 |
4 | import { rollup, VERSION } from 'rollup';
5 | import type { ExternalOption } from 'rollup';
6 |
7 | import { dts } from 'rollup-plugin-dts';
8 | import { swc, defineRollupSwcOption } from '../src/index';
9 |
10 | import pkg from '../package.json';
11 |
12 | const externalModules = new Set(Object.keys(pkg.dependencies).concat(Object.keys(pkg.peerDependencies)).concat(builtinModules));
13 | const external: ExternalOption = (id) => id.startsWith('node:') || externalModules.has(id);
14 |
15 | async function main() {
16 | async function build() {
17 | const bundle = await rollup({
18 | input: './src/index.ts',
19 | external,
20 | plugins: [swc(defineRollupSwcOption({
21 | jsc: {
22 | target: 'es2019',
23 | minify: {
24 | compress: true,
25 | mangle: true,
26 | module: true
27 | }
28 | },
29 | minify: true
30 | // The return type of swc() is `import('rollup2').Plugin` while the required type is `import('rollup3').Plugin`
31 | // Although they are identical, typescript is not happy about that
32 | // eslint-disable-next-line @typescript-eslint/no-explicit-any -- see above
33 | })) as any]
34 | });
35 |
36 | return Promise.all([
37 | bundle.write({ file: './dist/index.js', format: 'cjs', exports: 'named', interop: 'auto' }),
38 | bundle.write({ file: './dist/index.mjs', format: 'esm', exports: 'named', interop: 'auto' })
39 | ]);
40 | }
41 |
42 | console.log('rollup version', VERSION);
43 |
44 | return Promise.all([build(), createDtsFile()]).then(() => console.log('build finished!'));
45 | }
46 |
47 | async function createDtsFile() {
48 | const bundle = await rollup({
49 | input: './src/index.ts',
50 | external,
51 | plugins: [dts({
52 | respectExternal: true
53 | })]
54 | });
55 |
56 | return bundle.write({ file: './dist/index.d.ts' });
57 | }
58 |
59 | main().catch(err => {
60 | console.error(err);
61 | // eslint-disable-next-line sukka/unicorn/no-process-exit -- CLI tool
62 | process.exit(1);
63 | });
64 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "resolveJsonModule": true,
6 | "declaration": true,
7 | "outDir": "./dist",
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "strict": true,
11 | "strictNullChecks": true,
12 | "skipLibCheck": true
13 | },
14 | "include": ["src/**/*", "test/**/*", "tools/**/*"],
15 | "exclude": ["test/fixtures/**/*", "node_modules"]
16 | }
17 |
--------------------------------------------------------------------------------