weapp-ide-cli 5.2.4 → 5.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/{cli-CfovXw-V.js → cli-D7gUmbTG.js} +1068 -210
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +358 -9
- package/dist/index.js +2 -2
- package/package.json +3 -3
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { $ as readCustomConfig, B as getConfiguredLocale, C as configureLocaleFromArgv, G as operatingSystemName, I as bootstrapWechatDevtoolsSettings, J as createAutoBootstrapDevtoolsConfig, K as colors, Q as overwriteCustomConfig, R as resolveCliPath, T as validateLocaleOption, V as resolveDevtoolsAutomationDefaults, W as isOperatingSystemSupported, X as createCustomConfig, Y as createAutoTrustProjectConfig, Z as createLocaleConfig, a as navigateBack, c as pageStack, d as remote, et as removeCustomConfigKey, f as scrollTo, g as tap, i as input, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, nt as defaultCustomConfigFilePath, o as navigateTo, p as switchTab, q as logger_default, r as currentPage, rt as resolvePath, s as pageData, t as audit, u as redirectTo, w as i18nText, y as connectMiniProgram } from "./commands-XD_wemcg.js";
|
|
1
|
+
import { $ as readCustomConfig, B as getConfiguredLocale, C as configureLocaleFromArgv, G as operatingSystemName, I as bootstrapWechatDevtoolsSettings, J as createAutoBootstrapDevtoolsConfig, K as colors, L as detectWechatDevtoolsServicePort, Q as overwriteCustomConfig, R as resolveCliPath, S as withMiniProgram, T as validateLocaleOption, V as resolveDevtoolsAutomationDefaults, W as isOperatingSystemSupported, X as createCustomConfig, Y as createAutoTrustProjectConfig, Z as createLocaleConfig, a as navigateBack, c as pageStack, d as remote, et as removeCustomConfigKey, f as scrollTo, g as tap, i as input, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, nt as defaultCustomConfigFilePath, o as navigateTo, p as switchTab, q as logger_default, r as currentPage, rt as resolvePath, s as pageData, t as audit, u as redirectTo, w as i18nText, y as connectMiniProgram } from "./commands-XD_wemcg.js";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
import { fs as fs$1 } from "@weapp-core/shared/fs";
|
|
4
5
|
import process, { stdin, stdout } from "node:process";
|
|
5
6
|
import { PNG } from "pngjs";
|
|
6
7
|
import pixelmatch from "pixelmatch";
|
|
7
8
|
import { createInterface } from "node:readline/promises";
|
|
8
|
-
import { inspect } from "node:util";
|
|
9
9
|
import { emitKeypressEvents } from "node:readline";
|
|
10
|
+
import { inspect } from "node:util";
|
|
10
11
|
import { cac } from "cac";
|
|
11
12
|
//#region src/cli/automator-argv.ts
|
|
13
|
+
function parsePositiveInt(raw) {
|
|
14
|
+
const value = Number.parseInt(raw, 10);
|
|
15
|
+
if (!Number.isFinite(value) || value <= 0) return;
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
function takesValue(optionName) {
|
|
19
|
+
return optionName === "-p" || optionName === "--project" || optionName === "-t" || optionName === "--timeout" || optionName === "-o" || optionName === "--output" || optionName === "--page" || optionName === "--login-retry" || optionName === "--login-retry-timeout" || optionName === "--lang" || optionName === "--platform" || optionName === "--qr-output" || optionName === "-r" || optionName === "--result-output" || optionName === "--info-output" || optionName === "-i";
|
|
20
|
+
}
|
|
12
21
|
/**
|
|
13
22
|
* @description 解析 automator 命令通用参数与位置参数。
|
|
14
23
|
*/
|
|
@@ -65,19 +74,44 @@ function parseAutomatorArgs(argv) {
|
|
|
65
74
|
};
|
|
66
75
|
}
|
|
67
76
|
/**
|
|
68
|
-
* @description
|
|
77
|
+
* @description 读取选项值,支持多个别名,以及 --option value 与 --option=value。
|
|
69
78
|
*/
|
|
70
|
-
function readOptionValue(argv,
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
function readOptionValue(argv, ...optionNames) {
|
|
80
|
+
for (const optionName of optionNames) {
|
|
81
|
+
const optionWithEqual = `${optionName}=`;
|
|
82
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
83
|
+
const token = argv[index];
|
|
84
|
+
if (!token) continue;
|
|
85
|
+
if (token === optionName) {
|
|
86
|
+
const value = argv[index + 1];
|
|
87
|
+
if (typeof value !== "string") return;
|
|
88
|
+
return value.trim();
|
|
89
|
+
}
|
|
90
|
+
if (token.startsWith(optionWithEqual)) return token.slice(optionWithEqual.length).trim();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* @description 读取布尔选项,支持裸 flag、--flag=true/false 与 --flag true/false。
|
|
96
|
+
*/
|
|
97
|
+
function readBooleanOption(argv, ...optionNames) {
|
|
98
|
+
for (const optionName of optionNames) {
|
|
99
|
+
const optionWithEqual = `${optionName}=`;
|
|
100
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
101
|
+
const token = argv[index];
|
|
102
|
+
if (!token) continue;
|
|
103
|
+
if (token === optionName) {
|
|
104
|
+
const nextToken = argv[index + 1];
|
|
105
|
+
if (nextToken === "true") return true;
|
|
106
|
+
if (nextToken === "false") return false;
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
if (token.startsWith(optionWithEqual)) {
|
|
110
|
+
const rawValue = token.slice(optionWithEqual.length).trim().toLowerCase();
|
|
111
|
+
if (rawValue === "true") return true;
|
|
112
|
+
if (rawValue === "false") return false;
|
|
113
|
+
}
|
|
79
114
|
}
|
|
80
|
-
if (token.startsWith(optionWithEqual)) return token.slice(optionWithEqual.length).trim();
|
|
81
115
|
}
|
|
82
116
|
}
|
|
83
117
|
/**
|
|
@@ -99,14 +133,6 @@ function removeOption(argv, optionName) {
|
|
|
99
133
|
}
|
|
100
134
|
return nextArgv;
|
|
101
135
|
}
|
|
102
|
-
function parsePositiveInt(raw) {
|
|
103
|
-
const value = Number.parseInt(raw, 10);
|
|
104
|
-
if (!Number.isFinite(value) || value <= 0) return;
|
|
105
|
-
return value;
|
|
106
|
-
}
|
|
107
|
-
function takesValue(optionName) {
|
|
108
|
-
return optionName === "-p" || optionName === "--project" || optionName === "-t" || optionName === "--timeout" || optionName === "-o" || optionName === "--output" || optionName === "--page" || optionName === "--login-retry" || optionName === "--login-retry-timeout" || optionName === "--lang" || optionName === "--platform" || optionName === "--qr-output" || optionName === "-r" || optionName === "--result-output" || optionName === "--info-output" || optionName === "-i";
|
|
109
|
-
}
|
|
110
136
|
//#endregion
|
|
111
137
|
//#region src/cli/imageDiff.ts
|
|
112
138
|
function readPng(buffer, label) {
|
|
@@ -783,39 +809,186 @@ function isWeappIdeTopLevelCommand(command) {
|
|
|
783
809
|
return Boolean(command && WEAPP_IDE_TOP_LEVEL_COMMAND_SET.has(command));
|
|
784
810
|
}
|
|
785
811
|
//#endregion
|
|
812
|
+
//#region src/cli/inputCoordinator.ts
|
|
813
|
+
const sharedSessions = /* @__PURE__ */ new Set();
|
|
814
|
+
const exclusiveKeypressStack = [];
|
|
815
|
+
const EXCLUSIVE_KEYPRESS_CARRYOVER_GUARD_MS = 500;
|
|
816
|
+
let initialized = false;
|
|
817
|
+
let rawModeEnabled = false;
|
|
818
|
+
let dataListenerAttached = false;
|
|
819
|
+
let keypressListenerAttached = false;
|
|
820
|
+
let lastExclusiveResolvedKeypress;
|
|
821
|
+
function hasInteractiveStdin() {
|
|
822
|
+
return Boolean(process.stdin?.isTTY);
|
|
823
|
+
}
|
|
824
|
+
function canSetRawMode() {
|
|
825
|
+
return typeof process.stdin?.setRawMode === "function";
|
|
826
|
+
}
|
|
827
|
+
function updateTerminalState() {
|
|
828
|
+
const shouldKeepInputActive = Array.from(sharedSessions).some((session) => !session.closed && !session.suspended) || exclusiveKeypressStack.length > 0;
|
|
829
|
+
if (canSetRawMode() && rawModeEnabled !== shouldKeepInputActive) {
|
|
830
|
+
process.stdin.setRawMode(shouldKeepInputActive);
|
|
831
|
+
rawModeEnabled = shouldKeepInputActive;
|
|
832
|
+
}
|
|
833
|
+
if (shouldKeepInputActive) {
|
|
834
|
+
process.stdin.resume();
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
process.stdin.pause();
|
|
838
|
+
}
|
|
839
|
+
function handleData(chunk) {
|
|
840
|
+
if (exclusiveKeypressStack.length > 0) return;
|
|
841
|
+
for (const session of sharedSessions) {
|
|
842
|
+
if (session.closed || session.suspended || !session.options.onData) continue;
|
|
843
|
+
session.options.onData(chunk);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function handleKeypress(str, key) {
|
|
847
|
+
const activeExclusive = exclusiveKeypressStack.at(-1);
|
|
848
|
+
if (activeExclusive) {
|
|
849
|
+
const now = Date.now();
|
|
850
|
+
const signature = `${key?.ctrl ? "ctrl+" : ""}${key?.name ?? str}`;
|
|
851
|
+
if (lastExclusiveResolvedKeypress && lastExclusiveResolvedKeypress.signature === signature && now - lastExclusiveResolvedKeypress.timestamp <= EXCLUSIVE_KEYPRESS_CARRYOVER_GUARD_MS) {
|
|
852
|
+
lastExclusiveResolvedKeypress.timestamp = now;
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (now < activeExclusive.ignoreUntil) return;
|
|
856
|
+
const nextValue = activeExclusive.onKeypress(str, key);
|
|
857
|
+
if (nextValue !== void 0) {
|
|
858
|
+
clearTimeout(activeExclusive.timeout);
|
|
859
|
+
exclusiveKeypressStack.pop();
|
|
860
|
+
lastExclusiveResolvedKeypress = {
|
|
861
|
+
signature,
|
|
862
|
+
timestamp: now
|
|
863
|
+
};
|
|
864
|
+
updateTerminalState();
|
|
865
|
+
activeExclusive.resolve(nextValue);
|
|
866
|
+
}
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
for (const session of sharedSessions) {
|
|
870
|
+
if (session.closed || session.suspended || !session.options.onKeypress) continue;
|
|
871
|
+
session.options.onKeypress(str, key);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function ensureCoordinatorInitialized() {
|
|
875
|
+
if (initialized || !hasInteractiveStdin()) return;
|
|
876
|
+
emitKeypressEvents(process.stdin);
|
|
877
|
+
if (!dataListenerAttached) {
|
|
878
|
+
process.stdin.on("data", handleData);
|
|
879
|
+
dataListenerAttached = true;
|
|
880
|
+
}
|
|
881
|
+
if (!keypressListenerAttached) {
|
|
882
|
+
process.stdin.on("keypress", handleKeypress);
|
|
883
|
+
keypressListenerAttached = true;
|
|
884
|
+
}
|
|
885
|
+
initialized = true;
|
|
886
|
+
}
|
|
887
|
+
function createSharedInputSession(options) {
|
|
888
|
+
if (!hasInteractiveStdin()) return;
|
|
889
|
+
ensureCoordinatorInitialized();
|
|
890
|
+
const session = {
|
|
891
|
+
closed: false,
|
|
892
|
+
options,
|
|
893
|
+
suspended: false
|
|
894
|
+
};
|
|
895
|
+
sharedSessions.add(session);
|
|
896
|
+
updateTerminalState();
|
|
897
|
+
return {
|
|
898
|
+
close() {
|
|
899
|
+
if (session.closed) return;
|
|
900
|
+
session.closed = true;
|
|
901
|
+
sharedSessions.delete(session);
|
|
902
|
+
updateTerminalState();
|
|
903
|
+
},
|
|
904
|
+
resume() {
|
|
905
|
+
if (session.closed) return;
|
|
906
|
+
session.suspended = false;
|
|
907
|
+
updateTerminalState();
|
|
908
|
+
},
|
|
909
|
+
suspend() {
|
|
910
|
+
if (session.closed) return;
|
|
911
|
+
session.suspended = true;
|
|
912
|
+
updateTerminalState();
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
async function waitForExclusiveKeypress(options) {
|
|
917
|
+
if (!hasInteractiveStdin()) return "timeout";
|
|
918
|
+
ensureCoordinatorInitialized();
|
|
919
|
+
return await new Promise((resolve) => {
|
|
920
|
+
const normalizedTimeoutMs = Number.isFinite(options.timeoutMs) && options.timeoutMs && options.timeoutMs > 0 ? options.timeoutMs : 3e4;
|
|
921
|
+
const normalizedIgnoreInitialMs = Number.isFinite(options.ignoreInitialMs) && options.ignoreInitialMs && options.ignoreInitialMs > 0 ? options.ignoreInitialMs : 0;
|
|
922
|
+
const record = {
|
|
923
|
+
ignoreUntil: Date.now() + normalizedIgnoreInitialMs,
|
|
924
|
+
onKeypress: options.onKeypress,
|
|
925
|
+
resolve,
|
|
926
|
+
timeout: setTimeout(() => {
|
|
927
|
+
const index = exclusiveKeypressStack.lastIndexOf(record);
|
|
928
|
+
if (index >= 0) exclusiveKeypressStack.splice(index, 1);
|
|
929
|
+
updateTerminalState();
|
|
930
|
+
resolve("timeout");
|
|
931
|
+
}, normalizedTimeoutMs)
|
|
932
|
+
};
|
|
933
|
+
exclusiveKeypressStack.push(record);
|
|
934
|
+
updateTerminalState();
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
async function runWithSuspendedSharedInput(runner) {
|
|
938
|
+
const previousStates = /* @__PURE__ */ new Map();
|
|
939
|
+
for (const session of sharedSessions) {
|
|
940
|
+
if (session.closed) continue;
|
|
941
|
+
previousStates.set(session, session.suspended);
|
|
942
|
+
session.suspended = true;
|
|
943
|
+
}
|
|
944
|
+
if (canSetRawMode() && rawModeEnabled) {
|
|
945
|
+
process.stdin.setRawMode(false);
|
|
946
|
+
rawModeEnabled = false;
|
|
947
|
+
}
|
|
948
|
+
process.stdin.resume();
|
|
949
|
+
try {
|
|
950
|
+
return await runner();
|
|
951
|
+
} finally {
|
|
952
|
+
for (const [session, suspended] of previousStates) if (!session.closed) session.suspended = suspended;
|
|
953
|
+
updateTerminalState();
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
//#endregion
|
|
786
957
|
//#region src/cli/prompt.ts
|
|
787
958
|
/**
|
|
788
959
|
* @description 交互式提示并保存 CLI 路径
|
|
789
960
|
*/
|
|
790
961
|
async function promptForCliPath() {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
logger_default.info(`请设置 ${colors.bold("微信web开发者工具 CLI")} 的路径`);
|
|
797
|
-
logger_default.info("提示:命令行工具默认所在位置:");
|
|
798
|
-
logger_default.info(`- MacOS: ${colors.green("<安装路径>/Contents/MacOS/cli")}`);
|
|
799
|
-
logger_default.info(`- Windows: ${colors.green("<安装路径>/cli.bat")}`);
|
|
800
|
-
logger_default.info(`- Linux: ${colors.green("<安装路径>/files/bin/bin/wechat-devtools-cli")}`);
|
|
801
|
-
const cliPath = (await rl.question("请输入微信web开发者工具 CLI 路径:")).trim();
|
|
802
|
-
if (!cliPath) {
|
|
803
|
-
logger_default.error("路径不能为空,已取消本次配置。");
|
|
804
|
-
return null;
|
|
805
|
-
}
|
|
962
|
+
return await runWithSuspendedSharedInput(async () => {
|
|
963
|
+
const rl = createInterface({
|
|
964
|
+
input: stdin,
|
|
965
|
+
output: stdout
|
|
966
|
+
});
|
|
806
967
|
try {
|
|
807
|
-
|
|
808
|
-
logger_default.info(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
|
|
968
|
+
logger_default.info(`请设置 ${colors.bold("微信web开发者工具 CLI")} 的路径`);
|
|
969
|
+
logger_default.info("提示:命令行工具默认所在位置:");
|
|
970
|
+
logger_default.info(`- MacOS: ${colors.green("<安装路径>/Contents/MacOS/cli")}`);
|
|
971
|
+
logger_default.info(`- Windows: ${colors.green("<安装路径>/cli.bat")}`);
|
|
972
|
+
logger_default.info(`- Linux: ${colors.green("<安装路径>/files/bin/bin/wechat-devtools-cli")}`);
|
|
973
|
+
const cliPath = (await rl.question("请输入微信web开发者工具 CLI 路径:")).trim();
|
|
974
|
+
if (!cliPath) {
|
|
975
|
+
logger_default.error("路径不能为空,已取消本次配置。");
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
const normalizedPath = await createCustomConfig({ cliPath });
|
|
980
|
+
logger_default.info(`全局配置存储位置:${colors.green(defaultCustomConfigFilePath)}`);
|
|
981
|
+
if (!await fs$1.pathExists(normalizedPath)) logger_default.warn("在当前路径未找到微信web开发者命令行工具,请确认路径是否正确。");
|
|
982
|
+
return normalizedPath;
|
|
983
|
+
} catch (error) {
|
|
984
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
985
|
+
logger_default.error(`保存配置失败:${reason}`);
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
} finally {
|
|
989
|
+
rl.close();
|
|
815
990
|
}
|
|
816
|
-
}
|
|
817
|
-
rl.close();
|
|
818
|
-
}
|
|
991
|
+
});
|
|
819
992
|
}
|
|
820
993
|
//#endregion
|
|
821
994
|
//#region src/cli/config-command.ts
|
|
@@ -960,6 +1133,173 @@ async function handleConfigCommand(argv) {
|
|
|
960
1133
|
throw new Error(i18nText("支持的 config 子命令:lang | set-lang | show | get | set | unset | doctor | import | export", "Supported config subcommands: lang | set-lang | show | get | set | unset | doctor | import | export"));
|
|
961
1134
|
}
|
|
962
1135
|
//#endregion
|
|
1136
|
+
//#region src/cli/http.ts
|
|
1137
|
+
const DEFAULT_WECHAT_DEVTOOLS_HTTP_PORT = 9420;
|
|
1138
|
+
const ENGINE_BUILD_NOT_START = "NOT_START";
|
|
1139
|
+
const ENGINE_BUILD_OPEN_PROJECT = "OPEN_PROJECT";
|
|
1140
|
+
const ENGINE_BUILD_BUILDING = "BUILDING";
|
|
1141
|
+
const ENGINE_BUILD_END = "END";
|
|
1142
|
+
const ENGINE_BUILD_ERROR = "ERROR";
|
|
1143
|
+
function createWechatDevtoolsHttpError(message, code) {
|
|
1144
|
+
const error = new Error(message);
|
|
1145
|
+
error.code = code;
|
|
1146
|
+
return error;
|
|
1147
|
+
}
|
|
1148
|
+
async function resolveWechatDevtoolsHttpPort(port) {
|
|
1149
|
+
if (typeof port === "number" && Number.isInteger(port) && port > 0) return port;
|
|
1150
|
+
const detected = await detectWechatDevtoolsServicePort();
|
|
1151
|
+
if (detected.servicePortEnabled === false) throw createWechatDevtoolsHttpError("WECHAT_DEVTOOLS_SERVICE_PORT_DISABLED", "WECHAT_DEVTOOLS_SERVICE_PORT_DISABLED");
|
|
1152
|
+
return detected.servicePort ?? DEFAULT_WECHAT_DEVTOOLS_HTTP_PORT;
|
|
1153
|
+
}
|
|
1154
|
+
function createWechatDevtoolsHttpUrl(port, pathname, query) {
|
|
1155
|
+
const url = new URL(`http://127.0.0.1:${port}${pathname}`);
|
|
1156
|
+
for (const [key, value] of Object.entries(query)) url.searchParams.set(key, value);
|
|
1157
|
+
return url;
|
|
1158
|
+
}
|
|
1159
|
+
function parseWechatDevtoolsEngineBuildResult(body) {
|
|
1160
|
+
try {
|
|
1161
|
+
return JSON.parse(body);
|
|
1162
|
+
} catch {
|
|
1163
|
+
return {};
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
async function requestWechatDevtoolsHttp(pathname, query, options = {}) {
|
|
1167
|
+
const url = createWechatDevtoolsHttpUrl(await resolveWechatDevtoolsHttpPort(options.port), pathname, query);
|
|
1168
|
+
const controller = new AbortController();
|
|
1169
|
+
const timeout = setTimeout(() => {
|
|
1170
|
+
controller.abort();
|
|
1171
|
+
}, options.timeoutMs ?? 1e4);
|
|
1172
|
+
try {
|
|
1173
|
+
const response = await fetch(url, {
|
|
1174
|
+
method: "GET",
|
|
1175
|
+
signal: controller.signal
|
|
1176
|
+
});
|
|
1177
|
+
const body = await response.text();
|
|
1178
|
+
if (!response.ok) throw createWechatDevtoolsHttpError(body || `HTTP ${response.status}`, "WECHAT_DEVTOOLS_HTTP_REQUEST_FAILED");
|
|
1179
|
+
return body;
|
|
1180
|
+
} catch (error) {
|
|
1181
|
+
if (error instanceof Error && error.name === "AbortError") throw createWechatDevtoolsHttpError("WECHAT_DEVTOOLS_HTTP_TIMEOUT", "WECHAT_DEVTOOLS_HTTP_TIMEOUT");
|
|
1182
|
+
throw error;
|
|
1183
|
+
} finally {
|
|
1184
|
+
clearTimeout(timeout);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* @description 通过微信开发者工具 HTTP 服务端口重新打开项目;若项目已打开,开发者工具会刷新当前项目。
|
|
1189
|
+
*/
|
|
1190
|
+
async function openWechatIdeProjectByHttp(projectPath, options = {}) {
|
|
1191
|
+
return await requestWechatDevtoolsHttp("/open", { projectpath: path.resolve(projectPath) }, options);
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* @description 通过微信开发者工具 HTTP 服务端口重置当前项目的 fileutils 状态。
|
|
1195
|
+
*/
|
|
1196
|
+
async function resetWechatIdeFileUtilsByHttp(projectPath, options = {}) {
|
|
1197
|
+
return await requestWechatDevtoolsHttp("/v2/resetfileutils", { project: path.resolve(projectPath) }, options);
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* @description 通过微信开发者工具 HTTP 服务端口触发 engine build。
|
|
1201
|
+
*/
|
|
1202
|
+
async function startWechatIdeEngineBuildByHttp(projectPath, options = {}) {
|
|
1203
|
+
return { body: await requestWechatDevtoolsHttp("/engine/build", { projectpath: path.resolve(projectPath) }, options) };
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* @description 轮询微信开发者工具 engine build 状态。
|
|
1207
|
+
*/
|
|
1208
|
+
async function pollWechatIdeEngineBuildResultByHttp(options = {}) {
|
|
1209
|
+
const body = await requestWechatDevtoolsHttp("/engine/buildResult/", {}, options);
|
|
1210
|
+
const parsed = parseWechatDevtoolsEngineBuildResult(body);
|
|
1211
|
+
const status = parsed.status;
|
|
1212
|
+
return {
|
|
1213
|
+
body,
|
|
1214
|
+
done: status === ENGINE_BUILD_END,
|
|
1215
|
+
failed: status === ENGINE_BUILD_ERROR,
|
|
1216
|
+
msg: parsed.msg,
|
|
1217
|
+
status
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
const WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES = {
|
|
1221
|
+
BUILDING: ENGINE_BUILD_BUILDING,
|
|
1222
|
+
END: ENGINE_BUILD_END,
|
|
1223
|
+
ERROR: ENGINE_BUILD_ERROR,
|
|
1224
|
+
NOT_START: ENGINE_BUILD_NOT_START,
|
|
1225
|
+
OPEN_PROJECT: ENGINE_BUILD_OPEN_PROJECT
|
|
1226
|
+
};
|
|
1227
|
+
//#endregion
|
|
1228
|
+
//#region src/cli/engine.ts
|
|
1229
|
+
function createEngineBuildError(message, code) {
|
|
1230
|
+
const error = new Error(message);
|
|
1231
|
+
error.code = code;
|
|
1232
|
+
return error;
|
|
1233
|
+
}
|
|
1234
|
+
function sleep(ms) {
|
|
1235
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1236
|
+
}
|
|
1237
|
+
function createEngineBuildLogFilename() {
|
|
1238
|
+
const now = /* @__PURE__ */ new Date();
|
|
1239
|
+
return `${[
|
|
1240
|
+
now.getFullYear(),
|
|
1241
|
+
now.getMonth() + 1,
|
|
1242
|
+
now.getDate(),
|
|
1243
|
+
now.getHours(),
|
|
1244
|
+
now.getMinutes(),
|
|
1245
|
+
now.getSeconds()
|
|
1246
|
+
].join("-")}.json`;
|
|
1247
|
+
}
|
|
1248
|
+
async function resolveEngineBuildLogFilePath(logPath) {
|
|
1249
|
+
const resolvedLogPath = path.resolve(logPath);
|
|
1250
|
+
try {
|
|
1251
|
+
if ((await fs.stat(resolvedLogPath)).isDirectory()) return path.join(resolvedLogPath, createEngineBuildLogFilename());
|
|
1252
|
+
await fs.rm(resolvedLogPath, { force: true });
|
|
1253
|
+
} catch {}
|
|
1254
|
+
await fs.mkdir(path.dirname(resolvedLogPath), { recursive: true });
|
|
1255
|
+
return resolvedLogPath;
|
|
1256
|
+
}
|
|
1257
|
+
async function writeEngineBuildLog(logPath, content) {
|
|
1258
|
+
if (!logPath) return;
|
|
1259
|
+
const filePath = await resolveEngineBuildLogFilePath(logPath);
|
|
1260
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* @description 通过开发者工具 HTTP 服务端口执行 engine build,并轮询直到构建结束。
|
|
1264
|
+
*/
|
|
1265
|
+
async function runWechatIdeEngineBuildByHttp(projectPath, options = {}) {
|
|
1266
|
+
await startWechatIdeEngineBuildByHttp(projectPath, options);
|
|
1267
|
+
const startedAt = Date.now();
|
|
1268
|
+
while (true) {
|
|
1269
|
+
if (Date.now() - startedAt > (options.overallTimeoutMs ?? 12e4)) throw createEngineBuildError("WECHAT_DEVTOOLS_ENGINE_BUILD_TIMEOUT", "WECHAT_DEVTOOLS_ENGINE_BUILD_TIMEOUT");
|
|
1270
|
+
const result = await pollWechatIdeEngineBuildResultByHttp(options);
|
|
1271
|
+
options.onProgress?.(result);
|
|
1272
|
+
if (result.failed) throw createEngineBuildError(result.msg || result.body || "WECHAT_DEVTOOLS_ENGINE_BUILD_FAILED", "WECHAT_DEVTOOLS_ENGINE_BUILD_FAILED");
|
|
1273
|
+
if (result.done) return result;
|
|
1274
|
+
await sleep(options.pollIntervalMs ?? 1e3);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* @description 以更接近官方 CLI 的方式执行 engine build,并支持将构建日志写入文件。
|
|
1279
|
+
*/
|
|
1280
|
+
async function runWechatIdeEngineBuild(projectPath, options = {}) {
|
|
1281
|
+
const logs = [];
|
|
1282
|
+
let lastLoggedMessage;
|
|
1283
|
+
try {
|
|
1284
|
+
const result = await runWechatIdeEngineBuildByHttp(projectPath, {
|
|
1285
|
+
...options,
|
|
1286
|
+
onProgress: (progress) => {
|
|
1287
|
+
if (progress.msg && progress.msg !== lastLoggedMessage) {
|
|
1288
|
+
lastLoggedMessage = progress.msg;
|
|
1289
|
+
logs.push(progress.msg);
|
|
1290
|
+
}
|
|
1291
|
+
options.onProgress?.(progress);
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
await writeEngineBuildLog(options.logPath, logs.join("\n"));
|
|
1295
|
+
return result;
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
logs.push(error instanceof Error ? error.message : String(error));
|
|
1298
|
+
await writeEngineBuildLog(options.logPath, logs.join("\n"));
|
|
1299
|
+
throw error;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
//#endregion
|
|
963
1303
|
//#region src/cli/forwardConsole.ts
|
|
964
1304
|
const DEFAULT_FORWARD_CONSOLE_LEVELS = [
|
|
965
1305
|
"log",
|
|
@@ -1154,6 +1494,13 @@ async function runMinidev(argv) {
|
|
|
1154
1494
|
}
|
|
1155
1495
|
//#endregion
|
|
1156
1496
|
//#region src/cli/retry.ts
|
|
1497
|
+
const RETRY_PROMPT_INITIAL_IGNORE_MS = 300;
|
|
1498
|
+
const RETRY_CONFIRM_KEYS = ["y"];
|
|
1499
|
+
const RETRY_CANCEL_KEYS = [
|
|
1500
|
+
"q",
|
|
1501
|
+
"Esc",
|
|
1502
|
+
"Ctrl+C"
|
|
1503
|
+
];
|
|
1157
1504
|
const LOGIN_REQUIRED_PATTERNS = [
|
|
1158
1505
|
/code\s*[:=]\s*10/i,
|
|
1159
1506
|
/需要重新登录/,
|
|
@@ -1161,14 +1508,6 @@ const LOGIN_REQUIRED_PATTERNS = [
|
|
|
1161
1508
|
/re-?login/i
|
|
1162
1509
|
];
|
|
1163
1510
|
/**
|
|
1164
|
-
* @description 判断是否为微信开发者工具登录失效错误。
|
|
1165
|
-
*/
|
|
1166
|
-
function isWechatIdeLoginRequiredError(error) {
|
|
1167
|
-
const text = extractExecutionErrorText(error);
|
|
1168
|
-
if (!text) return false;
|
|
1169
|
-
return LOGIN_REQUIRED_PATTERNS.some((pattern) => pattern.test(text));
|
|
1170
|
-
}
|
|
1171
|
-
/**
|
|
1172
1511
|
* @description 提取执行错误文本,便于统一匹配与提示。
|
|
1173
1512
|
*/
|
|
1174
1513
|
function extractExecutionErrorText(error) {
|
|
@@ -1183,6 +1522,23 @@ function extractExecutionErrorText(error) {
|
|
|
1183
1522
|
]) if (typeof field === "string" && field.trim()) parts.push(field);
|
|
1184
1523
|
return parts.join("\n");
|
|
1185
1524
|
}
|
|
1525
|
+
function extractLoginRequiredMessage(text) {
|
|
1526
|
+
if (!text) return "";
|
|
1527
|
+
if (/需要重新登录/.test(text)) return "需要重新登录";
|
|
1528
|
+
const englishMatch = text.match(/need\s+re-?login|re-?login/i);
|
|
1529
|
+
if (englishMatch?.[0]) return englishMatch[0].toLowerCase();
|
|
1530
|
+
const firstLine = text.split(/\r?\n/).map((line) => line.trim()).find((line) => Boolean(line) && !line.startsWith("at "));
|
|
1531
|
+
if (!firstLine) return "";
|
|
1532
|
+
return firstLine.replace(/^\[error\]\s*/i, "").replace(/^error\s*:\s*/i, "").slice(0, 120);
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* @description 判断是否为微信开发者工具登录失效错误。
|
|
1536
|
+
*/
|
|
1537
|
+
function isWechatIdeLoginRequiredError(error) {
|
|
1538
|
+
const text = extractExecutionErrorText(error);
|
|
1539
|
+
if (!text) return false;
|
|
1540
|
+
return LOGIN_REQUIRED_PATTERNS.some((pattern) => pattern.test(text));
|
|
1541
|
+
}
|
|
1186
1542
|
/**
|
|
1187
1543
|
* @description 将登录失效错误格式化为更易读的摘要。
|
|
1188
1544
|
*/
|
|
@@ -1208,114 +1564,69 @@ function createWechatIdeLoginRequiredExitError(error, reason) {
|
|
|
1208
1564
|
loginError.exitCode = 10;
|
|
1209
1565
|
return loginError;
|
|
1210
1566
|
}
|
|
1211
|
-
function extractLoginRequiredMessage(text) {
|
|
1212
|
-
if (!text) return "";
|
|
1213
|
-
if (/需要重新登录/.test(text)) return "需要重新登录";
|
|
1214
|
-
const englishMatch = text.match(/need\s+re-?login|re-?login/i);
|
|
1215
|
-
if (englishMatch?.[0]) return englishMatch[0].toLowerCase();
|
|
1216
|
-
const firstLine = text.split(/\r?\n/).map((line) => line.trim()).find((line) => Boolean(line) && !line.startsWith("at "));
|
|
1217
|
-
if (!firstLine) return "";
|
|
1218
|
-
return firstLine.replace(/^\[error\]\s*/i, "").replace(/^error\s*:\s*/i, "").slice(0, 120);
|
|
1219
|
-
}
|
|
1220
1567
|
/**
|
|
1221
|
-
* @description
|
|
1568
|
+
* @description 交互等待用户按 y 重试,按 q 或 Ctrl+C 取消。
|
|
1222
1569
|
*/
|
|
1223
1570
|
async function waitForRetryKeypress(options = {}) {
|
|
1224
1571
|
const { timeoutMs = 3e4 } = options;
|
|
1225
1572
|
if (!process.stdin.isTTY) return "cancel";
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
process.stdin.resume();
|
|
1230
|
-
return new Promise((resolve) => {
|
|
1231
|
-
let settled = false;
|
|
1232
|
-
const timeout = setTimeout(() => {
|
|
1233
|
-
done("timeout");
|
|
1234
|
-
}, Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 3e4);
|
|
1235
|
-
const cleanup = () => {
|
|
1236
|
-
clearTimeout(timeout);
|
|
1237
|
-
process.stdin.off("keypress", onKeypress);
|
|
1238
|
-
if (hasSetRawMode) process.stdin.setRawMode(false);
|
|
1239
|
-
process.stdin.pause();
|
|
1240
|
-
};
|
|
1241
|
-
const done = (value) => {
|
|
1242
|
-
if (settled) return;
|
|
1243
|
-
settled = true;
|
|
1244
|
-
cleanup();
|
|
1245
|
-
resolve(value);
|
|
1246
|
-
};
|
|
1247
|
-
const onKeypress = (_str, key) => {
|
|
1573
|
+
return await waitForExclusiveKeypress({
|
|
1574
|
+
ignoreInitialMs: 300,
|
|
1575
|
+
onKeypress: (_str, key) => {
|
|
1248
1576
|
if (!key) return;
|
|
1249
|
-
if (key.ctrl && key.name === "c")
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
done("retry");
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
if (key.name === "q" || key.name === "escape") done("cancel");
|
|
1258
|
-
};
|
|
1259
|
-
process.stdin.on("keypress", onKeypress);
|
|
1577
|
+
if (key.ctrl && key.name === "c") return "cancel";
|
|
1578
|
+
if (key.name === "y") return "retry";
|
|
1579
|
+
if (key.name === "q" || key.name === "escape") return "cancel";
|
|
1580
|
+
},
|
|
1581
|
+
timeoutMs
|
|
1260
1582
|
});
|
|
1261
1583
|
}
|
|
1584
|
+
function highlightHotkey(key) {
|
|
1585
|
+
return colors.bold(colors.green(key));
|
|
1586
|
+
}
|
|
1262
1587
|
/**
|
|
1263
1588
|
* @description 生成重试按键提示,并高亮关键热键。
|
|
1264
1589
|
*/
|
|
1265
1590
|
function formatRetryHotkeyPrompt(timeoutMs = 3e4) {
|
|
1266
1591
|
const highlight = (key) => highlightHotkey(key);
|
|
1267
1592
|
const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1e3));
|
|
1268
|
-
|
|
1593
|
+
const confirmKeys = RETRY_CONFIRM_KEYS.map(highlight).join(" / ");
|
|
1594
|
+
const cancelKeys = RETRY_CANCEL_KEYS.map(highlight).join(" / ");
|
|
1595
|
+
return i18nText(`按 ${confirmKeys} 重试,按 ${cancelKeys} 退出(${timeoutSeconds}s 内无输入将自动失败)。`, `Press ${confirmKeys} to retry, ${cancelKeys} to cancel (auto fail in ${timeoutSeconds}s).`);
|
|
1269
1596
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1597
|
+
/**
|
|
1598
|
+
* @description 输出重试热键提示,并独占等待当前提示对应的按键输入。
|
|
1599
|
+
*/
|
|
1600
|
+
async function promptRetryKeypress(options) {
|
|
1601
|
+
const { logger, timeoutMs = 3e4 } = options;
|
|
1602
|
+
logger.info(formatRetryHotkeyPrompt(timeoutMs));
|
|
1603
|
+
return await waitForRetryKeypress({ timeoutMs });
|
|
1272
1604
|
}
|
|
1273
|
-
//#endregion
|
|
1274
|
-
//#region src/cli/run-login.ts
|
|
1275
1605
|
/**
|
|
1276
|
-
* @description
|
|
1606
|
+
* @description 统一处理微信开发者工具登录失效场景下的提示与重试交互。
|
|
1277
1607
|
*/
|
|
1278
|
-
async function
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
if (await promptLoginRetry(result, loginRetryOptions, retryCount) === "retry") {
|
|
1292
|
-
retryCount += 1;
|
|
1293
|
-
logger_default.info(i18nText("正在重试连接微信开发者工具...", "Retrying to connect Wechat DevTools..."));
|
|
1294
|
-
continue;
|
|
1295
|
-
}
|
|
1296
|
-
throw createWechatIdeLoginRequiredExitError(result);
|
|
1297
|
-
} catch (error) {
|
|
1298
|
-
if (!isWechatIdeLoginRequiredError(error)) throw error;
|
|
1299
|
-
if (await promptLoginRetry(error, loginRetryOptions, retryCount) === "retry") {
|
|
1300
|
-
retryCount += 1;
|
|
1301
|
-
logger_default.info(i18nText("正在重试连接微信开发者工具...", "Retrying to connect Wechat DevTools..."));
|
|
1302
|
-
continue;
|
|
1303
|
-
}
|
|
1304
|
-
throw createWechatIdeLoginRequiredExitError(error);
|
|
1608
|
+
async function promptWechatIdeLoginRetry(options) {
|
|
1609
|
+
const { allowRetry = true, cancelLevel = "info", error, logger, promptOpenIdeLogin = false, retryTimeoutMs = 3e4 } = options;
|
|
1610
|
+
logger.error(i18nText("检测到微信开发者工具登录状态失效,请先登录后重试。", "Wechat DevTools login has expired. Please login and retry."));
|
|
1611
|
+
if (promptOpenIdeLogin) logger.warn(i18nText("请先打开微信开发者工具完成登录。", "Please open Wechat DevTools and complete login first."));
|
|
1612
|
+
logger.warn(formatWechatIdeLoginRequiredError(error));
|
|
1613
|
+
if (!allowRetry) return "cancel";
|
|
1614
|
+
const action = await promptRetryKeypress({
|
|
1615
|
+
logger,
|
|
1616
|
+
timeoutMs: retryTimeoutMs
|
|
1617
|
+
});
|
|
1618
|
+
if (action === "timeout") {
|
|
1619
|
+
logger.error(i18nText(`等待登录重试输入超时(${retryTimeoutMs}ms),已自动取消。`, `Retry prompt timed out (${retryTimeoutMs}ms), canceled automatically.`));
|
|
1620
|
+
return "cancel";
|
|
1305
1621
|
}
|
|
1622
|
+
if (action !== "retry") {
|
|
1623
|
+
logger[cancelLevel](i18nText("已取消重试。完成登录后请重新执行当前命令。", "Retry canceled. Please run the command again after login."));
|
|
1624
|
+
return "cancel";
|
|
1625
|
+
}
|
|
1626
|
+
return "retry";
|
|
1306
1627
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
const ciMode = process.env.CI === "true";
|
|
1310
|
-
const nonTtyStdin = !isStdinInteractive();
|
|
1311
|
-
const nonInteractive = nonInteractiveFlag || ciMode || nonTtyStdin;
|
|
1312
|
-
const retryModeRaw = readOptionValue(argv, "--login-retry")?.toLowerCase();
|
|
1313
|
-
return {
|
|
1314
|
-
nonInteractive,
|
|
1315
|
-
retryMode: normalizeLoginRetryMode(retryModeRaw, nonInteractive),
|
|
1316
|
-
retryTimeoutMs: normalizeLoginRetryTimeout(readOptionValue(argv, "--login-retry-timeout"))
|
|
1317
|
-
};
|
|
1318
|
-
}
|
|
1628
|
+
//#endregion
|
|
1629
|
+
//#region src/cli/run-login-config.ts
|
|
1319
1630
|
function normalizeLoginRetryMode(value, nonInteractive) {
|
|
1320
1631
|
if (value && value !== "never" && value !== "once" && value !== "always") throw new Error(i18nText(`不支持的 --login-retry 值: ${value}(仅支持 never/once/always)`, `Invalid --login-retry value: ${value} (supported: never/once/always)`));
|
|
1321
1632
|
if (value === "never" || value === "once" || value === "always") return value;
|
|
@@ -1336,30 +1647,70 @@ function stripLoginRetryControlFlags(argv) {
|
|
|
1336
1647
|
next = removeOption(next, "--non-interactive");
|
|
1337
1648
|
return next;
|
|
1338
1649
|
}
|
|
1650
|
+
/**
|
|
1651
|
+
* @description 解析登录重试相关控制参数,并产出实际执行微信 CLI 所需的运行时参数。
|
|
1652
|
+
*/
|
|
1653
|
+
function resolveLoginRetryConfig(argv) {
|
|
1654
|
+
const nonInteractiveFlag = argv.includes("--non-interactive");
|
|
1655
|
+
const ciMode = process.env.CI === "true";
|
|
1656
|
+
const nonTtyStdin = !isStdinInteractive();
|
|
1657
|
+
const nonInteractive = nonInteractiveFlag || ciMode || nonTtyStdin;
|
|
1658
|
+
const retryModeRaw = readOptionValue(argv, "--login-retry")?.toLowerCase();
|
|
1659
|
+
return {
|
|
1660
|
+
nonInteractive,
|
|
1661
|
+
retryMode: normalizeLoginRetryMode(retryModeRaw, nonInteractive),
|
|
1662
|
+
retryTimeoutMs: normalizeLoginRetryTimeout(readOptionValue(argv, "--login-retry-timeout")),
|
|
1663
|
+
runtimeArgv: stripLoginRetryControlFlags(argv)
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
//#endregion
|
|
1667
|
+
//#region src/cli/run-login-executor.ts
|
|
1668
|
+
/**
|
|
1669
|
+
* @description 执行可重试命令循环,并将“是否可重试”与“如何提示重试”交给调用方定义。
|
|
1670
|
+
*/
|
|
1671
|
+
async function runRetryableCommand(options) {
|
|
1672
|
+
const { createCancelError, execute, isRetryableResult, onCancel, onRetry, promptRetry, shouldRetry } = options;
|
|
1673
|
+
let retryCount = 0;
|
|
1674
|
+
while (true) {
|
|
1675
|
+
const result = await execute();
|
|
1676
|
+
if (!isRetryableResult(result)) return result;
|
|
1677
|
+
if (shouldRetry(await promptRetry(result, retryCount))) {
|
|
1678
|
+
retryCount += 1;
|
|
1679
|
+
onRetry?.();
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1682
|
+
onCancel?.(result);
|
|
1683
|
+
throw createCancelError(result);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
//#endregion
|
|
1687
|
+
//#region src/cli/run-login.ts
|
|
1688
|
+
function unwrapWechatCliExecutionError(result) {
|
|
1689
|
+
return result.kind === "retryable" ? result.error : result.value;
|
|
1690
|
+
}
|
|
1339
1691
|
async function promptLoginRetry(errorLike, options, retryCount) {
|
|
1340
1692
|
const { nonInteractive, retryMode, retryTimeoutMs } = options;
|
|
1341
|
-
logger_default.error(i18nText("检测到微信开发者工具登录状态失效,请先登录后重试。", "Wechat DevTools login has expired. Please login and retry."));
|
|
1342
|
-
logger_default.warn(i18nText("请先打开微信开发者工具完成登录。", "Please open Wechat DevTools and complete login first."));
|
|
1343
|
-
logger_default.warn(formatWechatIdeLoginRequiredError(errorLike));
|
|
1344
1693
|
if (nonInteractive) {
|
|
1345
1694
|
logger_default.error(i18nText("当前为非交互模式,检测到登录失效后直接失败。", "Non-interactive mode enabled, failing immediately on login expiration."));
|
|
1346
1695
|
return "cancel";
|
|
1347
1696
|
}
|
|
1348
1697
|
if (!(retryMode === "always" || retryMode === "once" && retryCount < 1)) {
|
|
1698
|
+
await promptWechatIdeLoginRetry({
|
|
1699
|
+
allowRetry: false,
|
|
1700
|
+
error: errorLike,
|
|
1701
|
+
logger: logger_default,
|
|
1702
|
+
promptOpenIdeLogin: true,
|
|
1703
|
+
retryTimeoutMs
|
|
1704
|
+
});
|
|
1349
1705
|
logger_default.info(i18nText("当前重试策略不允许继续重试。", "Current retry policy does not allow further retries."));
|
|
1350
1706
|
return "cancel";
|
|
1351
1707
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
}
|
|
1358
|
-
if (action !== "retry") {
|
|
1359
|
-
logger_default.info(i18nText("已取消重试。完成登录后请重新执行当前命令。", "Retry canceled. Please run the command again after login."));
|
|
1360
|
-
return "cancel";
|
|
1361
|
-
}
|
|
1362
|
-
return "retry";
|
|
1708
|
+
return await promptWechatIdeLoginRetry({
|
|
1709
|
+
error: errorLike,
|
|
1710
|
+
logger: logger_default,
|
|
1711
|
+
promptOpenIdeLogin: true,
|
|
1712
|
+
retryTimeoutMs
|
|
1713
|
+
});
|
|
1363
1714
|
}
|
|
1364
1715
|
function flushExecutionOutput(result) {
|
|
1365
1716
|
if (!result || typeof result !== "object") return;
|
|
@@ -1367,6 +1718,92 @@ function flushExecutionOutput(result) {
|
|
|
1367
1718
|
if (typeof candidate.stdout === "string" && candidate.stdout) process.stdout.write(candidate.stdout);
|
|
1368
1719
|
if (typeof candidate.stderr === "string" && candidate.stderr) process.stderr.write(candidate.stderr);
|
|
1369
1720
|
}
|
|
1721
|
+
/**
|
|
1722
|
+
* @description 运行微信开发者工具 CLI,并在登录失效时允许按键重试。
|
|
1723
|
+
*/
|
|
1724
|
+
async function runWechatCliWithRetry(cliPath, argv) {
|
|
1725
|
+
const loginRetryOptions = resolveLoginRetryConfig(argv);
|
|
1726
|
+
const result = await runWithSuspendedSharedInput(async () => {
|
|
1727
|
+
return await runRetryableCommand({
|
|
1728
|
+
createCancelError: (result) => createWechatIdeLoginRequiredExitError(unwrapWechatCliExecutionError(result)),
|
|
1729
|
+
execute: async () => {
|
|
1730
|
+
try {
|
|
1731
|
+
return {
|
|
1732
|
+
kind: "result",
|
|
1733
|
+
value: await execute(cliPath, loginRetryOptions.runtimeArgv, {
|
|
1734
|
+
pipeStdout: false,
|
|
1735
|
+
pipeStderr: false
|
|
1736
|
+
})
|
|
1737
|
+
};
|
|
1738
|
+
} catch (error) {
|
|
1739
|
+
if (!isWechatIdeLoginRequiredError(error)) throw error;
|
|
1740
|
+
return {
|
|
1741
|
+
error,
|
|
1742
|
+
kind: "retryable"
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
},
|
|
1746
|
+
isRetryableResult: (result) => result.kind === "retryable" || isWechatIdeLoginRequiredError(result.value),
|
|
1747
|
+
onRetry: () => {
|
|
1748
|
+
logger_default.info(i18nText("正在重试连接微信开发者工具...", "Retrying to connect Wechat DevTools..."));
|
|
1749
|
+
},
|
|
1750
|
+
promptRetry: async (result, retryCount) => {
|
|
1751
|
+
return await promptLoginRetry(unwrapWechatCliExecutionError(result), loginRetryOptions, retryCount);
|
|
1752
|
+
},
|
|
1753
|
+
shouldRetry: (action) => action === "retry"
|
|
1754
|
+
});
|
|
1755
|
+
});
|
|
1756
|
+
if (result.kind === "retryable") throw createWechatIdeLoginRequiredExitError(result.error);
|
|
1757
|
+
if (isWechatIdeLoginRequiredError(result.value)) throw createWechatIdeLoginRequiredExitError(result.value);
|
|
1758
|
+
flushExecutionOutput(result.value);
|
|
1759
|
+
}
|
|
1760
|
+
//#endregion
|
|
1761
|
+
//#region src/cli/run-wechat-cli.ts
|
|
1762
|
+
function shouldBootstrapWechatDevtools(command) {
|
|
1763
|
+
return command === "open" || command === "auto" || command === "auto-preview";
|
|
1764
|
+
}
|
|
1765
|
+
function resolveBooleanCliOption(argv, optionName) {
|
|
1766
|
+
if (argv.includes(optionName)) return true;
|
|
1767
|
+
const rawValue = readOptionValue(argv, optionName);
|
|
1768
|
+
if (rawValue === void 0) return;
|
|
1769
|
+
const normalized = rawValue.trim().toLowerCase();
|
|
1770
|
+
if (normalized === "" || normalized === "true" || normalized === "1" || normalized === "on") return true;
|
|
1771
|
+
if (normalized === "false" || normalized === "0" || normalized === "off") return false;
|
|
1772
|
+
return true;
|
|
1773
|
+
}
|
|
1774
|
+
async function handleMissingCliPath(source) {
|
|
1775
|
+
const message = source === "custom" ? i18nText("在当前自定义路径中未找到微信web开发者命令行工具,请重新指定路径。", "Cannot find Wechat Web DevTools CLI in custom path, please reconfigure it.") : i18nText(`未检测到微信web开发者命令行工具,请执行 ${colors.bold(colors.green("weapp-ide-cli config"))} 指定路径。`, `Wechat Web DevTools CLI not found, please run ${colors.bold(colors.green("weapp-ide-cli config"))} to configure it.`);
|
|
1776
|
+
logger_default.warn(message);
|
|
1777
|
+
await promptForCliPath();
|
|
1778
|
+
}
|
|
1779
|
+
async function maybeBootstrapWechatDevtoolsSettings(argv) {
|
|
1780
|
+
const command = argv[0];
|
|
1781
|
+
if (!shouldBootstrapWechatDevtools(command)) return;
|
|
1782
|
+
const config = await readCustomConfig();
|
|
1783
|
+
if (config.autoBootstrapDevtools === false) return;
|
|
1784
|
+
const projectPath = readOptionValue(argv, "--project");
|
|
1785
|
+
const trustProjectOption = resolveBooleanCliOption(argv, "--trust-project");
|
|
1786
|
+
await bootstrapWechatDevtoolsSettings({
|
|
1787
|
+
projectPath,
|
|
1788
|
+
trustProject: trustProjectOption === void 0 ? config.autoTrustProject ?? false : trustProjectOption
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* @description 执行微信开发者工具 CLI 阶段,包括环境检查、路径解析、bootstrap 与登录重试。
|
|
1793
|
+
*/
|
|
1794
|
+
async function runWechatCliCommand(argv) {
|
|
1795
|
+
if (!isOperatingSystemSupported(operatingSystemName)) {
|
|
1796
|
+
logger_default.warn(i18nText(`微信web开发者工具不支持当前平台:${operatingSystemName} !`, `Wechat Web DevTools CLI is not supported on current platform: ${operatingSystemName}!`));
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
const { cliPath, source } = await resolveCliPath();
|
|
1800
|
+
if (!cliPath) {
|
|
1801
|
+
await handleMissingCliPath(source);
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
await maybeBootstrapWechatDevtoolsSettings(argv);
|
|
1805
|
+
await runWechatCliWithRetry(cliPath, argv);
|
|
1806
|
+
}
|
|
1370
1807
|
//#endregion
|
|
1371
1808
|
//#region src/cli/wechat-command-schema.ts
|
|
1372
1809
|
const CACHE_CLEAN_TYPES = [
|
|
@@ -1378,6 +1815,11 @@ const CACHE_CLEAN_TYPES = [
|
|
|
1378
1815
|
"session",
|
|
1379
1816
|
"all"
|
|
1380
1817
|
];
|
|
1818
|
+
const QR_FORMAT_TYPES = [
|
|
1819
|
+
"terminal",
|
|
1820
|
+
"image",
|
|
1821
|
+
"base64"
|
|
1822
|
+
];
|
|
1381
1823
|
function isNonEmptyText(value) {
|
|
1382
1824
|
return typeof value === "string" && value.trim().length > 0;
|
|
1383
1825
|
}
|
|
@@ -1388,14 +1830,14 @@ function validatePortOption(argv) {
|
|
|
1388
1830
|
if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(i18nText(`无效的 --port 值: ${port}(必须为正整数)`, `Invalid --port value: ${port} (must be a positive integer)`));
|
|
1389
1831
|
}
|
|
1390
1832
|
function validateProjectLocator(command, argv) {
|
|
1391
|
-
const projectPath = readOptionValue(argv, "--project");
|
|
1833
|
+
const projectPath = readOptionValue(argv, "--project", "-p");
|
|
1392
1834
|
const appid = readOptionValue(argv, "--appid");
|
|
1393
1835
|
if (isNonEmptyText(projectPath) || isNonEmptyText(appid)) return;
|
|
1394
1836
|
throw new Error(i18nText(`${command} 命令需要提供 --project 或 --appid`, `${command} command requires --project or --appid`));
|
|
1395
1837
|
}
|
|
1396
1838
|
function validateExtAppidDependency(argv) {
|
|
1397
1839
|
if (!isNonEmptyText(readOptionValue(argv, "--ext-appid"))) return;
|
|
1398
|
-
if (isNonEmptyText(readOptionValue(argv, "--project"))) return;
|
|
1840
|
+
if (isNonEmptyText(readOptionValue(argv, "--project", "-p"))) return;
|
|
1399
1841
|
if (isNonEmptyText(readOptionValue(argv, "--appid"))) return;
|
|
1400
1842
|
throw new Error(i18nText("--ext-appid 需要和 --appid 一起使用(当未提供 --project 时)", "--ext-appid requires --appid when --project is not provided"));
|
|
1401
1843
|
}
|
|
@@ -1408,26 +1850,480 @@ function validateWechatCliCommandArgs(argv) {
|
|
|
1408
1850
|
validatePortOption(argv);
|
|
1409
1851
|
validateExtAppidDependency(argv);
|
|
1410
1852
|
if (command === "upload") {
|
|
1411
|
-
const version = readOptionValue(argv, "--version"
|
|
1412
|
-
const desc = readOptionValue(argv, "--desc"
|
|
1853
|
+
const version = readOptionValue(argv, "--version", "-v");
|
|
1854
|
+
const desc = readOptionValue(argv, "--desc", "-d");
|
|
1413
1855
|
if (!isNonEmptyText(version) || !isNonEmptyText(desc)) throw new Error(i18nText("upload 命令缺少必填参数:--version/-v 和 --desc/-d", "upload command requires both --version/-v and --desc/-d"));
|
|
1414
1856
|
}
|
|
1415
1857
|
if (command === "preview") {
|
|
1416
1858
|
validateProjectLocator(command, argv);
|
|
1417
|
-
const qrFormat = readOptionValue(argv, "--qr-format"
|
|
1859
|
+
const qrFormat = readOptionValue(argv, "--qr-format", "-f");
|
|
1418
1860
|
if (!qrFormat) return;
|
|
1419
|
-
if (!
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1861
|
+
if (!QR_FORMAT_TYPES.includes(qrFormat.toLowerCase())) throw new Error(i18nText(`preview 命令的二维码格式无效: ${qrFormat}(仅支持 ${QR_FORMAT_TYPES.join("/")})`, `Invalid preview qr format: ${qrFormat} (supported: ${QR_FORMAT_TYPES.join("/")})`));
|
|
1862
|
+
}
|
|
1863
|
+
if (command === "login") {
|
|
1864
|
+
const qrFormat = readOptionValue(argv, "--qr-format", "-f");
|
|
1865
|
+
if (qrFormat && !QR_FORMAT_TYPES.includes(qrFormat.toLowerCase())) throw new Error(i18nText(`login 命令的二维码格式无效: ${qrFormat}(仅支持 ${QR_FORMAT_TYPES.join("/")})`, `Invalid login qr format: ${qrFormat} (supported: ${QR_FORMAT_TYPES.join("/")})`));
|
|
1424
1866
|
}
|
|
1425
1867
|
if (command === "cache") {
|
|
1426
|
-
const cleanType = readOptionValue(argv, "--clean"
|
|
1868
|
+
const cleanType = readOptionValue(argv, "--clean", "-c");
|
|
1427
1869
|
if (!isNonEmptyText(cleanType)) throw new Error(i18nText("cache 命令缺少必填参数:--clean/-c", "cache command requires --clean/-c"));
|
|
1428
1870
|
if (!CACHE_CLEAN_TYPES.includes(cleanType)) throw new Error(i18nText(`cache 命令的清理类型无效: ${cleanType}(仅支持 ${CACHE_CLEAN_TYPES.join("/")})`, `Invalid cache clean type: ${cleanType} (supported: ${CACHE_CLEAN_TYPES.join("/")})`));
|
|
1429
1871
|
}
|
|
1430
|
-
if (command === "upload" || command === "auto" || command === "auto-preview") validateProjectLocator(command, argv);
|
|
1872
|
+
if (command === "upload" || command === "auto" || command === "auto-replay" || command === "auto-preview" || command === "reset-fileutils") validateProjectLocator(command, argv);
|
|
1873
|
+
if (command === "build-npm") {
|
|
1874
|
+
const compileType = readOptionValue(argv, "--compile-type");
|
|
1875
|
+
if (compileType !== void 0 && !isNonEmptyText(compileType)) throw new Error(i18nText("build-npm 命令的 --compile-type 不能为空字符串", "build-npm command requires a non-empty --compile-type value"));
|
|
1876
|
+
}
|
|
1877
|
+
if (command === "build-apk") {
|
|
1878
|
+
const output = readOptionValue(argv, "--output", "-o");
|
|
1879
|
+
const keyStore = readOptionValue(argv, "--key-store", "--keyStore", "-ks");
|
|
1880
|
+
const keyAlias = readOptionValue(argv, "--key-alias", "--keyAlias", "-ka");
|
|
1881
|
+
const keyPass = readOptionValue(argv, "--key-pass", "--keyPass", "-kp");
|
|
1882
|
+
const storePass = readOptionValue(argv, "--store-pass", "--storePass", "-sp");
|
|
1883
|
+
if (!isNonEmptyText(output) || !isNonEmptyText(keyStore) || !isNonEmptyText(keyAlias) || !isNonEmptyText(keyPass) || !isNonEmptyText(storePass)) throw new Error(i18nText("build-apk 命令缺少必填参数:--output/-o、--key-store/-ks、--key-alias/-ka、--key-pass/-kp、--store-pass/-sp", "build-apk command requires --output/-o, --key-store/-ks, --key-alias/-ka, --key-pass/-kp, and --store-pass/-sp"));
|
|
1884
|
+
}
|
|
1885
|
+
if (command === "build-ipa") {
|
|
1886
|
+
const output = readOptionValue(argv, "--output", "-o");
|
|
1887
|
+
const isDistribute = readBooleanOption(argv, "--isDistribute");
|
|
1888
|
+
if (!isNonEmptyText(output) || isDistribute === void 0) throw new Error(i18nText("build-ipa 命令缺少必填参数:--output/-o 和 --isDistribute", "build-ipa command requires both --output/-o and --isDistribute"));
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
//#endregion
|
|
1892
|
+
//#region src/cli/wechat-commands.ts
|
|
1893
|
+
function appendProjectLocatorArgv(argv, options) {
|
|
1894
|
+
if (options.projectPath) argv.push("--project", path.resolve(options.projectPath));
|
|
1895
|
+
if (options.appid) argv.push("--appid", options.appid);
|
|
1896
|
+
if (options.extAppid) argv.push("--ext-appid", options.extAppid);
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* @description 调用微信开发者工具 open 命令。
|
|
1900
|
+
*/
|
|
1901
|
+
async function openWechatIde(options = {}) {
|
|
1902
|
+
const argv = ["open"];
|
|
1903
|
+
appendProjectLocatorArgv(argv, options);
|
|
1904
|
+
if (options.platform) argv.push("--platform", options.platform);
|
|
1905
|
+
if (options.trustProject) argv.push("--trust-project");
|
|
1906
|
+
await runWechatCliCommand(argv);
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* @description 调用微信开发者工具 login 命令。
|
|
1910
|
+
*/
|
|
1911
|
+
async function loginWechatIde(options = {}) {
|
|
1912
|
+
const argv = ["login"];
|
|
1913
|
+
if (options.qrFormat) argv.push("--qr-format", options.qrFormat);
|
|
1914
|
+
if (options.qrOutput) argv.push("--qr-output", path.resolve(options.qrOutput));
|
|
1915
|
+
if (options.qrSize) argv.push("--qr-size", options.qrSize);
|
|
1916
|
+
if (options.resultOutput) argv.push("--result-output", path.resolve(options.resultOutput));
|
|
1917
|
+
await runWechatCliCommand(argv);
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* @description 调用微信开发者工具 islogin 命令。
|
|
1921
|
+
*/
|
|
1922
|
+
async function isWechatIdeLoggedIn() {
|
|
1923
|
+
await runWechatCliCommand(["islogin"]);
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* @description 调用微信开发者工具 build-npm 命令。
|
|
1927
|
+
*/
|
|
1928
|
+
async function buildWechatIdeNpm(options = {}) {
|
|
1929
|
+
const argv = [
|
|
1930
|
+
"build-npm",
|
|
1931
|
+
"--project",
|
|
1932
|
+
path.resolve(options.projectPath ?? process.cwd())
|
|
1933
|
+
];
|
|
1934
|
+
if (options.compileType) argv.push("--compile-type", options.compileType);
|
|
1935
|
+
await runWechatCliCommand(argv);
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* @description 调用微信开发者工具 preview 命令。
|
|
1939
|
+
*/
|
|
1940
|
+
async function previewWechatIde(options = {}) {
|
|
1941
|
+
const argv = ["preview"];
|
|
1942
|
+
appendProjectLocatorArgv(argv, options);
|
|
1943
|
+
if (options.qrFormat) argv.push("--qr-format", options.qrFormat);
|
|
1944
|
+
if (options.qrOutput) argv.push("--qr-output", path.resolve(options.qrOutput));
|
|
1945
|
+
if (options.qrSize) argv.push("--qr-size", options.qrSize);
|
|
1946
|
+
if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
|
|
1947
|
+
if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
|
|
1948
|
+
await runWechatCliCommand(argv);
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* @description 调用微信开发者工具 auto-preview 命令。
|
|
1952
|
+
*/
|
|
1953
|
+
async function autoPreviewWechatIde(options = {}) {
|
|
1954
|
+
const argv = ["auto-preview"];
|
|
1955
|
+
appendProjectLocatorArgv(argv, options);
|
|
1956
|
+
if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
|
|
1957
|
+
if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
|
|
1958
|
+
await runWechatCliCommand(argv);
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* @description 调用微信开发者工具 auto 命令。
|
|
1962
|
+
*/
|
|
1963
|
+
async function autoWechatIde(options = {}) {
|
|
1964
|
+
const argv = ["auto"];
|
|
1965
|
+
appendProjectLocatorArgv(argv, options);
|
|
1966
|
+
if (options.port) argv.push("--auto-port", options.port);
|
|
1967
|
+
if (options.account) argv.push("--auto-account", options.account);
|
|
1968
|
+
if (options.testTicket) argv.push("--test-ticket", options.testTicket);
|
|
1969
|
+
if (options.ticket) argv.push("--ticket", options.ticket);
|
|
1970
|
+
if (options.trustProject) argv.push("--trust-project");
|
|
1971
|
+
await runWechatCliCommand(argv);
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* @description 调用微信开发者工具 auto-replay 命令。
|
|
1975
|
+
*/
|
|
1976
|
+
async function autoReplayWechatIde(options = {}) {
|
|
1977
|
+
const argv = ["auto-replay"];
|
|
1978
|
+
appendProjectLocatorArgv(argv, options);
|
|
1979
|
+
if (options.port) argv.push("--auto-port", options.port);
|
|
1980
|
+
if (options.account) argv.push("--auto-account", options.account);
|
|
1981
|
+
if (options.replayAll) argv.push("--replay-all");
|
|
1982
|
+
if (options.replayConfigPath) argv.push("--replay-config-path", path.resolve(options.replayConfigPath));
|
|
1983
|
+
if (options.testTicket) argv.push("--test-ticket", options.testTicket);
|
|
1984
|
+
if (options.ticket) argv.push("--ticket", options.ticket);
|
|
1985
|
+
if (options.trustProject) argv.push("--trust-project");
|
|
1986
|
+
await runWechatCliCommand(argv);
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* @description 调用微信开发者工具 upload 命令。
|
|
1990
|
+
*/
|
|
1991
|
+
async function uploadWechatIde(options) {
|
|
1992
|
+
const argv = ["upload"];
|
|
1993
|
+
appendProjectLocatorArgv(argv, options);
|
|
1994
|
+
argv.push("--version", options.version, "--desc", options.desc);
|
|
1995
|
+
if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
|
|
1996
|
+
await runWechatCliCommand(argv);
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* @description 调用微信开发者工具 close 命令。
|
|
2000
|
+
*/
|
|
2001
|
+
async function closeWechatIdeProject() {
|
|
2002
|
+
await runWechatCliCommand(["close"]);
|
|
2003
|
+
}
|
|
2004
|
+
/**
|
|
2005
|
+
* @description 调用微信开发者工具 quit 命令。
|
|
2006
|
+
*/
|
|
2007
|
+
async function quitWechatIde() {
|
|
2008
|
+
await runWechatCliCommand(["quit"]);
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* @description 调用微信开发者工具 cache 命令。
|
|
2012
|
+
*/
|
|
2013
|
+
async function clearWechatIdeCache(options) {
|
|
2014
|
+
await runWechatCliCommand([
|
|
2015
|
+
"cache",
|
|
2016
|
+
"--clean",
|
|
2017
|
+
options.clean
|
|
2018
|
+
]);
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* @description 调用微信开发者工具 open-other 命令。
|
|
2022
|
+
*/
|
|
2023
|
+
async function openWechatIdeOtherProject(_options = {}) {
|
|
2024
|
+
await runWechatCliCommand(["open-other"]);
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* @description 通过微信开发者工具 HTTP 服务端口重置指定项目的 fileutils 状态。
|
|
2028
|
+
*/
|
|
2029
|
+
async function resetWechatIdeFileUtils(options) {
|
|
2030
|
+
await resetWechatIdeFileUtilsByHttp(path.resolve(options.projectPath));
|
|
2031
|
+
}
|
|
2032
|
+
function createAutomatorSessionOptions(options) {
|
|
2033
|
+
return {
|
|
2034
|
+
preferOpenedSession: options.preferOpenedSession ?? true,
|
|
2035
|
+
projectPath: path.resolve(options.projectPath),
|
|
2036
|
+
sharedSession: options.sharedSession ?? true,
|
|
2037
|
+
timeout: options.timeout
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* @description 通过已打开或新建的 automator 会话获取开发者工具基础信息。
|
|
2042
|
+
*/
|
|
2043
|
+
async function getWechatIdeToolInfo(options) {
|
|
2044
|
+
return await withMiniProgram(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2045
|
+
return await miniProgram.toolInfo();
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* @description 通过已打开或新建的 automator 会话执行项目编译。
|
|
2050
|
+
*/
|
|
2051
|
+
async function compileWechatIdeByAutomator(options) {
|
|
2052
|
+
return await withMiniProgram(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2053
|
+
return await miniProgram.compile({ force: options.force });
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
/**
|
|
2057
|
+
* @description 通过已打开或新建的 automator 会话清理开发者工具缓存。
|
|
2058
|
+
*/
|
|
2059
|
+
async function clearWechatIdeCacheByAutomator(options) {
|
|
2060
|
+
return await withMiniProgram(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2061
|
+
return await miniProgram.clearCache({ clean: options.clean });
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* @description 通过已打开或新建的 automator 会话获取当前 ticket。
|
|
2066
|
+
*/
|
|
2067
|
+
async function getWechatIdeTicket(options) {
|
|
2068
|
+
return await withMiniProgram(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2069
|
+
return await miniProgram.getTicket();
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* @description 通过已打开或新建的 automator 会话设置 ticket。
|
|
2074
|
+
*/
|
|
2075
|
+
async function setWechatIdeTicket(options) {
|
|
2076
|
+
return await withMiniProgram(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2077
|
+
await miniProgram.setTicket(options.ticket);
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* @description 通过已打开或新建的 automator 会话刷新 ticket。
|
|
2082
|
+
*/
|
|
2083
|
+
async function refreshWechatIdeTicket(options) {
|
|
2084
|
+
return await withMiniProgram(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2085
|
+
await miniProgram.refreshTicket();
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* @description 通过已打开或新建的 automator 会话获取测试账号列表。
|
|
2090
|
+
*/
|
|
2091
|
+
async function getWechatIdeTestAccounts(options) {
|
|
2092
|
+
return await withMiniProgram(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2093
|
+
return await miniProgram.testAccounts();
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
2097
|
+
* @description 调用微信开发者工具 build-apk 命令。
|
|
2098
|
+
*/
|
|
2099
|
+
async function buildWechatIdeApk(options) {
|
|
2100
|
+
const argv = [
|
|
2101
|
+
"build-apk",
|
|
2102
|
+
"--key-store",
|
|
2103
|
+
path.resolve(options.keyStore),
|
|
2104
|
+
"--key-alias",
|
|
2105
|
+
options.keyAlias,
|
|
2106
|
+
"--key-pass",
|
|
2107
|
+
options.keyPass,
|
|
2108
|
+
"--store-pass",
|
|
2109
|
+
options.storePass,
|
|
2110
|
+
"--output",
|
|
2111
|
+
path.resolve(options.output)
|
|
2112
|
+
];
|
|
2113
|
+
if (options.useAab !== void 0) argv.push("--use-aab", String(options.useAab));
|
|
2114
|
+
if (options.desc) argv.push("--desc", options.desc);
|
|
2115
|
+
if (options.isUploadResourceBundle) argv.push("--isUploadResourceBundle");
|
|
2116
|
+
if (options.resourceBundleVersion) argv.push("--resourceBundleVersion", options.resourceBundleVersion);
|
|
2117
|
+
if (options.resourceBundleDesc) argv.push("--resourceBundleDesc", options.resourceBundleDesc);
|
|
2118
|
+
await runWechatCliCommand(argv);
|
|
2119
|
+
}
|
|
2120
|
+
/**
|
|
2121
|
+
* @description 调用微信开发者工具 build-ipa 命令。
|
|
2122
|
+
*/
|
|
2123
|
+
async function buildWechatIdeIpa(options) {
|
|
2124
|
+
const argv = [
|
|
2125
|
+
"build-ipa",
|
|
2126
|
+
"--output",
|
|
2127
|
+
path.resolve(options.output),
|
|
2128
|
+
"--isDistribute",
|
|
2129
|
+
String(options.isDistribute)
|
|
2130
|
+
];
|
|
2131
|
+
if (options.isRemoteBuild !== void 0) argv.push("--isRemoteBuild", String(options.isRemoteBuild));
|
|
2132
|
+
if (options.profilePath) argv.push("--profilePath", path.resolve(options.profilePath));
|
|
2133
|
+
if (options.certificateName) argv.push("--certificateName", options.certificateName);
|
|
2134
|
+
if (options.p12Path) argv.push("--p12Path", path.resolve(options.p12Path));
|
|
2135
|
+
if (options.p12Password) argv.push("--p12Password", options.p12Password);
|
|
2136
|
+
if (options.tpnsProfilePath) argv.push("--tpnsProfilePath", path.resolve(options.tpnsProfilePath));
|
|
2137
|
+
if (options.isUploadBeta !== void 0) argv.push("--isUploadBeta", String(options.isUploadBeta));
|
|
2138
|
+
if (options.isUploadResourceBundle) argv.push("--isUploadResourceBundle");
|
|
2139
|
+
if (options.resourceBundleVersion) argv.push("--resourceBundleVersion", options.resourceBundleVersion);
|
|
2140
|
+
if (options.resourceBundleDesc) argv.push("--resourceBundleDesc", options.resourceBundleDesc);
|
|
2141
|
+
if (options.versionName) argv.push("--versionName", options.versionName);
|
|
2142
|
+
if (options.versionCode !== void 0) argv.push("--versionCode", String(options.versionCode));
|
|
2143
|
+
if (options.versionDesc) argv.push("--versionDesc", options.versionDesc);
|
|
2144
|
+
await runWechatCliCommand(argv);
|
|
2145
|
+
}
|
|
2146
|
+
//#endregion
|
|
2147
|
+
//#region src/cli/wechat-dispatch.ts
|
|
2148
|
+
function hasOption(argv, optionName) {
|
|
2149
|
+
return argv.includes(optionName) || argv.some((token) => token.startsWith(`${optionName}=`));
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* @description 优先将高频官方命令分发到 weapp-ide-cli 的稳定 helper,实现更清晰的程序化命令层。
|
|
2153
|
+
*/
|
|
2154
|
+
async function dispatchWechatCliCommand(argv) {
|
|
2155
|
+
const command = argv[0];
|
|
2156
|
+
if (!command) return false;
|
|
2157
|
+
if (command === "login") {
|
|
2158
|
+
await loginWechatIde({
|
|
2159
|
+
qrFormat: readOptionValue(argv, "--qr-format", "-f"),
|
|
2160
|
+
qrOutput: readOptionValue(argv, "--qr-output", "-o"),
|
|
2161
|
+
qrSize: readOptionValue(argv, "--qr-size"),
|
|
2162
|
+
resultOutput: readOptionValue(argv, "--result-output", "-r")
|
|
2163
|
+
});
|
|
2164
|
+
return true;
|
|
2165
|
+
}
|
|
2166
|
+
if (command === "open") {
|
|
2167
|
+
await openWechatIde({
|
|
2168
|
+
appid: readOptionValue(argv, "--appid"),
|
|
2169
|
+
extAppid: readOptionValue(argv, "--ext-appid"),
|
|
2170
|
+
platform: readOptionValue(argv, "--platform"),
|
|
2171
|
+
projectPath: readOptionValue(argv, "--project", "-p"),
|
|
2172
|
+
trustProject: hasOption(argv, "--trust-project")
|
|
2173
|
+
});
|
|
2174
|
+
return true;
|
|
2175
|
+
}
|
|
2176
|
+
if (command === "islogin") {
|
|
2177
|
+
await isWechatIdeLoggedIn();
|
|
2178
|
+
return true;
|
|
2179
|
+
}
|
|
2180
|
+
if (command === "build-npm") {
|
|
2181
|
+
await buildWechatIdeNpm({
|
|
2182
|
+
compileType: readOptionValue(argv, "--compile-type"),
|
|
2183
|
+
projectPath: readOptionValue(argv, "--project")
|
|
2184
|
+
});
|
|
2185
|
+
return true;
|
|
2186
|
+
}
|
|
2187
|
+
if (command === "preview") {
|
|
2188
|
+
await previewWechatIde({
|
|
2189
|
+
appid: readOptionValue(argv, "--appid"),
|
|
2190
|
+
compileCondition: readOptionValue(argv, "--compile-condition"),
|
|
2191
|
+
extAppid: readOptionValue(argv, "--ext-appid"),
|
|
2192
|
+
infoOutput: readOptionValue(argv, "--info-output", "-i"),
|
|
2193
|
+
projectPath: readOptionValue(argv, "--project", "-p"),
|
|
2194
|
+
qrFormat: readOptionValue(argv, "--qr-format", "-f"),
|
|
2195
|
+
qrOutput: readOptionValue(argv, "--qr-output", "-o"),
|
|
2196
|
+
qrSize: readOptionValue(argv, "--qr-size")
|
|
2197
|
+
});
|
|
2198
|
+
return true;
|
|
2199
|
+
}
|
|
2200
|
+
if (command === "auto-preview") {
|
|
2201
|
+
await autoPreviewWechatIde({
|
|
2202
|
+
appid: readOptionValue(argv, "--appid"),
|
|
2203
|
+
compileCondition: readOptionValue(argv, "--compile-condition"),
|
|
2204
|
+
extAppid: readOptionValue(argv, "--ext-appid"),
|
|
2205
|
+
infoOutput: readOptionValue(argv, "--info-output", "-i"),
|
|
2206
|
+
projectPath: readOptionValue(argv, "--project", "-p")
|
|
2207
|
+
});
|
|
2208
|
+
return true;
|
|
2209
|
+
}
|
|
2210
|
+
if (command === "auto") {
|
|
2211
|
+
await autoWechatIde({
|
|
2212
|
+
account: readOptionValue(argv, "--auto-account"),
|
|
2213
|
+
appid: readOptionValue(argv, "--appid"),
|
|
2214
|
+
extAppid: readOptionValue(argv, "--ext-appid"),
|
|
2215
|
+
port: readOptionValue(argv, "--auto-port"),
|
|
2216
|
+
projectPath: readOptionValue(argv, "--project", "-p"),
|
|
2217
|
+
testTicket: readOptionValue(argv, "--test-ticket"),
|
|
2218
|
+
ticket: readOptionValue(argv, "--ticket"),
|
|
2219
|
+
trustProject: hasOption(argv, "--trust-project")
|
|
2220
|
+
});
|
|
2221
|
+
return true;
|
|
2222
|
+
}
|
|
2223
|
+
if (command === "auto-replay") {
|
|
2224
|
+
await autoReplayWechatIde({
|
|
2225
|
+
account: readOptionValue(argv, "--auto-account"),
|
|
2226
|
+
appid: readOptionValue(argv, "--appid"),
|
|
2227
|
+
extAppid: readOptionValue(argv, "--ext-appid"),
|
|
2228
|
+
port: readOptionValue(argv, "--auto-port"),
|
|
2229
|
+
projectPath: readOptionValue(argv, "--project", "-p"),
|
|
2230
|
+
replayAll: hasOption(argv, "--replay-all"),
|
|
2231
|
+
replayConfigPath: readOptionValue(argv, "--replay-config-path"),
|
|
2232
|
+
testTicket: readOptionValue(argv, "--test-ticket"),
|
|
2233
|
+
ticket: readOptionValue(argv, "--ticket"),
|
|
2234
|
+
trustProject: hasOption(argv, "--trust-project")
|
|
2235
|
+
});
|
|
2236
|
+
return true;
|
|
2237
|
+
}
|
|
2238
|
+
if (command === "upload") {
|
|
2239
|
+
const version = readOptionValue(argv, "--version", "-v");
|
|
2240
|
+
const desc = readOptionValue(argv, "--desc", "-d");
|
|
2241
|
+
if (!version || !desc) return false;
|
|
2242
|
+
await uploadWechatIde({
|
|
2243
|
+
appid: readOptionValue(argv, "--appid"),
|
|
2244
|
+
desc,
|
|
2245
|
+
extAppid: readOptionValue(argv, "--ext-appid"),
|
|
2246
|
+
infoOutput: readOptionValue(argv, "--info-output", "-i"),
|
|
2247
|
+
projectPath: readOptionValue(argv, "--project", "-p"),
|
|
2248
|
+
version
|
|
2249
|
+
});
|
|
2250
|
+
return true;
|
|
2251
|
+
}
|
|
2252
|
+
if (command === "build-apk") {
|
|
2253
|
+
const output = readOptionValue(argv, "--output", "-o");
|
|
2254
|
+
const keyStore = readOptionValue(argv, "--key-store", "--keyStore", "-ks");
|
|
2255
|
+
const keyAlias = readOptionValue(argv, "--key-alias", "--keyAlias", "-ka");
|
|
2256
|
+
const keyPass = readOptionValue(argv, "--key-pass", "--keyPass", "-kp");
|
|
2257
|
+
const storePass = readOptionValue(argv, "--store-pass", "--storePass", "-sp");
|
|
2258
|
+
if (!output || !keyStore || !keyAlias || !keyPass || !storePass) return false;
|
|
2259
|
+
await buildWechatIdeApk({
|
|
2260
|
+
desc: readOptionValue(argv, "--desc", "-d"),
|
|
2261
|
+
isUploadResourceBundle: hasOption(argv, "--isUploadResourceBundle"),
|
|
2262
|
+
keyAlias,
|
|
2263
|
+
keyPass,
|
|
2264
|
+
keyStore,
|
|
2265
|
+
output,
|
|
2266
|
+
resourceBundleDesc: readOptionValue(argv, "--resourceBundleDesc"),
|
|
2267
|
+
resourceBundleVersion: readOptionValue(argv, "--resourceBundleVersion"),
|
|
2268
|
+
storePass,
|
|
2269
|
+
useAab: readBooleanOption(argv, "--use-aab", "--useAab", "-u")
|
|
2270
|
+
});
|
|
2271
|
+
return true;
|
|
2272
|
+
}
|
|
2273
|
+
if (command === "build-ipa") {
|
|
2274
|
+
const output = readOptionValue(argv, "--output", "-o");
|
|
2275
|
+
const isDistribute = readBooleanOption(argv, "--isDistribute");
|
|
2276
|
+
if (!output || isDistribute === void 0) return false;
|
|
2277
|
+
await buildWechatIdeIpa({
|
|
2278
|
+
certificateName: readOptionValue(argv, "--certificateName"),
|
|
2279
|
+
isDistribute,
|
|
2280
|
+
isRemoteBuild: readBooleanOption(argv, "--isRemoteBuild") ?? false,
|
|
2281
|
+
isUploadBeta: readBooleanOption(argv, "--isUploadBeta") ?? false,
|
|
2282
|
+
isUploadResourceBundle: hasOption(argv, "--isUploadResourceBundle"),
|
|
2283
|
+
output,
|
|
2284
|
+
p12Password: readOptionValue(argv, "--p12Password"),
|
|
2285
|
+
p12Path: readOptionValue(argv, "--p12Path"),
|
|
2286
|
+
profilePath: readOptionValue(argv, "--profilePath"),
|
|
2287
|
+
resourceBundleDesc: readOptionValue(argv, "--resourceBundleDesc"),
|
|
2288
|
+
resourceBundleVersion: readOptionValue(argv, "--resourceBundleVersion"),
|
|
2289
|
+
tpnsProfilePath: readOptionValue(argv, "--tpnsProfilePath"),
|
|
2290
|
+
versionCode: readOptionValue(argv, "--versionCode") ? Number(readOptionValue(argv, "--versionCode")) : void 0,
|
|
2291
|
+
versionDesc: readOptionValue(argv, "--versionDesc"),
|
|
2292
|
+
versionName: readOptionValue(argv, "--versionName")
|
|
2293
|
+
});
|
|
2294
|
+
return true;
|
|
2295
|
+
}
|
|
2296
|
+
if (command === "close") {
|
|
2297
|
+
await closeWechatIdeProject();
|
|
2298
|
+
return true;
|
|
2299
|
+
}
|
|
2300
|
+
if (command === "quit") {
|
|
2301
|
+
await quitWechatIde();
|
|
2302
|
+
return true;
|
|
2303
|
+
}
|
|
2304
|
+
if (command === "cache") {
|
|
2305
|
+
const clean = readOptionValue(argv, "--clean", "-c");
|
|
2306
|
+
if (!clean) return false;
|
|
2307
|
+
await clearWechatIdeCache({ clean });
|
|
2308
|
+
return true;
|
|
2309
|
+
}
|
|
2310
|
+
if (command === "open-other" && !hasOption(argv, "--project") && !hasOption(argv, "-p")) {
|
|
2311
|
+
await openWechatIdeOtherProject();
|
|
2312
|
+
return true;
|
|
2313
|
+
}
|
|
2314
|
+
if (command === "reset-fileutils") {
|
|
2315
|
+
const projectPath = readOptionValue(argv, "--project", "-p");
|
|
2316
|
+
if (!projectPath) return false;
|
|
2317
|
+
await resetWechatIdeFileUtils({ projectPath });
|
|
2318
|
+
return true;
|
|
2319
|
+
}
|
|
2320
|
+
if (command === "engine" && argv[1] === "build") {
|
|
2321
|
+
const projectPath = argv[2];
|
|
2322
|
+
if (!projectPath) return false;
|
|
2323
|
+
await runWechatIdeEngineBuild(projectPath, { logPath: readOptionValue(argv, "--logPath", "-l") });
|
|
2324
|
+
return true;
|
|
2325
|
+
}
|
|
2326
|
+
return false;
|
|
1431
2327
|
}
|
|
1432
2328
|
//#endregion
|
|
1433
2329
|
//#region src/cli/run.ts
|
|
@@ -1480,11 +2376,6 @@ async function handleHelpCommand(args) {
|
|
|
1480
2376
|
}
|
|
1481
2377
|
createCli().outputHelp();
|
|
1482
2378
|
}
|
|
1483
|
-
async function handleMissingCliPath(source) {
|
|
1484
|
-
const message = source === "custom" ? i18nText("在当前自定义路径中未找到微信web开发者命令行工具,请重新指定路径。", "Cannot find Wechat Web DevTools CLI in custom path, please reconfigure it.") : i18nText(`未检测到微信web开发者命令行工具,请执行 ${colors.bold(colors.green("weapp-ide-cli config"))} 指定路径。`, `Wechat Web DevTools CLI not found, please run ${colors.bold(colors.green("weapp-ide-cli config"))} to configure it.`);
|
|
1485
|
-
logger_default.warn(message);
|
|
1486
|
-
await promptForCliPath();
|
|
1487
|
-
}
|
|
1488
2379
|
/**
|
|
1489
2380
|
* @description 判断 open 指令是否应转发到 minidev。
|
|
1490
2381
|
*/
|
|
@@ -1502,30 +2393,6 @@ function createMinidevOpenArgv(argv) {
|
|
|
1502
2393
|
nextArgv[0] = "ide";
|
|
1503
2394
|
return removeOption(nextArgv, "--platform");
|
|
1504
2395
|
}
|
|
1505
|
-
function shouldBootstrapWechatDevtools(command) {
|
|
1506
|
-
return command === "open" || command === "auto" || command === "auto-preview";
|
|
1507
|
-
}
|
|
1508
|
-
function resolveBooleanCliOption(argv, optionName) {
|
|
1509
|
-
if (argv.includes(optionName)) return true;
|
|
1510
|
-
const rawValue = readOptionValue(argv, optionName);
|
|
1511
|
-
if (rawValue === void 0) return;
|
|
1512
|
-
const normalized = rawValue.trim().toLowerCase();
|
|
1513
|
-
if (normalized === "" || normalized === "true" || normalized === "1" || normalized === "on") return true;
|
|
1514
|
-
if (normalized === "false" || normalized === "0" || normalized === "off") return false;
|
|
1515
|
-
return true;
|
|
1516
|
-
}
|
|
1517
|
-
async function maybeBootstrapWechatDevtoolsSettings(argv) {
|
|
1518
|
-
const command = argv[0];
|
|
1519
|
-
if (!shouldBootstrapWechatDevtools(command)) return;
|
|
1520
|
-
const config = await readCustomConfig();
|
|
1521
|
-
if (config.autoBootstrapDevtools === false) return;
|
|
1522
|
-
const projectPath = readOptionValue(argv, "--project");
|
|
1523
|
-
const trustProjectOption = resolveBooleanCliOption(argv, "--trust-project");
|
|
1524
|
-
await bootstrapWechatDevtoolsSettings({
|
|
1525
|
-
projectPath,
|
|
1526
|
-
trustProject: trustProjectOption === void 0 ? config.autoTrustProject ?? false : trustProjectOption
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
2396
|
/**
|
|
1530
2397
|
* @description CLI 入口解析与分发。
|
|
1531
2398
|
*/
|
|
@@ -1560,18 +2427,9 @@ async function parse(argv) {
|
|
|
1560
2427
|
await runMinidev(createMinidevOpenArgv(formattedArgv));
|
|
1561
2428
|
return;
|
|
1562
2429
|
}
|
|
1563
|
-
if (!isOperatingSystemSupported(operatingSystemName)) {
|
|
1564
|
-
logger_default.warn(i18nText(`微信web开发者工具不支持当前平台:${operatingSystemName} !`, `Wechat Web DevTools CLI is not supported on current platform: ${operatingSystemName}!`));
|
|
1565
|
-
return;
|
|
1566
|
-
}
|
|
1567
2430
|
validateWechatCliCommandArgs(formattedArgv);
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
await handleMissingCliPath(source);
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
await maybeBootstrapWechatDevtoolsSettings(formattedArgv);
|
|
1574
|
-
await runWechatCliWithRetry(cliPath, formattedArgv);
|
|
2431
|
+
if (await dispatchWechatCliCommand(formattedArgv)) return;
|
|
2432
|
+
await runWechatCliCommand(formattedArgv);
|
|
1575
2433
|
}
|
|
1576
2434
|
//#endregion
|
|
1577
|
-
export {
|
|
2435
|
+
export { resetWechatIdeFileUtilsByHttp as $, RETRY_CANCEL_KEYS as A, waitForRetryKeypress as B, refreshWechatIdeTicket as C, validateWechatCliCommandArgs as D, uploadWechatIde as E, formatRetryHotkeyPrompt as F, transformArgv as G, execute as H, formatWechatIdeLoginRequiredError as I, runWechatIdeEngineBuildByHttp as J, startForwardConsole as K, isWechatIdeLoginRequiredError as L, RETRY_PROMPT_INITIAL_IGNORE_MS as M, createWechatIdeLoginRequiredExitError as N, runWechatCliWithRetry as O, extractExecutionErrorText as P, requestWechatDevtoolsHttp as Q, promptRetryKeypress as R, quitWechatIde as S, removeOption as St, setWechatIdeTicket as T, createAlias as U, runMinidev as V, createPathCompat as W, openWechatIdeProjectByHttp as X, WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES as Y, pollWechatIdeEngineBuildResultByHttp as Z, isWechatIdeLoggedIn as _, parseCompareArgs as _t, autoReplayWechatIde as a, waitForExclusiveKeypress as at, openWechatIdeOtherProject as b, readBooleanOption as bt, buildWechatIdeIpa as c, WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES as ct, clearWechatIdeCacheByAutomator as d, AUTOMATOR_COMMAND_NAMES as dt, startWechatIdeEngineBuildByHttp as et, closeWechatIdeProject as f, getAutomatorCommandHelp as ft, getWechatIdeToolInfo as g, printScreenshotHelp as gt, getWechatIdeTicket as h, parseScreenshotArgs as ht, autoPreviewWechatIde as i, runWithSuspendedSharedInput as it, RETRY_CONFIRM_KEYS as j, runRetryableCommand as k, buildWechatIdeNpm as l, WECHAT_CLI_COMMAND_NAMES as lt, getWechatIdeTestAccounts as m, runAutomatorCommand as mt, parse as n, promptForCliPath as nt, autoWechatIde as o, CONFIG_COMMAND_NAME as ot, compileWechatIdeByAutomator as p, isAutomatorCommand as pt, runWechatIdeEngineBuild as q, dispatchWechatCliCommand as r, createSharedInputSession as rt, buildWechatIdeApk as s, MINIDEV_NAMESPACE_COMMAND_NAMES as st, createCli as t, handleConfigCommand as tt, clearWechatIdeCache as u, isWeappIdeTopLevelCommand as ut, loginWechatIde as v, printCompareHelp as vt, resetWechatIdeFileUtils as w, previewWechatIde as x, readOptionValue as xt, openWechatIde as y, parseAutomatorArgs as yt, promptWechatIdeLoginRetry as z };
|