今天這篇文章就只是流水帳地閒聊我當時試著打包side project時的心路歷程。算是拋個堪用的磚頭,如果觸及到這篇散文的讀者剛好有興趣,也可以到下文提到的開源庫,一起研究更好的方案。(我個人是挺期待能否乾脆用Deno建置GAS專案)
昨天(Day09)分享了我的esbuild打包配置方案。然而,儘管GAS專案也是用JavaScript寫,由於esbuild畢竟是一個服務網頁開發的工具,比較正規的做法應該是去詳讀官方文件並寫個插件,但我這裡用了比較隨意粗暴的做法,反正它暫時works fine😎
就我對於esbuild粗淺的認識,當前理想上format
就是選esm
,如果要相容於legacy專案,才依瀏覽器或Node.js端選用iife
或cjs
。
已知GAS環境不支援ES modules,於是直覺上先嘗試用iife
,結果被打包成:
(() => {
function onOpen() {
createMenu();
}
})();
這導致了試算表在開啟時,GAS環境call不到onOpen
。
卡住了。當下我就覺得就不要自己瞎摸索,我應該要相信不是只有我會想試著用esbuild在GAS專案上。於是我找到了很多文章[^1]推薦的esbuild-gas-plugin插件。
這個esbuild插件受到gas-webpack-plugin啟發,這給我更多信心,顯然我遇到的問題,長期以來已經有一套解決方案並被實作成package。
在我查閱到esbuild插件的當下,作者英明(ひであき)さん剛好三個月內有做更新,還有人開issue提醒peer dependency的版本有已知漏洞與workaround,顯然是有受到一定人數使用並協作的插件。[^2]
遺憾的是,我當下實際使用後,並不work。雖然我相信應該是我哪裡配置錯誤,畢竟其他人都能成功使用;又或者我根本沒確認清楚這個插件是否服務我的場景。不過如同上週(Day03)聊到的,GAS環境已經實作globalThis
標準,插件的let global = this
是舊的解題方案。於是我就選擇不繼續探索這個插件與其相關的解法。
好的,繞了一圈,對於我自己當下的問題沒有直接幫助。[^3]
讓我回到問題點:simple triggers。前端專案通常入口點是main.js
,並把UI作為元件單位。既然GAS專案的simple triggers在打包時會是個問題,那就把它們作為入口點,而非其它ui.js
。
entryPoints: ["src/triggers.js"]
雖然決定了GAS專案的入口點,對我原本僵硬的腦迴路來說算是個大突破,但依然沒有解決掉iife
的問題。那這邊並沒有什麼縝密的思考,僅僅就是無腦trial and error,選擇了剩下的選項:
format: "cjs"
接下是對我而言最大的問題:我對於treeShaking
的實作細節一無所知。GAS專案尷尬的是,simple triggers並不會像前端專案被引用,就算把它們集中於入口文檔,並關閉treeShaking
:
treeShaking: false
悲傷的是,就結果來說,當在使用SpreadsheetApp
這個GAS特有全域變數來建立自定義選單時,打包後仍然會被esbuild移除handlers。
我目前比較取巧採用以下的workaroud:
import { pullFromCalendar } from "../logic/pull";
import { pushToCalendar } from "../logic/push";
(globalThis as any).pullFromCalendar = pullFromCalendar;
(globalThis as any).pushToCalendar = pushToCalendar;
export function createMenu(): void {
const ui = SpreadsheetApp.getUi();
ui.createMenu("Sync")
.addItem("pull from calendar", "pullFromCalendar")
.addItem("push to calendar", "pushToCalendar")
.addToUi();
}
很醜、有any
,但works fine。
總之,如此一來,我就成功把文檔拆開、又主動合併回去。雖然繞了幾圈,但至少我可以不用擔心GAS環境無法完全預料的「合併邏輯」,又能使用相對熟悉的文檔架構,讓專案可以繼續擴展。
是的,今天這篇就只是個流水帳,昨天分享的配置方案的背後並沒有任何solid的原因,僅在對esbuild不夠了解的情況下,拼湊出暫時沒有bug的打包配置。
撰文時實測了一下,把它format
設為esm
也是一樣的output,畢竟neutral
預設就是esm
,而且我暴力地把treeShaking
關了。不過秉持著能動就先不要改,可能等專案出現預期外的錯誤時我才會再調整打包設定。
當然,這個配置是為了我個人長期使用的專案能更舒適地擴展,如果回歸到處理行政流程自動化等工作場景,如前天(Day08)提到的,我自己是覺得沒有必要做到這麼繁重的配置。
[^1]: 例如REALITY株式会社的《Google Apps Scriptの開発体験を向上させる Now in REALITY Tech #114》。
[^2]: 英明さん甚至在README.md提到了Deno。身為剛入Deno教的信徒,當下親切值+100。
[^3]: 作為side project,不同於work project,相對沒有時間壓力,至少有探索到新的社群,稍微看一下別人在做什麼,我覺得還是挺有趣的。