使用 pkg 打包 node.js 程序为可执行程序exe

360 阅读1分钟

记录一次使用pkgnode工程打包成.exe的实践过程。

1. 下载

npm install -g pkg

2. 配置package.json

pkg可以通过在package.json 中的配置项来对代码进行打包

{
    "type": "module",
    "name": "server",
    "version": "1.0.0",
    "scripts": {
        "start": "node index.js",
        "pkg": "node build/build.js",
    },
    "bin": "index.js",
    "pkg": {
        "name": "my-server",
        "targets": [
            "node18-win-x64"
        ]
    },
    "output": "dist"
}
  • bin 是入口文件路径
  • name 最终输出的可执行程序名称
  • targets 指定平台和 Node 版本,这里使用 node18
  • output 是输出可执行文件的目录

3. 当引入的node_modules中包含 .node

我的项目中使用了better-sqlite3,打包时不会将better_sqlite3.node打包到文件中,此时需要手动配置,将该文件复制到打包后的文件中,这里我自己编译了一份脚本,将静态资源进行复制。

import { exec } from 'pkg'
import path from 'path';
import { mkdirSync } from "fs";
import { resolve, dirname } from 'path';
import fg from 'fast-glob';
import { createRequire } from 'module';

// 动态导入 fs-extra (兼容 CJS)
const require = createRequire(import.meta.url);
const fsExtra = require('fs-extra');

const build = async () => {
  try {
    // 先移除package文件夹中的内容
    await fsExtra.remove('package');
    // 获取输入文件
    const entryFile = path.resolve(process.cwd(), './dist/index.cjs');
    await exec([
      entryFile,
      '--targets', 'node18-win-x64',
      '--output', 'package/my-service.exe'
      // 如有其它 .node 原生模块文件,可继续添加 --assets 路径
    ]);

    await copyStaticFile();

    await fsExtra.remove('dist');

    console.log('✅ 打包完成: package/my-service.exe');
  } catch (err) {
    console.error('❌ 打包失败:', err);
  }
}

const copyStaticFile = async () => {
  // 配置要拷贝的静态资源
  const patterns = [
    'db/*',
    'public/**/*',
    'better_sqlite3.node',
    '.env',
  ];
  const files = await fg(patterns, { dot: true, onlyFiles: false, followSymbolicLinks: true, ignore: [] });

  files.forEach(file => {
    const srcPath = resolve(file);
    // 复制到pkg打包目录下
    const pkgPath = resolve('package', file);

    // 复制到pkg打包目录下
    fsExtra.copySync(srcPath, pkgPath);
    console.log(`[tsup] Copied: ${file} -> dist/${file}`);
  })
}

build();