@adtkcn/hb-cli
Version:
用于uniapp的打包工具,可切换环境变量、可多配置,切换证书,更改版本,wifi调试
528 lines (494 loc) • 14.3 kB
JavaScript
const path = require("path");
const os = require("os");
const fs = require("fs");
const cp = require("child_process");
const JSON5 = require("json5");
var iconv = require("iconv-lite");
const config = require("../config/config.js");
const { deepAssign } = require("./merge.js");
var workDir = process.cwd();
/**
* 打开wifi调试
*/
function OpenWifiDebug() {
return new Promise((resolve, reject) => {
try {
var ls = cp.spawn(config.HBuilderAdb, ["tcpip", "5555"], {});
ls.on("exit", function (code) {
if (code === 0) {
console.log("OpenWifiDebug 成功");
// 等待时间
setTimeout(() => {
resolve(0);
}, 2000);
} else {
console.log("OpenWifiDebug 失败" + code);
reject(code);
}
});
} catch (error) {
console.log("OpenWifiDebug 状态", error);
reject(1);
}
});
}
/**
* 连接到wifi
* @param {string} ip
* @returns
*/
function ConnectPhoneWithWifi(ip) {
return new Promise((resolve, reject) => {
try {
var ls = cp.spawn(config.HBuilderAdb, ["connect", ip], {});
ls.on("exit", function (code) {
if (code === 0) {
console.log("ConnectPhoneWithWifi 完成不一定成功", code);
// 等待时间
setTimeout(() => {
resolve(0);
}, 2000);
} else {
console.log("ConnectPhoneWithWifi 失败", code);
reject(code);
}
});
} catch (error) {
console.log("ConnectPhoneWithWifi 异常", error);
reject(1);
}
});
}
/**
* 打开HBuilder编辑器
* @returns {Promise<Number>} 状态码:0成功
*/
function OpenHBuilder() {
return new Promise((resolve, reject) => {
try {
var ls = cp.spawn(config.HBuilderCli, ["open"], {});
ls.on("exit", function (code) {
if (code === 0) {
console.log("打开HBuilder编辑器成功");
// 给hbuilder加载时间
setTimeout(() => {
resolve(0);
}, 4000);
} else {
console.log("打开HBuilder编辑器失败");
reject(1);
}
});
} catch (error) {
console.log("打开HBuilder编辑器错误", error);
reject(1);
}
});
}
/**
* 获取本机ip
*
* @return {string} 局域网ip地址
*/
function getLocalIP() {
const osType = os.type(); //系统类型
const netInfo = os.networkInterfaces(); //网络信息
let ip = "";
// console.log(osType, netInfo);
if (osType === "Windows_NT") {
for (let dev in netInfo) {
// console.log(dev);
//win7的网络信息中显示为本地连接,win10显示为以太网
if (dev === "本地连接" || dev === "以太网" || dev == "WLAN") {
for (let j = 0; j < netInfo[dev].length; j++) {
if (netInfo[dev][j].family === "IPv4") {
ip = netInfo[dev][j].address;
break;
}
}
}
}
} else if (osType === "Linux") {
ip = netInfo.eth0[0].address;
}
return ip;
}
/**
*打开默认浏览器
* @param {string} url
*/
function openDefaultBrowser(url) {
console.log("预览地址:", url);
console.log("使用 ctrl+c 关闭终端");
switch (process.platform) {
case "darwin":
cp.exec("open " + url);
break;
case "win32":
cp.exec("start " + url);
break;
default:
cp.exec(`xdg-open "${url}"`);
}
}
/**
* 读取工作目录配置文件
* @param {string} FileName
* @returns {Promise<any>}
*/
function readConfig(FileName) {
return new Promise((resolve, reject) => {
fs.readFile(FileName, function (err, data) {
if (err) {
reject("读取配置文件错误,检查是否存在" + FileName);
return;
}
var d = String(data);
try {
var c = JSON5.parse(d);
resolve(c);
} catch (error) {
console.log(error);
reject(error);
}
});
});
}
/**
* 更改配置文件
* @param {string} ConfigFilePath
* @param {string|object} Config
* @returns {Promise<string>}
*/
function WriteConfig(ConfigFilePath, Config = "") {
return new Promise((resolve, reject) => {
fs.mkdir(
path.dirname(ConfigFilePath),
{
recursive: true,
},
function (err) {
if (err) {
console.log(err);
return reject(err);
}
if (typeof Config == "string") {
fs.writeFileSync(ConfigFilePath, Config);
} else if (typeof Config == "object") {
fs.writeFileSync(
ConfigFilePath,
JSON.stringify(Config, undefined, "\t")
);
}
resolve(ConfigFilePath);
}
);
});
}
/**
* 合并HBuilderConfig配置文件
* @param {object} packConfig
* @param {object} packConfig.android
* @param {string} packConfig.android.certfile
* @param {object} packConfig.ios
* @param {string} packConfig.ios.profile
* @param {string} packConfig.ios.certfile
* @param {object} info
* @return {object}
*/
function MergeHBuilderConfig(packConfig, info = {}) {
var newConfig = deepAssign({}, packConfig, info);
newConfig.android.certfile = newConfig.android.certfile
? path.join(workDir, newConfig.android.certfile)
: "";
newConfig.ios.profile = newConfig.ios.profile
? path.join(workDir, newConfig.ios.profile)
: "";
newConfig.ios.certfile = newConfig.ios.certfile
? path.join(workDir, newConfig.ios.certfile)
: "";
return newConfig;
}
/**
*
* @param {object} ManifestConfig
* @param {object} info
* @returns {object}
*/
function MergeManifestConfig(ManifestConfig = {}, info = {}) {
var newConfig = deepAssign({}, ManifestConfig, info);
// var str = JSON.stringify(newConfig, undefined, "\t");
return newConfig;
}
/**
* 运行cli
* @param {Array} cli
* @param {Function} callback
*/
function RunCli(cli, callback) {
console.log(config.HBuilderCli, cli.join(" "));
// var pack = cp.spawn(config.HBuilderCli, [
// "pack",
// "--config",
// HBuilderConfigFileTemp,
// ]);
var pack = cp.spawn(config.HBuilderCli, cli);
pack.stdout.on("data", (data) => {
var str = iconv.decode(Buffer.from(data, "binary"), "GBK");
callback && callback(-2, str);
});
pack.stderr.on("data", (data) => {
var str = iconv.decode(Buffer.from(data, "binary"), "GBK");
callback && callback(-3, str);
});
pack.on("exit", function (code) {
callback && callback(code);
});
}
/**
*
* @param {boolean} isCustom 是不是自定义基座
* @returns
*/
async function buildApp(isCustom) {
return new Promise((resolve, reject) => {
try {
var apps = [];
RunCli(
["pack", "--config", config.ConfigFileTemp],
function (code, data) {
// code==-1 自定义错误code,-2是正常数据,-3是错误数据, 其他是进程code
if (code == 0) {
if (isCustom === false) {
if (apps.length) {
console.log("安装包", apps);
} else {
console.log("未获取到安装包");
}
}
resolve(apps);
} else if (code == -1 && data) {
//自定义异常
console.log(data);
reject(data);
} else if (code == -2 && data) {
//进程正常返回数据
console.log(data);
if (
data.indexOf("打包成功") != -1 &&
data.indexOf("安装包位置:") != -1
) {
// 打包成功 安装包位置:E:/xiangheng/通知订阅/消息订阅/unpackage/release/apk/__UNI__ECA51B4__20220426171608.apk
var appPath = data.split("安装包位置:")[1];
appPath = appPath.split(" ")[0];
if (!appPath) {
reject("打包的路径获取出错");
return;
}
var newAppPath = appPath
.replace(/\//g, "\\")
.replace(/\n/g, "")
.replace(/(\s*$)/g, "");
apps.push(newAppPath);
}
if (data.indexOf("iOS Appstore 下载地址:") != -1) {
// ios下载地址
var appUrl = GetUrl(data);
if (appUrl) {
// 下载文件
apps.push(appUrl);
}
}
} else if (code == -3 && data) {
//进程异常返回数据
console.log(data); // 追加一行
reject(data);
}
}
);
} catch (error) {
console.log("error", error);
reject(error);
}
});
}
/**
* 导出wgt包
* @param {object} HBuilderConfig
* @param {string} HBuilderConfig.project 项目名称
* @param {boolean} HBuilderConfig.isconfusion 是否混淆
* @returns {Promise<Array>}
*/
function buildWgtCli(HBuilderConfig) {
return new Promise((resolve, reject) => {
var apps = [];
RunCli(
[
"publish",
"--platform",
"APP",
"--type",
"wgt",
"--project",
HBuilderConfig.project,
"--name",
`${HBuilderConfig.project}.wgt`,
"--confuse",HBuilderConfig.isconfusion||false,//混淆
],
function (code, data) {
if (code == 0) {
resolve(apps);
} else if (code == -1 && data) {
//自定义异常
console.log(data);
reject(data);
} else if (code == -2 && data) {
//进程正常返回数据
console.log(data);
if (data.indexOf(`导出成功,路径为:`) != -1) {
var appPath = data.split("导出成功,路径为:")[1];
if (!appPath) {
reject("打包的路径获取出错");
return;
}
//从appPath中截取以.wgt结尾
appPath = appPath.split(".wgt")[0] + ".wgt";
apps.push(appPath);
}
} else if (code == -3 && data) {
//进程异常返回数据
console.log(data); // 追加一行
reject(data);
}
}
);
});
}
/**
* 导出appResource
* @param {object} HBuilderConfig
* @param {string} HBuilderConfig.project
* @returns {Promise<Array>}
*/
function buildAppResourceCli(HBuilderConfig) {
return new Promise((resolve, reject) => {
var apps = [];
RunCli(
[
"publish",
"--platform",
"APP",
"--type",
"appResource",
"--project",
HBuilderConfig.project,
],
function (code, data) {
if (code == 0) {
resolve(apps);
} else if (code == -1 && data) {
//自定义异常
console.log(data);
reject(data);
} else if (code == -2 && data) {
//进程正常返回数据
console.log(data);
if (data.indexOf(`导出成功,路径为:`) != -1) {
let appPath = data.split("导出成功,路径为:")[1];
if (!appPath) {
reject("打包的路径获取出错");
return;
}
//从appPath中截取以resources结尾
appPath = appPath.split("resources")[0] + "resources";
apps.push(appPath);
}
} else if (code == -3 && data) {
//进程异常返回数据
console.log(data); // 追加一行
reject(data);
}
}
);
});
}
/**
* 从字符串中提取首个URL地址
* @param {string} str - 需要解析的原始字符串
* @returns {string|null} 返回找到的第一个URL地址,未找到返回null
*/
function GetUrl(str) {
const reg =
/(https?|http|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g;
const strValue = str.match(reg);
if (strValue && strValue.length > 0) {
return strValue[0];
}
return null;
}
/**
* 打开指定目录:nodejs封装方法打开指定目录,兼容win,mac,linux
* @param {string} filePath 文件路径
*/
function openDirectory(filePath) {
return new Promise((resolve, reject) => {
const absolutePath = path.resolve(filePath);
// 检查路径是否存在
// if (!fs.existsSync(absolutePath)) {
// console.error("路径不存在:", absolutePath);
// return;
// }
// 获取路径信息(文件 or 文件夹)
const isDirectory = fs.statSync(absolutePath).isDirectory();
let command;
if (process.platform === "win32") {
// Windows
if (isDirectory) {
command = `start ${absolutePath}`; // 直接打开文件夹
} else {
const parentDirectory = path.dirname(filePath);
command = `start ${parentDirectory}`; // 打开文件夹并
}
} else if (process.platform === "darwin") {
// macOS
if (isDirectory) {
command = `open "${absolutePath}"`; // 直接打开文件夹
} else {
command = `open -R "${absolutePath}"`; // 打开文件夹并定位到文件
}
} else {
// Linux (使用 xdg-open)
command = `xdg-open "${
isDirectory ? absolutePath : path.dirname(absolutePath)
}"`;
}
// 执行命令
cp.exec(command, (error) => {
if (error) {
console.error("Failed to open location:", error);
reject(error);
return;
}
resolve(0);
});
});
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
module.exports = {
openDefaultBrowser,
getLocalIP,
readConfig,
MergeHBuilderConfig,
MergeManifestConfig,
WriteConfig,
buildApp,
buildWgtCli,
buildAppResourceCli,
OpenHBuilder,
OpenWifiDebug,
ConnectPhoneWithWifi,
GetUrl,
openDirectory,sleep
};