Skip to content

Instantly share code, notes, and snippets.

@miaulightouch
Created August 17, 2018 15:29
Show Gist options
  • Save miaulightouch/b59f995d5a08faf42bd2cf2fe55ec179 to your computer and use it in GitHub Desktop.
Save miaulightouch/b59f995d5a08faf42bd2cf2fe55ec179 to your computer and use it in GitHub Desktop.

從 CommonJS 到 ES module(下):使用 rollup 打包發佈

續前篇:從 CommonJS 到 ES module(上)

自上篇我們將 CJS 結構的轉寫成 ESM 格式之後,我們發現在 ESM 內無法使用 CJS 模組。

更別說還有巢狀相依的狀況,那會有一大票套件必須轉換為 ESM 格式。

這種工作量光想就累了。

難道我們就沒有工具能夠簡單快速的解決相依問題嗎?

當然,那就是 rollup


rollup 與 webpack 同屬 Javascript 的打包工具,主要的用途是將程式中用到的 npm 模組打包,將散佈在各模組中的功能集合起來。

而 rollup 比 webpack 更出色的地方是,在產出模組時 rollup 可以選擇 ESM,而目前 webpack 尚未支援 ESM 輸出

基礎設定

就像 webpack 的設定檔一樣,基礎都會有

  1. Entry 接入點
  2. Output 輸出位置

不過 rollup 的設定檔,不像 webpack 遵循 CJS 格式,而是一開始就得使用 ESM 格式撰寫。

https://gist.github.com/a050a82f8d23f23520e9b40906524155

於是我們讓 rollup 讀取 src/index.js 並將打包結果輸出 ESM 格式到 esm/bundle.js

接著下 rollup --config 指令就會進行打包。

不過這時候 rollup 會顯示找不到 supports-color,因為還沒轉譯成 ESM。

解決 CJS 相依問題

官方文件指出,在 ESM 中使用 CJS 模組是不支援的。

於是我們需要 rollup 幫我們接管 CJS 模組的部分,並將相依模組與我們的主程式打包在一起。

除了 rollup 主程式以外,我們還需要以下 plugin:

  1. rollup-plugin-node-resolve:解析 npm 模組路徑
  2. rollup-plugin-commonjs:將 CJS 轉為 ESM

於是延續上面的設定,我們將 plugin 放入:

https://gist.github.com/2be9b806eb0ed28353845798ad1cd7b5

再執行一次 rollup --config,哈!這不就包進來了嗎?

這時候我們就可以用個簡單的程式測試我們剛打包好的模組:

https://gist.github.com/d3649bcb38fc20fa91c58e4e634fa867

卻在 Webpack 碰上問題

假設我們已經把打包好的 ESM 發佈到 NPM 上。

使用者在 webpack 上搭配 babel-loader 可能就會出現問題。

  1. 因為 第三方 loader 預設不會處理 mjs
  2. webpack 會對 ESM 填充一個 CJS 格式的 process polyfill 然後報錯

所以我們現階段只能斷然放棄 nodejs 直接使用 bundle.mjs,而是當作大家都會使用 CJS 或都會先進行打包後再執行。

所以只好

  1. 將輸出的 .mjs 改為 .js
  2. 為了相容性,也同時輸出 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

補充:Tree-shaking

如果我們把 debug-es 模組接入點 index.js 打開來看

https://gist.github.com/ef1691a867c9d7e6d47b9178b69caaa1

我們打從一開始就載入了兩種實例(node 或 browser),但最終只會有其中一種實例導出。

為何不就把 browser 跟 node 也獨立導出呢?

於是我們最後把 rollup 的設定改成了:

https://gist.github.com/3b2488d3b7a7b7c77ae0180a69b58786

我們將同一個套件打包成三個分支

  1. index.js: 自動判斷實例
  2. browser.js: 瀏覽器用實例
  3. 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 打包看看能不能正常運作。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment