如何使用 pkg 將 Node.js 專案打包成可攜式執行檔
- Leo
- 技術宅 ( tech geek)
- 2024年3月18日
目錄
為了在沒有 Node.js 的環境中執行專案,我們可以使用 pkg 將專案打包成可攜式執行檔。 雖然 pkg 已經停止維護了,但是它仍然是一個很好用的工具,然而使用上有不少需要注意的地方,這篇文章將會介紹如何使用 pkg 將 Node.js 專案打包成可攜式執行檔。
轉成 CommonJS
並合併成單一檔案
由於 pkg
在 Node.js
中對 ESM
的支援度不是很完整,為了避免自己的專案及 node_module
中的專案有使用到 ESM
造成的錯誤,我們可以將專案透過 webpack
搭配 Babel
轉換成 commonjs
並合併成一個檔案來避免這個問題。
- 首先安裝
webpack
及Babel
相關套件
npm install -D webpack webpack-cli copy-webpack-plugin @babel/cli @babel/core @babel/node @babel/preset-env babel-loader
- 在
root
創建webpack.config.js
檔案
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
mode: "development",
// mode: 'production',
entry: "./src/index.mjs",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
resolve: {
extensions: [".js", ".mjs"],
},
target: "node",
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: "path/to/your/assets/file.txt", to: "file.txt" },
{ from: "path/to/your/assets/folder/", to: "folder/" },
],
}),
],
};
- 在
root
創建.babelrc
檔案
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
},
"modules": "commonjs"
}
]
]
}
接著執行 webpack
後,將會在 dist
資料夾中看到 index.js
以及一些複製的檔案。
查看 index.js
會發現已經將自己的以及 node_module
中的檔案轉換成 commonjs
並合併成一個檔案。
npx webpack
使用 pkg
打包檔案
完成上述步驟後,就可以使用 pkg
打包檔案了。
- 首先安裝
pkg
npm install -D pkg
- 在
root
創建pkg.config.json
檔案
會建議不要在 package.json
中設定 pkg
的設定,因為這樣會讓 package.json
變得不容易管理,所以建議將 pkg
的設定獨立出來。
設定 assets
以及 targets
,assets
是要複製到執行檔中的檔案,targets
是要打包的平台 (需要注意在 arm64
的平台上可以打包 x64
的執行檔,但是在 x64
的平台上無法打包 arm64
的執行檔)。
並且 node
的版本需要設定成 18
,在 Windows
上可以使用 nvm
切換版本,在 macOS
及 Linux
上建議使用 asdf
切換版本。
{
"pkg": {
"assets": ["dist/file.txt", "dist/folder/**/*"],
"targets": [
"node18-win-x64",
"node18-macos-x64",
"node18-linux-x64",
"node18-win-arm64",
"node18-macos-arm64",
"node18-linux-arm64"
],
"outputPath": "dist"
}
}
- 使用
pkg
打包檔案
npx pkg dist/index.js --config pkg.config.json
打包完成後,將會在 dist
資料夾中看到打包好的執行檔。
補充
雖然 pkg
的指令很簡單,但是在打包的過程中,還是不免會碰到一些相容性及版本打包的問題,但是只要透過上述第一步驟的轉換,就可以避免大部分的問題。
另外,若是在專案中有使用 __dirname
或 __filename
這兩個變數,需要注意根據 README
在打包後這兩個變數會變成 /snapshot
,所以會建議使用下面的方法來取代 __dirname
或 __filename
等變數。
import path from "path";
export function getAppDir() {
if (process.pkg && process.pkg.entrypoint) {
return path.dirname(process.pkg.entrypoint);
}
return process.cwd();
}