從 CommonJS 到 ES module(下)

轉譯相依模組並使用 rollup 打包

Miau Lightouch
4 min readAug 17, 2018

續前篇:從 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 格式撰寫。

指示 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 放入:

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

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

在 Webpack 碰上問題

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

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

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

到這裡,若我們繼續堅持使用 .mjs 作為附檔名,那在 babel-loader 上就會出現問題。

如果不堅持使用 .mjs 作為附檔名,那麼 nodejs 將無法直接讀取 ESM 模組。

於是我決定暫時向 webpack 靠攏,畢竟 nodejs 對 ESM 的支援尚未完善:

  1. 將輸出的打包從 .mjs 改為 .js
  2. 為了更好的相容性,也同時輸出 cjs 格式的打包

於是我們的設定就變成了

現在可以根據環境變數 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 打開來看

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

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

我們將同一個模組打包成三個分支

  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 打包看看能不能正常運作。

--

--