山特▶
wol
如何在 yarn 2+ 直接使用 node 執行建置/編譯完成後的 js 檔案
一些雷跟背後原理解釋
晚點可能整理上部落格
山特▶
【背景】
我想把我 nest js 的 code 搬到 Docker 上,俗稱 containerize
但我在 build image 的過程中遇到問題:我想直接將編譯完的 dist/ 複製到執行環境,並直接透過 node $entryFile.js來執行
但卻會遇到 module not found
https://images.plurk.com/5RZIJ05x1fA4wLUyaiHhyx.png
山特▶
(從 Docker 截的,我昨天噴錯 cmd 已經關掉了撈不出 log,並沒有邪教到 cmd 底色是白色謝謝)
山特▶
【首先】
我第一反應就是「我忘記搬 node_modules 了,所有 module source 都在 node_modules 裡,只要搬過去就可以解決問題。」
山特▶
但是我一看 repo 發現不對,我用的是 yarn 2+,yarn 2+ 是沒有 node_modules 的
.yarn 下也都只有 .zip 的壓縮包
那麼問題來了:yarn 到底是怎麼拿到 source code 並執行檔案的?
山特▶
抱著這個疑問,我從新打開 yarn 官網認真研究了一下 yarn 2+ 背後的原理
Plug'n'Play | Yarn
山特▶
其他內容先按下不表,簡而言之:
yarn 2+ 靠的是用 .pnp.cjs 這個 file 記錄所有 dependency 名稱及其對應的位置
舉例來說,下圖這個 entry
https://images.plurk.com/7vX9V29MIIcfQmVCipWq8H.png
記錄了 babel 的 core module 放在 @babel-core-${version}.zip 下的哪個位置
山特▶
於是 build/runtime 的時候,node 就可以根據這個 entry 去正確的拿到城市中需要的 code
山特▶
【其次】
Okay, makes sense
於是我將 .yarn/.pnp.cjs 也一起搬到執行環境裡
(後來發現 package.json, yarn.lock 也需要搬進去)
山特▶
下一個問題來了:直接 node $entryfile.js 依舊不能執行
還是會跳 module not found 的錯誤
https://images.plurk.com/4TDvDWeVNOOszFb0lYtIkr.png
山特▶
其實我執行前就在想 node 居然這麼有智慧,知道怎麼進 zip 找檔案嗎
山特▶
於是我繼續翻 yarn 官網,找到另一個 yarn 指令
yarn node (ref)
是以 yarn 的環境執行 node,並在執行前將所有 pnp 的設定都處理好
山特▶
【最後】
最後這個錯有點令人哭笑不得
總之我的開發環境主要是用 esmodule,所以我在 root package.json 中有設定 "type": "module"
山特▶
但是我的 tsconfig 中編譯目標是 commonjs
所以直接 node 執行就會跳 export not defined
https://images.plurk.com/pUjumSWcioV1nVPGZMOjG.png
山特▶
就,哎呀XD
山特▶
最後的最後我就先暫時在執行前把 package.json 中的 type definition 拿掉,然後就可以順利執行了👏
https://images.plurk.com/KovYbxNycwznNxKF035Wt.png
山特▶
總之 containerization 的問題到這邊應該可以解決,但我需要先去把整個 repo 的環境都調一下😂
山特▶
【補充】
剛剛走了個彎路是,我在看到 export not defined 的時候第一反應是改 tsconf 設定,把編譯目標改成 esmodule
具體改法是在 compilerOptions 中設定
1. module: ESNext (或任何你想用的版本,ref
2. moduleResolution: bundler (或任何你想用的形式,ref
兩個都要設,缺一不可
山特▶
【Takeaway】
要在 PnP 環境下直接執行 transpiled code 所需要的最少 source file
1. packages.json
2. .pnp.cjs
3. yarn.lock
4. .yarn/cached/
5. .yarn/unplugged/
載入新的回覆