"use strict";
|
|
const { execSync } = require("child_process");
|
const fs = require("fs");
|
const path = require("path");
|
|
const ROOT = path.resolve(__dirname, "..");
|
const DIST = path.join(ROOT, "dist");
|
const PKG_CACHE = path.join(ROOT, ".pkg-cache");
|
|
const TARGETS = {
|
win: { id: "node18-win-x64", ext: ".exe", os: "windows" },
|
linux:{ id: "node18-linux-x64", ext: "", os: "linux" },
|
};
|
|
const args = process.argv.slice(2);
|
const winOnly = args.includes("--win");
|
const linuxOnly = args.includes("--linux");
|
const targets = winOnly ? ["win"]
|
: linuxOnly ? ["linux"]
|
: ["win", "linux"];
|
|
// ── 1. 清理 ──
|
console.log("[1/5] 清理输出目录...");
|
fs.mkdirSync(DIST, { recursive: true });
|
for (const key of targets) {
|
const t = TARGETS[key];
|
const outDir = path.join(DIST, `jms-connection-service-${t.os}`);
|
try {
|
fs.rmSync(outDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 500 });
|
} catch {
|
console.log(` ⚠ ${t.os} 目录清理失败(可能被占用),尝试增量覆盖...`);
|
}
|
}
|
|
// ── 2. 检查 pkg ──
|
console.log("[2/5] 检查 pkg...");
|
const pkgBin = path.join(ROOT, "node_modules", ".bin", "pkg");
|
if (!fs.existsSync(pkgBin) && !fs.existsSync(pkgBin + ".cmd")) {
|
console.log(" pkg 未安装,正在安装...");
|
execSync("npm install --save-dev pkg", { cwd: ROOT, stdio: "inherit" });
|
}
|
|
// ── 3. pkg 编译 ──
|
console.log("[3/5] 编译可执行文件...");
|
const builtFiles = [];
|
|
for (const key of targets) {
|
const t = TARGETS[key];
|
const baseName = `jms-connection-service-${t.os}`;
|
const outDir = path.join(DIST, `jms-connection-service-${t.os}`);
|
fs.mkdirSync(outDir, { recursive: true });
|
|
const outPath = path.join(outDir, baseName);
|
console.log(` 编译 ${t.id} → ${path.relative(ROOT, outPath)}${t.ext}`);
|
|
try {
|
execSync(
|
`npx pkg . --targets ${t.id} --output "${outPath}"`,
|
{ cwd: ROOT, stdio: "inherit", env: { ...process.env, PKG_CACHE_PATH: PKG_CACHE } }
|
);
|
builtFiles.push({ key, outDir, baseName, ext: t.ext, os: t.os });
|
} catch (err) {
|
console.error(` ✗ ${t.id} 编译失败: ${err.message}`);
|
process.exit(1);
|
}
|
}
|
|
// ── 4. 组装发布包 ──
|
console.log("[4/5] 组装发布包...");
|
|
for (const item of builtFiles) {
|
const { outDir } = item;
|
|
// 复制配置模板
|
const configSrc = path.join(ROOT, "config.json");
|
const configDst = path.join(outDir, "config.json");
|
fs.copyFileSync(configSrc, configDst);
|
|
// 创建 logs 目录
|
fs.mkdirSync(path.join(outDir, "logs"), { recursive: true });
|
|
// 写入使用说明
|
const readme = [
|
"JMS 透析机 TCP 联机服务",
|
"========================",
|
"",
|
"快速开始:",
|
" 1. 编辑 config.json,配置设备 IP、端口、序列号",
|
" 2. 命令行启动:",
|
` Windows: .\\jms-connection-service-windows.exe`,
|
` Linux: ./jms-connection-service-linux`,
|
" 3. 浏览器访问 http://localhost:3100 查看监控大屏",
|
"",
|
"目录结构:",
|
` jms-connection-service-${item.os}/`,
|
` ├── jms-connection-service-${item.os}${item.ext} # 主程序`,
|
" ├── config.json # 配置文件(可编辑)",
|
" └── logs/ # 日志目录(自动创建)",
|
"",
|
"注册为系统服务(推荐生产环境):",
|
` Windows: sc create JMSConnection binPath= "完整路径\\jms-connection-service-windows.exe" start= auto`,
|
" Linux: 参考 DEPLOY.md 中的 PM2 / systemd 章节",
|
"",
|
"详细文档见 DEPLOY.md",
|
].join("\n");
|
fs.writeFileSync(path.join(outDir, "README.txt"), readme, "utf-8");
|
|
console.log(` ${item.os}: ${path.relative(ROOT, outDir)}/ 组装完成`);
|
}
|
|
// ── 5. 打包归档 ──
|
console.log("[5/5] 打包归档...");
|
|
for (const item of builtFiles) {
|
const { outDir, baseName, os } = item;
|
const dirName = path.basename(outDir);
|
|
if (os === "windows") {
|
const zipPath = path.join(DIST, `${baseName}.zip`);
|
try {
|
// 清除旧 zip
|
try { fs.unlinkSync(zipPath); } catch {}
|
execSync(
|
`powershell -Command "Compress-Archive -Path '${outDir}' -DestinationPath '${zipPath}' -Force"`,
|
{ stdio: "ignore" }
|
);
|
console.log(` ${path.basename(zipPath)} (${formatSize(fs.statSync(zipPath).size)})`);
|
} catch {
|
console.log(` ⚠ PowerShell 不可用,跳过 zip (文件在 ${path.relative(ROOT, outDir)}/)`);
|
}
|
} else {
|
const tgzPath = path.join(DIST, `${baseName}.tar.gz`);
|
try {
|
try { fs.unlinkSync(tgzPath); } catch {}
|
// 用相对路径避免 Windows 盘符问题
|
execSync(
|
`tar -czf "${tgzPath}" -C "${DIST}" "${dirName}"`,
|
{ stdio: "ignore", cwd: DIST }
|
);
|
console.log(` ${path.basename(tgzPath)} (${formatSize(fs.statSync(tgzPath).size)})`);
|
} catch {
|
console.log(` ⚠ tar 不可用,跳过归档 (文件在 ${path.relative(ROOT, outDir)}/)`);
|
}
|
}
|
}
|
|
// ── 汇总 ──
|
console.log("\n======== 打包完成 ========");
|
console.log(`输出目录: ${path.relative(ROOT, DIST)}/`);
|
const distFiles = fs.readdirSync(DIST);
|
for (const f of distFiles) {
|
const full = path.join(DIST, f);
|
const stat = fs.statSync(full);
|
if (stat.isDirectory()) {
|
const contents = fs.readdirSync(full);
|
const exe = contents.find(x => x.endsWith(".exe") || x.startsWith("jms-connection-service-"));
|
console.log(` ${f}/ (${exe ? exe + "、" : ""}config.json、logs/、README.txt)`);
|
} else {
|
console.log(` ${f} (${formatSize(stat.size)})`);
|
}
|
}
|
console.log("\n下一步: 进入 dist/ 对应目录,编辑 config.json 后启动程序验证。");
|
|
// ── helpers ──
|
function formatSize(bytes) {
|
if (bytes >= 1048576) return (bytes / 1048576).toFixed(1) + " MB";
|
if (bytes >= 1024) return (bytes / 1024).toFixed(1) + " KB";
|
return bytes + " B";
|
}
|