自上篇我們將 CJS 結構的轉寫成 ESM 格式之後,我們發現在 ESM 內無法使用 CJS 模組。
更別說還有巢狀相依的狀況,那會有一大票套件必須轉換為 ESM 格式。
這種工作量光想就累了。
難道我們就沒有工具能夠簡單快速的解決相依問題嗎?
當然,那就是 rollup。
rollup 與 webpack 同屬 Javascript 的打包工具,主要的用途是將程式中用到的 npm 模組打包,將散佈在各模組中的功能集合起來。
而 rollup 比 webpack 更出色的地方是,在產出模組時 rollup 可以選擇 ESM,而目前 webpack 尚未支援 ESM 輸出。
就像 webpack 的設定檔一樣,基礎都會有
- Entry 接入點
- Output 輸出位置
不過 rollup 的設定檔,不像 webpack 遵循 CJS 格式,而是一開始就得使用 ESM 格式撰寫。
https://gist.github.com/a050a82f8d23f23520e9b40906524155
於是我們讓 rollup 讀取 src/index.js
並將打包結果輸出 ESM 格式到 esm/bundle.js
接著下 rollup --config
指令就會進行打包。
不過這時候 rollup 會顯示找不到 supports-color,因為還沒轉譯成 ESM。
官方文件指出,在 ESM 中使用 CJS 模組是不支援的。
於是我們需要 rollup 幫我們接管 CJS 模組的部分,並將相依模組與我們的主程式打包在一起。
除了 rollup 主程式以外,我們還需要以下 plugin:
- rollup-plugin-node-resolve:解析 npm 模組路徑
- rollup-plugin-commonjs:將 CJS 轉為 ESM
於是延續上面的設定,我們將 plugin 放入:
https://gist.github.com/2be9b806eb0ed28353845798ad1cd7b5
再執行一次 rollup --config
,哈!這不就包進來了嗎?
這時候我們就可以用個簡單的程式測試我們剛打包好的模組:
https://gist.github.com/d3649bcb38fc20fa91c58e4e634fa867
假設我們已經把打包好的 ESM 發佈到 NPM 上。
使用者在 webpack 上搭配 babel-loader 可能就會出現問題。
- 因為 第三方 loader 預設不會處理 mjs
- webpack 會對 ESM 填充一個 CJS 格式的 process polyfill 然後報錯
所以我們現階段只能斷然放棄 nodejs 直接使用 bundle.mjs,而是當作大家都會使用 CJS 或都會先進行打包後再執行。
所以只好
- 將輸出的 .mjs 改為 .js
- 為了相容性,也同時輸出 cjs 格式的打包
於是我們的設定就變成了
https://gist.github.com/7fa7b42369b216873183ba38e8ab8265
現在根據環境變數 BABEL_ENV
的不同我們就可以輸出不同版本的打包
BABEL_ENV=cjs rollup --config
則會產出 lib/bundle.js (CJS)
BABEL_ENV=esm rollup --config
則會產出 esm/bundle.js (ESM)
然後讓 package.json 的 main
欄位指向 lib/bundle.js
,並導引使用者若以 ESM 撰寫原始碼的話可以考慮載入 esm/bundle.js
。
如果我們把 debug-es
模組接入點 index.js 打開來看
https://gist.github.com/ef1691a867c9d7e6d47b9178b69caaa1
我們打從一開始就載入了兩種實例(node 或 browser),但最終只會有其中一種實例導出。
為何不就把 browser 跟 node 也獨立導出呢?
於是我們最後把 rollup 的設定改成了:
https://gist.github.com/3b2488d3b7a7b7c77ae0180a69b58786
我們將同一個套件打包成三個分支
- index.js: 自動判斷實例
- browser.js: 瀏覽器用實例
- node.js: nodejs 用實例
再加上 esm, cjs 格式,其實總計有六個打包檔案。
這樣使用者只要載入
debug-es/lib/browser
debug-es/lib/node
debug-es/esm/browser
debug-es/esm/node
就可以直接選擇需要的實例。
其實 ESM 上仍有不少前端、後端利用上有歧異或是不完善的地方。
但不管你使用 CJS 還是 ESM 撰寫模組,最後還是希望可以輸出 CJS 模組就好。
因為現在不管是對 ESM 的管理還是 .mjs 處理問題,都還在過度中的陣痛期。
如果你仍執意要多種格式都釋出的話,記得把打包結果放進測試專案的 node_modules 內,使用 webpack 打包看看能不能正常運作。