├── .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 | --------------------------------------------------------------------------------