IE11対応のnpmライブラリのビルドをvite+swcで爆速化した話
普段職場で作るアプリのジャンルが業務系ということで、西暦と和暦を相互に変換したくなることが多く、ちょうどよいライブラリが見つからなかったので「warera」というライブラリを自作しました。
IE11に対応することが必須であったため、従来はwebpack+babelでビルドしUMD形式で出力する方法を取っていたのですが、昨今のビルドツールブーム(?)に乗じて、vite+swcでビルドするように変更しましたので、その具体的な方法について書き残しておきます。
IE11対応でライブラリを作ることなんてもうないかもしれません(そうであってほしい)が、参考になれば幸いです。
TLDR
以下のような状態のリポジトリがあったとして、
以下のように改修すればOKです。
詳細な手順
ライブラリ
必要なライブラリは以下の通りです。
- vite
- @swc/core
- unplugin-swc
- core-js
- (@swc/jest)
$ npm i -D vite @swc/core unplugin-swc core-js@3 @swc/jest
viteの設定
vite.config.js
を新規に作成します。
import path from "path";
import swc from "unplugin-swc";
import { defineConfig } from "vite";
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, "src/main.ts"),
name: "Warera",
formats: ["umd"],
fileName: (format) => `warera.${format}.js`,
},
minify: "terser",
sourcemap: true,
},
plugins: [swc.vite()],
});
viteというとReactだったりVue.jsアプリケーションのビルドに用いるイメージが強いですが、「Library Mode」というものがあり、素のTS/JSで書かれたnpmライブラリのビルドにも使用することができます。
おおむね公式ドキュメントに記載の内容に沿っていますが、formats
を明示的に指定することで、UMD形式のみが出力されるようにしています。(デフォルトではES ModulesとUMD両方出力されます)
ES Modules形式も一緒に出力するようにしてもよいのですが、swcでトランスパイルしている関係上、core-jsからのポリフィルがES Modules形式の出力ファイルにも含まれてしまいます。[1]
今回はIE11対応を主眼に置いていたので、UMDのみでも差し支えないだろうということで明示的に指定することにしました。
また、minify
オプションでterser
を明示的に指定しています。
デフォルトではesbuild
となっており、esbuildが持っているminify機能をそのまま使います。しかし、今回はunplugin-swc
を通じてビルドにswcを使うため、esbuildを使おうとしても使えませんので、terser
とします。
注意点としては、以下にあるように、「formats
がes
の時は適用されない」ことです。これは配列で複数指定していても同様で、デフォルトの['es', 'umd']
であれば、UMD形式のファイルは圧縮され、ES Module形式のファイルは圧縮されません。
npmライブラリを作るため、ソースマップ出力はtrueとしています。
また、plugins
にswc.vite()
を追加します。
swcの設定
.swcrc
を新規に作成します。
{
"jsc": {
"parser": {
"syntax": "typescript"
}
},
"env": {
"mode": "usage",
"coreJs": 3
}
}
TypeScriptを読み込ませるために、jsc.parser.syntax
にtypescript
を指定し、ポリフィルを注入するためにenv
を指定します。
env
は本来対応させるブラウザの指定に用いられますが、.browserslistrc
の自動読み込みに対応しているため、今回はenv.targets
の指定を省略し、別途.browserslistrc
を用意します。
env.mode
はbabelでもおなじみの設定項目ですが、「usage
モードは現在Babelほど効率的ではありません」とのことです。
browserslistの設定
.browserslistrc
を新規に作成します。
defaults
IE 11
maintained node versions
今回は上記のようにしました。対応範囲によって変更してください。
TypeScriptの設定
tsconfig.json
にも若干の変更を加えます。
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"lib": ["esnext"],
"moduleResolution": "node",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"isolatedModules": true,
"skipLibCheck": true,
"emitDeclarationOnly": true, <-- ココ
"declaration": true,
"declarationMap": true, <-- ココ
"declarationDir": "./types" <-- ココ
},
"include": ["./src"]
}
webpackでは、TSの型情報も一緒に出力してくれていましたが、swcはトランスパイル後のJSファイルのみを出力するため、別途tscを使って型情報を出力する必要があります。そのため、tsconfig.jsonでも「指定フォルダに型情報のみを出力するようにする」必要があります。
具体的には矢印で指し示した3か所を指定する必要があります。
npmスクリプトの設定
最後に、package.json
でビルドスクリプト等を変更します。
{
"name": "warera",
"version": "0.2.4",
"description": "",
"main": "./dist/warera.umd.js", <-- ココ
"exports": {
".": "./dist/warera.umd.js" <-- ココ
},
"types": "./types/main.d.ts", <-- ココ
"files": [
"/dist", <-- ココ
"/types" <-- ココ
],
"scripts": {
"build": "vite build", <-- ココ
"build:prod": "tsc && vite build", <-- ココ
"watch": "npm run build -- -w", <-- ココ
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
… 以下略
}
(おまけ) jestの設定
どうせswcを使うなら、ついでにjestの実行も爆速化してしまいましょう。
... 以上略
transform: {
"^.+\\.(ts|tsx)$": "@swc/jest",
},
... 以下略
@swc/jest
をインストール後、transform
を書き換えるだけです。めっちゃ簡単。
不要なファイルを消す
webpack、babel関連のファイルは不要なので消してしまいましょう。
- webpack.config.js
- .babelrc
また、ライブラリも消してしまいます。
- @babel/core
- @babel/preset-env
- babel-loader
- ts-loader
- tslib
- webpack
- webpack-cli
- (ts-jest)
結果
具体的な計測は今のところしていませんが、明らかに差がわかるレベルでビルド時間が短縮されました。体感では半分以下になっています。
それよりも、複雑で煩雑なwebpackの設定ファイルからオサラバできたことがかなりのメリットに感じます。IE11対応という比較的めんどくさいことをしなければならない場合であっても、vite+swcならばこの程度の分量の設定ファイルで済むというのは、なかなか良いのではないでしょうか。
もし、みなさんも今後npmライブラリを作る機会があれば、vite+swcを試してみてはいかがでしょうか。IE11対応も行けますよ。[2]
参考にしたページ
Discussion