weapp-vite 6.15.13 → 6.15.15
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 +40 -0
- package/bin/bootstrap.js +34 -0
- package/bin/bootstrap.test.ts +31 -2
- package/dist/auto-import-components/resolvers.mjs +2 -0
- package/dist/auto-routes.mjs +1 -1
- package/dist/cli.mjs +852 -222
- package/dist/{config-DzoHnvv-.d.mts → config-BEJTp6sp.d.mts} +58 -9
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +3 -3
- package/dist/{createContext-CBnOZ_FN.mjs → createContext-Bwdmz5ML.mjs} +1324 -14500
- package/dist/docs/README.md +40 -0
- package/dist/file-C7J-GBEl.mjs +2 -0
- package/dist/{file-CycxGM0E.mjs → file-fMqSV4Z9.mjs} +1 -1
- package/dist/getInstance-C_LndkCo.mjs +2 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +4 -4
- package/dist/json.d.mts +1 -1
- package/dist/json.mjs +1 -1
- package/dist/mcp.d.mts +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +13 -13
- package/dist/file-BbtjekFm.mjs +0 -2
- package/dist/getInstance-BN1eXe7O.mjs +0 -2
- /package/dist/{config-B2xtjEug.mjs → config-DJjSbpNX.mjs} +0 -0
- /package/dist/{json-wnfVS9jE.mjs → json-D0HkutE0.mjs} +0 -0
- /package/dist/{pluginHost-BzPJL4F-.mjs → pluginHost-SJdl15d3.mjs} +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as isPathInside, S as shouldPassPlatformArgToIdeOpen, _ as createCjsConfigLoadError, b as normalizeMiniPlatform, c as createSharedBuildConfig, f as resolveWeappConfigFile, g as parseCommentJson, h as loadViteConfigFile, l as SHARED_CHUNK_VIRTUAL_PREFIX, m as getProjectConfigFileName, n as syncProjectSupportFiles, p as checkRuntime, r as syncManagedTsconfigBootstrapFiles, s as formatBytes, t as createCompilerContext, u as resolveHmrProfileJsonPath, v as DEFAULT_MP_PLATFORM, x as resolveMiniPlatform, y as getDefaultIdeProjectRoot } from "./createContext-Bwdmz5ML.mjs";
|
|
2
2
|
import { r as logger_default, t as colors } from "./logger-CgxdNjvb.mjs";
|
|
3
|
-
import { h as VERSION } from "./file-
|
|
3
|
+
import { h as VERSION } from "./file-fMqSV4Z9.mjs";
|
|
4
4
|
import { a as resolveWeappMcpConfig, o as startWeappViteMcpServer } from "./mcp-DRlj32v4.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import path, { posix } from "pathe";
|
|
@@ -15,14 +15,113 @@ import { execFile } from "node:child_process";
|
|
|
15
15
|
import { Buffer } from "node:buffer";
|
|
16
16
|
import { cac } from "cac";
|
|
17
17
|
import { resolveCommand } from "package-manager-detector/commands";
|
|
18
|
+
import { RETRY_CANCEL_KEYS, RETRY_CONFIRM_KEYS, bootstrapWechatDevtoolsSettings, buildWechatIdeNpm, clearWechatIdeCache, clearWechatIdeCacheByAutomator, closeSharedMiniProgram, closeWechatIdeProject, compileWechatIdeByAutomator, connectOpenedAutomator, createSharedInputSession, dispatchWechatCliCommand, formatAutomatorLoginError, getConfig, getWechatIdeTestAccounts, getWechatIdeTicket, getWechatIdeToolInfo, isAutomatorLoginError, isWeappIdeTopLevelCommand, isWechatIdeLoginRequiredError, launchAutomator, openWechatIdeProjectByHttp, parse, promptRetryKeypress, promptWechatIdeLoginRetry, quitWechatIde, refreshWechatIdeTicket, resetWechatIdeFileUtilsByHttp, runRetryableCommand, runWechatIdeEngineBuild, runWithSuspendedSharedInput, setWechatIdeTicket, startForwardConsole, takeScreenshot } from "weapp-ide-cli";
|
|
18
19
|
import { promisify } from "node:util";
|
|
19
|
-
import { bootstrapWechatDevtoolsSettings, closeSharedMiniProgram, connectOpenedAutomator, formatAutomatorLoginError, formatRetryHotkeyPrompt, formatWechatIdeLoginRequiredError, getConfig, isAutomatorLoginError, isWeappIdeTopLevelCommand, isWechatIdeLoginRequiredError, launchAutomator, parse, startForwardConsole, takeScreenshot, waitForRetryKeypress } from "weapp-ide-cli";
|
|
20
20
|
import { generateJs, generateJson, generateWxml, generateWxss } from "@weapp-core/schematics";
|
|
21
21
|
import { determineAgent } from "@vercel/detect-agent";
|
|
22
22
|
import { initConfig } from "@weapp-core/init";
|
|
23
23
|
import { createInterface } from "node:readline/promises";
|
|
24
24
|
import { clearTimeout, setTimeout as setTimeout$1 } from "node:timers";
|
|
25
|
-
|
|
25
|
+
//#region src/analyze/hmr.ts
|
|
26
|
+
function createMetricSummary(values) {
|
|
27
|
+
if (!values.length) return { count: 0 };
|
|
28
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
29
|
+
return {
|
|
30
|
+
count: values.length,
|
|
31
|
+
averageMs: total / values.length,
|
|
32
|
+
maxMs: Math.max(...values)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function sortCountEntries(map) {
|
|
36
|
+
return [...map.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).map(([name, count]) => ({
|
|
37
|
+
name,
|
|
38
|
+
count
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
function collectCounts(target, values) {
|
|
42
|
+
for (const value of values ?? []) {
|
|
43
|
+
if (!value) continue;
|
|
44
|
+
target.set(value, (target.get(value) ?? 0) + 1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function isFiniteNumber$1(value) {
|
|
48
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @description 聚合 HMR JSONL profile,为命令行与后续仪表盘复用。
|
|
52
|
+
*/
|
|
53
|
+
async function analyzeHmrProfile(options) {
|
|
54
|
+
const lines = (await fs.readFile(options.profilePath, "utf8")).split(/\r?\n/);
|
|
55
|
+
const samples = [];
|
|
56
|
+
let skippedLineCount = 0;
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
const trimmed = line.trim();
|
|
59
|
+
if (!trimmed) continue;
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(trimmed);
|
|
62
|
+
if (!isFiniteNumber$1(parsed.totalMs)) {
|
|
63
|
+
skippedLineCount += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
samples.push(parsed);
|
|
67
|
+
} catch {
|
|
68
|
+
skippedLineCount += 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const eventCounts = /* @__PURE__ */ new Map();
|
|
72
|
+
const dirtyReasonCounts = /* @__PURE__ */ new Map();
|
|
73
|
+
const pendingReasonCounts = /* @__PURE__ */ new Map();
|
|
74
|
+
const totalValues = [];
|
|
75
|
+
const buildCoreValues = [];
|
|
76
|
+
const transformValues = [];
|
|
77
|
+
const writeValues = [];
|
|
78
|
+
const watchToDirtyValues = [];
|
|
79
|
+
const emitValues = [];
|
|
80
|
+
const sharedChunkValues = [];
|
|
81
|
+
for (const sample of samples) {
|
|
82
|
+
totalValues.push(sample.totalMs);
|
|
83
|
+
if (sample.event) eventCounts.set(sample.event, (eventCounts.get(sample.event) ?? 0) + 1);
|
|
84
|
+
if (isFiniteNumber$1(sample.buildCoreMs)) buildCoreValues.push(sample.buildCoreMs);
|
|
85
|
+
if (isFiniteNumber$1(sample.transformMs)) transformValues.push(sample.transformMs);
|
|
86
|
+
if (isFiniteNumber$1(sample.writeMs)) writeValues.push(sample.writeMs);
|
|
87
|
+
if (isFiniteNumber$1(sample.watchToDirtyMs)) watchToDirtyValues.push(sample.watchToDirtyMs);
|
|
88
|
+
if (isFiniteNumber$1(sample.emitMs)) emitValues.push(sample.emitMs);
|
|
89
|
+
if (isFiniteNumber$1(sample.sharedChunkResolveMs)) sharedChunkValues.push(sample.sharedChunkResolveMs);
|
|
90
|
+
collectCounts(dirtyReasonCounts, sample.dirtyReasonSummary);
|
|
91
|
+
collectCounts(pendingReasonCounts, sample.pendingReasonSummary);
|
|
92
|
+
}
|
|
93
|
+
const orderedByTime = [...samples].sort((left, right) => {
|
|
94
|
+
const leftTime = typeof left.timestamp === "string" ? Date.parse(left.timestamp) : NaN;
|
|
95
|
+
const rightTime = typeof right.timestamp === "string" ? Date.parse(right.timestamp) : NaN;
|
|
96
|
+
if (Number.isFinite(leftTime) && Number.isFinite(rightTime)) return leftTime - rightTime;
|
|
97
|
+
return 0;
|
|
98
|
+
});
|
|
99
|
+
const slowestSamples = [...samples].sort((left, right) => (right.totalMs ?? 0) - (left.totalMs ?? 0)).slice(0, options.topSlowest ?? 5);
|
|
100
|
+
return {
|
|
101
|
+
runtime: "mini",
|
|
102
|
+
kind: "hmr-profile",
|
|
103
|
+
generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
104
|
+
profilePath: options.profilePath,
|
|
105
|
+
sampleCount: samples.length,
|
|
106
|
+
skippedLineCount,
|
|
107
|
+
firstTimestamp: orderedByTime[0]?.timestamp,
|
|
108
|
+
lastTimestamp: orderedByTime.at(-1)?.timestamp,
|
|
109
|
+
metrics: {
|
|
110
|
+
totalMs: createMetricSummary(totalValues),
|
|
111
|
+
buildCoreMs: createMetricSummary(buildCoreValues),
|
|
112
|
+
transformMs: createMetricSummary(transformValues),
|
|
113
|
+
writeMs: createMetricSummary(writeValues),
|
|
114
|
+
watchToDirtyMs: createMetricSummary(watchToDirtyValues),
|
|
115
|
+
emitMs: createMetricSummary(emitValues),
|
|
116
|
+
sharedChunkResolveMs: createMetricSummary(sharedChunkValues)
|
|
117
|
+
},
|
|
118
|
+
events: sortCountEntries(eventCounts),
|
|
119
|
+
dirtyReasons: sortCountEntries(dirtyReasonCounts),
|
|
120
|
+
pendingReasons: sortCountEntries(pendingReasonCounts),
|
|
121
|
+
slowestSamples
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
26
125
|
//#region src/analyze/subpackages/classifier.ts
|
|
27
126
|
const VIRTUAL_MODULE_INDICATOR = "\0";
|
|
28
127
|
const VIRTUAL_PREFIX = `${SHARED_CHUNK_VIRTUAL_PREFIX}/`;
|
|
@@ -810,8 +909,42 @@ async function writeAnalyzeResult(result, outputOption, configService) {
|
|
|
810
909
|
logger_default.success(`分析结果已写入 ${colors.green(relativeOutput)}`);
|
|
811
910
|
return resolvedOutputPath;
|
|
812
911
|
}
|
|
912
|
+
function formatMetricSummary(label, metric) {
|
|
913
|
+
if (!metric.count || metric.averageMs === void 0 || metric.maxMs === void 0) return;
|
|
914
|
+
return `${label} avg ${metric.averageMs.toFixed(2)} ms,max ${metric.maxMs.toFixed(2)} ms`;
|
|
915
|
+
}
|
|
916
|
+
function formatCountItems(items, limit = 5) {
|
|
917
|
+
return items.slice(0, limit).map((item) => `${item.name} x${item.count}`).join(",");
|
|
918
|
+
}
|
|
919
|
+
function printHmrProfileAnalysisSummary(result, configService) {
|
|
920
|
+
logger_default.success("HMR profile 分析完成");
|
|
921
|
+
logger_default.info(`- profile:${colors.green(configService.relativeCwd(result.profilePath))}`);
|
|
922
|
+
logger_default.info(`- 样本:${result.sampleCount} 条`);
|
|
923
|
+
if (result.firstTimestamp && result.lastTimestamp) logger_default.info(`- 时间范围:${result.firstTimestamp} -> ${result.lastTimestamp}`);
|
|
924
|
+
const totalSummary = formatMetricSummary("total", result.metrics.totalMs);
|
|
925
|
+
const watchSummary = formatMetricSummary("watch->dirty", result.metrics.watchToDirtyMs);
|
|
926
|
+
const emitSummary = formatMetricSummary("emit", result.metrics.emitMs);
|
|
927
|
+
const sharedSummary = formatMetricSummary("shared", result.metrics.sharedChunkResolveMs);
|
|
928
|
+
for (const summary of [
|
|
929
|
+
totalSummary,
|
|
930
|
+
watchSummary,
|
|
931
|
+
emitSummary,
|
|
932
|
+
sharedSummary
|
|
933
|
+
]) if (summary) logger_default.info(`- ${summary}`);
|
|
934
|
+
if (result.events.length) logger_default.info(`- 事件分布:${formatCountItems(result.events)}`);
|
|
935
|
+
if (result.dirtyReasons.length) logger_default.info(`- 主要 dirty 原因:${formatCountItems(result.dirtyReasons)}`);
|
|
936
|
+
if (result.pendingReasons.length) logger_default.info(`- 主要 pending 原因:${formatCountItems(result.pendingReasons)}`);
|
|
937
|
+
if (result.skippedLineCount > 0) logger_default.warn(`- 跳过 ${result.skippedLineCount} 条无法解析的 profile 记录`);
|
|
938
|
+
if (result.slowestSamples.length) {
|
|
939
|
+
logger_default.info("- 最慢样本:");
|
|
940
|
+
for (const sample of result.slowestSamples.slice(0, 3)) {
|
|
941
|
+
const fileLabel = sample.file ? configService.relativeCwd(sample.file) : "(unknown)";
|
|
942
|
+
logger_default.info(` - ${sample.totalMs?.toFixed(2) ?? "0.00"} ms,${sample.event ?? "unknown"},${fileLabel}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
813
946
|
function registerAnalyzeCommand(cli) {
|
|
814
|
-
cli.command("analyze [root]", "analyze 两端包体与源码映射").option("--json", `[boolean] 输出 JSON 结果`).option("--output <file>", `[string] 将分析结果写入指定文件(JSON)`).option("-p, --platform <platform>", `[string] target platform (weapp | h5)`).option("--project-config <path>", `[string] project config path (miniprogram only)`).action(async (root, options) => {
|
|
947
|
+
cli.command("analyze [root]", "analyze 两端包体与源码映射").option("--hmr-profile [file]", `[string | boolean] 分析 HMR JSONL profile,省略值时优先读取配置,否则回退到默认路径`).option("--json", `[boolean] 输出 JSON 结果`).option("--output <file>", `[string] 将分析结果写入指定文件(JSON)`).option("-p, --platform <platform>", `[string] target platform (weapp | h5)`).option("--project-config <path>", `[string] project config path (miniprogram only)`).action(async (root, options) => {
|
|
815
948
|
filterDuplicateOptions(options);
|
|
816
949
|
const configFile = resolveConfigFile(options);
|
|
817
950
|
const outputJson = coerceBooleanOption(options.json);
|
|
@@ -831,6 +964,21 @@ function registerAnalyzeCommand(cli) {
|
|
|
831
964
|
resolvedConfigPlatform: ctx.configService.platform
|
|
832
965
|
});
|
|
833
966
|
const outputOption = typeof options.output === "string" ? options.output.trim() : "";
|
|
967
|
+
if (options.hmrProfile !== void 0 && options.hmrProfile !== false) {
|
|
968
|
+
const profileOption = typeof options.hmrProfile === "string" && options.hmrProfile.trim() ? options.hmrProfile.trim() : ctx.configService.weappViteConfig.hmr?.profileJson;
|
|
969
|
+
const profilePath = resolveHmrProfileJsonPath({
|
|
970
|
+
cwd: ctx.configService.cwd,
|
|
971
|
+
option: profileOption,
|
|
972
|
+
fallbackToDefault: true
|
|
973
|
+
});
|
|
974
|
+
if (!profilePath) throw new Error("未找到可用的 HMR profile 文件路径");
|
|
975
|
+
const hmrProfileResult = await analyzeHmrProfile({ profilePath });
|
|
976
|
+
const writtenPath = await writeAnalyzeResult(hmrProfileResult, outputOption, ctx.configService);
|
|
977
|
+
if (outputJson) {
|
|
978
|
+
if (!writtenPath) process.stdout.write(`${JSON.stringify(hmrProfileResult, null, 2)}\n`);
|
|
979
|
+
} else printHmrProfileAnalysisSummary(hmrProfileResult, ctx.configService);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
834
982
|
if (targets.runWeb) {
|
|
835
983
|
const webResult = createWebAnalyzeResult(ctx.configService, { platform: targets.label === "web" ? "web" : "h5" });
|
|
836
984
|
const writtenPath = await writeAnalyzeResult(webResult, outputOption, ctx.configService);
|
|
@@ -952,42 +1100,133 @@ function logBuildPackageSizeReport(options) {
|
|
|
952
1100
|
}
|
|
953
1101
|
}
|
|
954
1102
|
//#endregion
|
|
955
|
-
//#region src/cli/openIde.ts
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1103
|
+
//#region src/cli/openIde/execute.ts
|
|
1104
|
+
function readArgOption(argv, ...names) {
|
|
1105
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1106
|
+
const current = argv[index];
|
|
1107
|
+
if (!names.includes(current)) continue;
|
|
1108
|
+
const next = argv[index + 1];
|
|
1109
|
+
if (typeof next === "string" && !next.startsWith("-")) return next;
|
|
1110
|
+
}
|
|
960
1111
|
}
|
|
961
|
-
async function
|
|
962
|
-
(
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1112
|
+
async function tryExecuteWechatIdeCliCommandByAutomator(argv, projectPath) {
|
|
1113
|
+
if (!projectPath) return false;
|
|
1114
|
+
const command = argv[0];
|
|
1115
|
+
if (!command) return false;
|
|
1116
|
+
if (command === "compile") {
|
|
1117
|
+
await compileWechatIdeByAutomator({ projectPath });
|
|
1118
|
+
return true;
|
|
1119
|
+
}
|
|
1120
|
+
if (command === "cache") {
|
|
1121
|
+
const cleanType = readArgOption(argv, "--clean", "-c");
|
|
1122
|
+
if (cleanType !== "compile" && cleanType !== "all") return false;
|
|
1123
|
+
await clearWechatIdeCacheByAutomator({
|
|
1124
|
+
clean: cleanType,
|
|
1125
|
+
projectPath
|
|
1126
|
+
});
|
|
1127
|
+
return true;
|
|
1128
|
+
}
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
async function tryExecuteWechatIdeCliCommandByHttp(argv, projectPath) {
|
|
1132
|
+
const command = argv[0];
|
|
1133
|
+
if (!command) return false;
|
|
1134
|
+
if (command === "compile") {
|
|
1135
|
+
if (!projectPath) return false;
|
|
1136
|
+
await openWechatIdeProjectByHttp(projectPath);
|
|
1137
|
+
return true;
|
|
1138
|
+
}
|
|
1139
|
+
if (command === "reset-fileutils") {
|
|
1140
|
+
if (!projectPath) return false;
|
|
1141
|
+
await resetWechatIdeFileUtilsByHttp(projectPath);
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1144
|
+
if (command === "engine" && argv[1] === "build") {
|
|
1145
|
+
const engineProjectPath = argv[2] || projectPath;
|
|
1146
|
+
if (!engineProjectPath) return false;
|
|
1147
|
+
await runWechatIdeEngineBuild(engineProjectPath, { logPath: readArgOption(argv, "--logPath", "-l") });
|
|
1148
|
+
return true;
|
|
1149
|
+
}
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
async function tryExecuteWechatIdeCliCommandByHelper(argv) {
|
|
1153
|
+
const command = argv[0];
|
|
1154
|
+
if (!command) return false;
|
|
1155
|
+
if (command === "close") {
|
|
1156
|
+
await closeWechatIdeProject();
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
if (command === "quit") {
|
|
1160
|
+
await quitWechatIde();
|
|
1161
|
+
return true;
|
|
1162
|
+
}
|
|
1163
|
+
if (command === "cache") {
|
|
1164
|
+
const cleanType = readArgOption(argv, "--clean", "-c");
|
|
1165
|
+
if (!cleanType) return false;
|
|
1166
|
+
await clearWechatIdeCache({ clean: cleanType });
|
|
1167
|
+
return true;
|
|
1168
|
+
}
|
|
1169
|
+
return false;
|
|
966
1170
|
}
|
|
967
1171
|
/**
|
|
968
|
-
* @description
|
|
1172
|
+
* @description 统一执行 weapp-ide-cli 命令,并在登录失效时复用同一套重试交互。
|
|
969
1173
|
*/
|
|
970
|
-
async function
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
logger_default.error(error);
|
|
978
|
-
return;
|
|
1174
|
+
async function executeWechatIdeCliCommand(argv, options = {}) {
|
|
1175
|
+
const { automatorMode = "prefer", cancelLevel = "warn", httpMode = "prefer", onNonLoginError, onRetry, projectPath } = options;
|
|
1176
|
+
await runWithSuspendedSharedInput(async () => {
|
|
1177
|
+
if (httpMode !== "skip") try {
|
|
1178
|
+
if (await tryExecuteWechatIdeCliCommandByHttp(argv, projectPath)) return;
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
if (httpMode === "require") throw error;
|
|
979
1181
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
logger_default.warn("已取消重试。完成登录后请重新执行当前命令。");
|
|
985
|
-
retrying = false;
|
|
986
|
-
continue;
|
|
1182
|
+
try {
|
|
1183
|
+
if (await tryExecuteWechatIdeCliCommandByAutomator(argv, projectPath)) return;
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
if (automatorMode === "require") throw error;
|
|
987
1186
|
}
|
|
988
|
-
|
|
989
|
-
|
|
1187
|
+
try {
|
|
1188
|
+
if (await tryExecuteWechatIdeCliCommandByHelper(argv)) return;
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
if (onNonLoginError) {
|
|
1191
|
+
onNonLoginError(error);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
throw error;
|
|
1195
|
+
}
|
|
1196
|
+
await runRetryableCommand({
|
|
1197
|
+
createCancelError: () => /* @__PURE__ */ new Error("cancelled"),
|
|
1198
|
+
execute: async () => {
|
|
1199
|
+
try {
|
|
1200
|
+
await parse(argv);
|
|
1201
|
+
return null;
|
|
1202
|
+
} catch (error) {
|
|
1203
|
+
if (!isWechatIdeLoginRequiredError(error)) {
|
|
1204
|
+
if (onNonLoginError) {
|
|
1205
|
+
onNonLoginError(error);
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
throw error;
|
|
1209
|
+
}
|
|
1210
|
+
return error;
|
|
1211
|
+
}
|
|
1212
|
+
},
|
|
1213
|
+
isRetryableResult: (result) => result !== null,
|
|
1214
|
+
onCancel: () => {},
|
|
1215
|
+
onRetry: () => {
|
|
1216
|
+
onRetry?.();
|
|
1217
|
+
},
|
|
1218
|
+
promptRetry: async (error) => await promptWechatIdeLoginRetry({
|
|
1219
|
+
cancelLevel,
|
|
1220
|
+
error,
|
|
1221
|
+
logger: logger_default
|
|
1222
|
+
}),
|
|
1223
|
+
shouldRetry: (action) => action === "retry"
|
|
1224
|
+
});
|
|
1225
|
+
});
|
|
990
1226
|
}
|
|
1227
|
+
//#endregion
|
|
1228
|
+
//#region src/cli/openIde/close.ts
|
|
1229
|
+
const execFileAsync = promisify(execFile);
|
|
991
1230
|
async function closeIdeByAppleScript() {
|
|
992
1231
|
if (process.platform !== "darwin") return false;
|
|
993
1232
|
const appName = process.env.WEAPP_DEVTOOLS_APP_NAME || "wechatwebdevtools";
|
|
@@ -1009,29 +1248,21 @@ async function closeIdeByProcessKill(cliPath) {
|
|
|
1009
1248
|
}
|
|
1010
1249
|
}
|
|
1011
1250
|
/**
|
|
1012
|
-
* @description
|
|
1251
|
+
* @description 关闭微信开发者工具,并在 CLI 不可用时回退到系统级关闭。
|
|
1013
1252
|
*/
|
|
1014
|
-
function
|
|
1015
|
-
if (!mpDistRoot || !mpDistRoot.trim()) return;
|
|
1016
|
-
const parent = path.dirname(mpDistRoot);
|
|
1017
|
-
if (!parent || parent === "." || parent === "/") return;
|
|
1018
|
-
return parent;
|
|
1019
|
-
}
|
|
1020
|
-
/**
|
|
1021
|
-
* @description 结合 mpDistRoot 与配置根目录解析最终 IDE 项目目录。
|
|
1022
|
-
*/
|
|
1023
|
-
function resolveIdeProjectRoot(mpDistRoot, cwd) {
|
|
1024
|
-
return resolveIdeProjectPath(mpDistRoot) ?? cwd;
|
|
1025
|
-
}
|
|
1026
|
-
async function closeIde() {
|
|
1253
|
+
async function closeIde$1() {
|
|
1027
1254
|
const config = await getConfig();
|
|
1028
1255
|
const cliPath = config.cliPath?.trim() ? config.cliPath : null;
|
|
1029
1256
|
try {
|
|
1030
|
-
await
|
|
1257
|
+
await closeWechatIdeProject();
|
|
1031
1258
|
return true;
|
|
1032
1259
|
} catch (error) {
|
|
1033
1260
|
if (isWechatIdeLoginRequiredError(error)) try {
|
|
1034
|
-
await
|
|
1261
|
+
await executeWechatIdeCliCommand(["close"], {
|
|
1262
|
+
cancelLevel: "warn",
|
|
1263
|
+
onNonLoginError: (retryError) => logger_default.error(retryError),
|
|
1264
|
+
onRetry: () => logger_default.info("正在重试连接微信开发者工具...")
|
|
1265
|
+
});
|
|
1035
1266
|
return true;
|
|
1036
1267
|
} catch (retryError) {
|
|
1037
1268
|
logger_default.error(retryError);
|
|
@@ -1051,25 +1282,36 @@ async function closeIde() {
|
|
|
1051
1282
|
return false;
|
|
1052
1283
|
}
|
|
1053
1284
|
}
|
|
1285
|
+
//#endregion
|
|
1286
|
+
//#region src/cli/openIde/reuse.ts
|
|
1054
1287
|
function formatReuseOpenedWechatIdePrompt() {
|
|
1055
1288
|
return `目标项目已在微信开发者工具中打开,已跳过重复打开。按 ${colors.bold(colors.green("r"))} 关闭当前窗口后重新打开。`;
|
|
1056
1289
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1290
|
+
async function openWechatIdeByAutomator(projectPath) {
|
|
1291
|
+
(await launchAutomator({
|
|
1292
|
+
projectPath,
|
|
1293
|
+
trustProject: true
|
|
1294
|
+
})).disconnect();
|
|
1295
|
+
}
|
|
1296
|
+
async function connectOpenedProject(projectPath) {
|
|
1062
1297
|
try {
|
|
1063
|
-
|
|
1298
|
+
return await connectOpenedAutomator({
|
|
1064
1299
|
projectPath,
|
|
1065
1300
|
timeout: 3e3
|
|
1066
1301
|
});
|
|
1067
1302
|
} catch {
|
|
1068
1303
|
return null;
|
|
1069
1304
|
}
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* @description 若当前项目已在微信开发者工具中打开且自动化可连通,则直接复用现有会话,避免重复拉起 IDE。
|
|
1308
|
+
*/
|
|
1309
|
+
async function tryReuseOpenedWechatIde(projectPath, closeIde) {
|
|
1310
|
+
const miniProgram = await connectOpenedProject(projectPath);
|
|
1311
|
+
if (!miniProgram) return null;
|
|
1070
1312
|
miniProgram.disconnect();
|
|
1071
1313
|
logger_default.info(formatReuseOpenedWechatIdePrompt());
|
|
1072
|
-
if (await
|
|
1314
|
+
if (await promptRetryKeypress({ logger: logger_default }) !== "retry") return {
|
|
1073
1315
|
reopened: false,
|
|
1074
1316
|
reused: true
|
|
1075
1317
|
};
|
|
@@ -1081,22 +1323,113 @@ async function tryReuseOpenedWechatIde(projectPath) {
|
|
|
1081
1323
|
reused: false
|
|
1082
1324
|
};
|
|
1083
1325
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
});
|
|
1091
|
-
} catch {
|
|
1092
|
-
return false;
|
|
1093
|
-
}
|
|
1326
|
+
/**
|
|
1327
|
+
* @description 对已打开的目标项目执行强制重开,以刷新最新构建产物。
|
|
1328
|
+
*/
|
|
1329
|
+
async function reopenOpenedWechatIde(projectPath, closeIde) {
|
|
1330
|
+
const miniProgram = await connectOpenedProject(projectPath);
|
|
1331
|
+
if (!miniProgram) return false;
|
|
1094
1332
|
miniProgram.disconnect();
|
|
1095
1333
|
logger_default.info("目标项目已在微信开发者工具中打开,当前命令将主动重开以刷新最新构建产物。");
|
|
1096
1334
|
if (!await closeIde()) logger_default.warn("关闭当前微信开发者工具失败,仍继续尝试重新打开目标项目。");
|
|
1097
1335
|
await openWechatIdeByAutomator(projectPath);
|
|
1098
1336
|
return true;
|
|
1099
1337
|
}
|
|
1338
|
+
//#endregion
|
|
1339
|
+
//#region src/cli/openIde/index.ts
|
|
1340
|
+
function shouldLogAutomatorFallbackError() {
|
|
1341
|
+
const flag = process.env.WEAPP_VITE_DEBUG_AUTOMATOR_OPEN;
|
|
1342
|
+
return flag === "1" || flag === "true";
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* @description 执行 IDE 打开流程,并在登录失效时允许按键重试。
|
|
1346
|
+
*/
|
|
1347
|
+
async function runWechatIdeOpenWithRetry(argv) {
|
|
1348
|
+
await executeWechatIdeCliCommand(argv, {
|
|
1349
|
+
cancelLevel: "warn",
|
|
1350
|
+
onNonLoginError: (error) => logger_default.error(error),
|
|
1351
|
+
onRetry: () => {
|
|
1352
|
+
logger_default.info(colors.bold(colors.green("正在重试连接微信开发者工具...")));
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* @description 根据 mpDistRoot 推导 IDE 项目目录(目录内应包含 project/mini 配置)
|
|
1358
|
+
*/
|
|
1359
|
+
function resolveIdeProjectPath(mpDistRoot) {
|
|
1360
|
+
if (!mpDistRoot || !mpDistRoot.trim()) return;
|
|
1361
|
+
const parent = path.dirname(mpDistRoot);
|
|
1362
|
+
if (!parent || parent === "." || parent === "/") return;
|
|
1363
|
+
return parent;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* @description 结合 mpDistRoot 与配置根目录解析最终 IDE 项目目录。
|
|
1367
|
+
*/
|
|
1368
|
+
function resolveIdeProjectRoot(mpDistRoot, cwd) {
|
|
1369
|
+
return resolveIdeProjectPath(mpDistRoot) ?? cwd;
|
|
1370
|
+
}
|
|
1371
|
+
async function closeIde() {
|
|
1372
|
+
return await closeIde$1();
|
|
1373
|
+
}
|
|
1374
|
+
async function tryOpenWechatIdeByAutomator(projectPath, options) {
|
|
1375
|
+
if (options.reuseOpenedProject === false) {
|
|
1376
|
+
if (await reopenOpenedWechatIde(projectPath, closeIde)) return true;
|
|
1377
|
+
}
|
|
1378
|
+
const reuseResult = await tryReuseOpenedWechatIde(projectPath, closeIde);
|
|
1379
|
+
if (reuseResult?.reused || reuseResult?.reopened) return true;
|
|
1380
|
+
await openWechatIdeByAutomator(projectPath);
|
|
1381
|
+
return true;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* @description 打开后主动刷新微信开发者工具的项目索引,避免模拟器沿用过期 app 配置。
|
|
1385
|
+
*/
|
|
1386
|
+
async function stabilizeOpenedWechatIdeProject(projectPath, servicePortEnabled) {
|
|
1387
|
+
if (servicePortEnabled === false) return;
|
|
1388
|
+
try {
|
|
1389
|
+
await executeWechatIdeCliCommand(["compile"], {
|
|
1390
|
+
httpMode: "prefer",
|
|
1391
|
+
onNonLoginError: (error) => logger_default.error(error),
|
|
1392
|
+
projectPath
|
|
1393
|
+
});
|
|
1394
|
+
await executeWechatIdeCliCommand([
|
|
1395
|
+
"reset-fileutils",
|
|
1396
|
+
"-p",
|
|
1397
|
+
projectPath
|
|
1398
|
+
], {
|
|
1399
|
+
httpMode: "prefer",
|
|
1400
|
+
onNonLoginError: (error) => logger_default.error(error),
|
|
1401
|
+
projectPath
|
|
1402
|
+
});
|
|
1403
|
+
await executeWechatIdeCliCommand([
|
|
1404
|
+
"engine",
|
|
1405
|
+
"build",
|
|
1406
|
+
projectPath
|
|
1407
|
+
], {
|
|
1408
|
+
httpMode: "prefer",
|
|
1409
|
+
onNonLoginError: (error) => logger_default.error(error),
|
|
1410
|
+
projectPath
|
|
1411
|
+
});
|
|
1412
|
+
try {
|
|
1413
|
+
await executeWechatIdeCliCommand(["compile"], {
|
|
1414
|
+
automatorMode: "require",
|
|
1415
|
+
httpMode: "skip",
|
|
1416
|
+
projectPath
|
|
1417
|
+
});
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
if (shouldLogAutomatorFallbackError()) logger_default.error(error);
|
|
1420
|
+
}
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
logger_default.warn("刷新微信开发者工具项目索引失败,已保留当前打开状态;如模拟器仍显示旧状态,可手动刷新一次。");
|
|
1423
|
+
if (shouldLogAutomatorFallbackError()) logger_default.error(error);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
function createIdeOpenArgv(platform, projectPath, options = {}) {
|
|
1427
|
+
const argv = ["open", "-p"];
|
|
1428
|
+
if (projectPath) argv.push(projectPath);
|
|
1429
|
+
if (platform === "weapp" && options.trustProject !== false) argv.push("--trust-project");
|
|
1430
|
+
if (platform && shouldPassPlatformArgToIdeOpen(platform)) argv.push("--platform", platform);
|
|
1431
|
+
return argv;
|
|
1432
|
+
}
|
|
1100
1433
|
async function openIde(platform, projectPath, options = {}) {
|
|
1101
1434
|
let bootstrapResult;
|
|
1102
1435
|
if (platform === "weapp" && projectPath) try {
|
|
@@ -1110,13 +1443,10 @@ async function openIde(platform, projectPath, options = {}) {
|
|
|
1110
1443
|
}
|
|
1111
1444
|
if (platform === "weapp" && projectPath && bootstrapResult?.servicePortEnabled === false) logger_default.warn("检测到微信开发者工具服务端口当前处于关闭状态,已保留用户设置并回退到普通 open 流程。");
|
|
1112
1445
|
if (platform === "weapp" && projectPath && options.trustProject !== false && bootstrapResult?.servicePortEnabled !== false) try {
|
|
1113
|
-
if (
|
|
1114
|
-
|
|
1446
|
+
if (await tryOpenWechatIdeByAutomator(projectPath, options)) {
|
|
1447
|
+
await stabilizeOpenedWechatIdeProject(projectPath, bootstrapResult?.servicePortEnabled);
|
|
1448
|
+
return;
|
|
1115
1449
|
}
|
|
1116
|
-
const reuseResult = await tryReuseOpenedWechatIde(projectPath);
|
|
1117
|
-
if (reuseResult?.reused || reuseResult?.reopened) return;
|
|
1118
|
-
await openWechatIdeByAutomator(projectPath);
|
|
1119
|
-
return;
|
|
1120
1450
|
} catch (error) {
|
|
1121
1451
|
if (isAutomatorLoginError(error)) {
|
|
1122
1452
|
logger_default.error("检测到微信开发者工具登录状态失效,请先登录后重试。");
|
|
@@ -1125,11 +1455,8 @@ async function openIde(platform, projectPath, options = {}) {
|
|
|
1125
1455
|
logger_default.warn("通过 automator 启动微信开发者工具并自动信任项目失败,回退到普通 open 流程。");
|
|
1126
1456
|
if (shouldLogAutomatorFallbackError()) logger_default.error(error);
|
|
1127
1457
|
}
|
|
1128
|
-
|
|
1129
|
-
if (projectPath)
|
|
1130
|
-
if (platform === "weapp" && options.trustProject !== false) argv.push("--trust-project");
|
|
1131
|
-
if (platform && shouldPassPlatformArgToIdeOpen(platform)) argv.push("--platform", platform);
|
|
1132
|
-
await runWechatIdeOpenWithRetry(argv);
|
|
1458
|
+
await runWechatIdeOpenWithRetry(createIdeOpenArgv(platform, projectPath, options));
|
|
1459
|
+
if (platform === "weapp" && projectPath) await stabilizeOpenedWechatIdeProject(projectPath, bootstrapResult?.servicePortEnabled);
|
|
1133
1460
|
}
|
|
1134
1461
|
/**
|
|
1135
1462
|
* @description 解析 IDE 相关命令所需的平台、项目目录与配置上下文。
|
|
@@ -1149,6 +1476,7 @@ async function resolveIdeCommandContext(options) {
|
|
|
1149
1476
|
platform ??= ctx.configService.platform;
|
|
1150
1477
|
if (!projectPath) projectPath = resolveIdeProjectRoot(ctx.configService.mpDistRoot, ctx.configService.cwd);
|
|
1151
1478
|
return {
|
|
1479
|
+
cwd: ctx.configService.cwd,
|
|
1152
1480
|
platform,
|
|
1153
1481
|
projectPath,
|
|
1154
1482
|
weappViteConfig: ctx.configService.weappViteConfig,
|
|
@@ -1160,6 +1488,7 @@ async function resolveIdeCommandContext(options) {
|
|
|
1160
1488
|
if (defaultProjectRoot) projectPath = resolveIdeProjectRoot(defaultProjectRoot, cwd);
|
|
1161
1489
|
}
|
|
1162
1490
|
return {
|
|
1491
|
+
cwd,
|
|
1163
1492
|
platform,
|
|
1164
1493
|
projectPath
|
|
1165
1494
|
};
|
|
@@ -1586,6 +1915,79 @@ function sleep(ms) {
|
|
|
1586
1915
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1587
1916
|
}
|
|
1588
1917
|
//#endregion
|
|
1918
|
+
//#region src/cli/hmrProfileSummary.ts
|
|
1919
|
+
function isFiniteNumber(value) {
|
|
1920
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
1921
|
+
}
|
|
1922
|
+
function parseLatestHmrProfileSample(content) {
|
|
1923
|
+
const lines = content.split(/\r?\n/);
|
|
1924
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1925
|
+
const trimmed = lines[index]?.trim();
|
|
1926
|
+
if (!trimmed) continue;
|
|
1927
|
+
try {
|
|
1928
|
+
const parsed = JSON.parse(trimmed);
|
|
1929
|
+
if (isFiniteNumber(parsed.totalMs)) return parsed;
|
|
1930
|
+
} catch {
|
|
1931
|
+
continue;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
function formatPhaseHint(sample) {
|
|
1936
|
+
const topPhase = [
|
|
1937
|
+
{
|
|
1938
|
+
label: "build-core",
|
|
1939
|
+
value: sample.buildCoreMs
|
|
1940
|
+
},
|
|
1941
|
+
{
|
|
1942
|
+
label: "transform",
|
|
1943
|
+
value: sample.transformMs
|
|
1944
|
+
},
|
|
1945
|
+
{
|
|
1946
|
+
label: "watch->dirty",
|
|
1947
|
+
value: sample.watchToDirtyMs
|
|
1948
|
+
},
|
|
1949
|
+
{
|
|
1950
|
+
label: "emit",
|
|
1951
|
+
value: sample.emitMs
|
|
1952
|
+
},
|
|
1953
|
+
{
|
|
1954
|
+
label: "shared",
|
|
1955
|
+
value: sample.sharedChunkResolveMs
|
|
1956
|
+
},
|
|
1957
|
+
{
|
|
1958
|
+
label: "write",
|
|
1959
|
+
value: sample.writeMs
|
|
1960
|
+
}
|
|
1961
|
+
].filter((phase) => isFiniteNumber(phase.value)).sort((left, right) => (right.value ?? 0) - (left.value ?? 0))[0];
|
|
1962
|
+
if (!topPhase || (topPhase.value ?? 0) < 5) return;
|
|
1963
|
+
return `${topPhase.label} ${topPhase.value.toFixed(2)} ms`;
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* @description 读取最近一次 HMR profile,并格式化为 IDE 日志启动前的单行摘要。
|
|
1967
|
+
*/
|
|
1968
|
+
async function readLatestHmrProfileSummary(options) {
|
|
1969
|
+
const profilePath = resolveHmrProfileJsonPath({
|
|
1970
|
+
cwd: options.cwd,
|
|
1971
|
+
option: options.weappViteConfig?.hmr?.profileJson
|
|
1972
|
+
});
|
|
1973
|
+
if (!profilePath) return;
|
|
1974
|
+
const content = await fs.readFile(profilePath, "utf8").catch(() => void 0);
|
|
1975
|
+
if (!content) return;
|
|
1976
|
+
const sample = parseLatestHmrProfileSample(content);
|
|
1977
|
+
if (!sample || !isFiniteNumber(sample.totalMs)) return;
|
|
1978
|
+
const relativeCwd = options.relativeCwd ?? ((value) => value);
|
|
1979
|
+
const segments = [`[hmr] 最近一次热更新 ${sample.totalMs.toFixed(2)} ms`];
|
|
1980
|
+
if (sample.event) segments.push(sample.event);
|
|
1981
|
+
if (sample.file) segments.push(relativeCwd(sample.file));
|
|
1982
|
+
const phaseHint = formatPhaseHint(sample);
|
|
1983
|
+
if (phaseHint) segments.push(`主耗时 ${phaseHint}`);
|
|
1984
|
+
return {
|
|
1985
|
+
file: sample.file,
|
|
1986
|
+
profilePath,
|
|
1987
|
+
line: segments.join(",")
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
//#endregion
|
|
1589
1991
|
//#region src/cli/commands/ide.ts
|
|
1590
1992
|
async function waitForTermination(cleanup) {
|
|
1591
1993
|
await new Promise((resolve) => {
|
|
@@ -1608,11 +2010,15 @@ async function waitForTermination(cleanup) {
|
|
|
1608
2010
|
}
|
|
1609
2011
|
});
|
|
1610
2012
|
}
|
|
2013
|
+
function formatIdeOutput(data, options) {
|
|
2014
|
+
if (options.json) return JSON.stringify(data, null, 2);
|
|
2015
|
+
return JSON.stringify(data, null, 2);
|
|
2016
|
+
}
|
|
1611
2017
|
/**
|
|
1612
2018
|
* @description 执行 ide 子命令。
|
|
1613
2019
|
*/
|
|
1614
2020
|
async function runIdeCommand(action, root, options) {
|
|
1615
|
-
if (action !== "logs" && action !== "setup") throw new Error(`未知 ide 子命令: ${action ?? "(empty)"}`);
|
|
2021
|
+
if (action !== "logs" && action !== "setup" && action !== "info" && action !== "test-accounts" && action !== "ticket" && action !== "ticket:set" && action !== "ticket:refresh") throw new Error(`未知 ide 子命令: ${action ?? "(empty)"}`);
|
|
1616
2022
|
filterDuplicateOptions(options);
|
|
1617
2023
|
const configFile = resolveConfigFile(options);
|
|
1618
2024
|
const targets = resolveRuntimeTargets(options);
|
|
@@ -1634,6 +2040,35 @@ async function runIdeCommand(action, root, options) {
|
|
|
1634
2040
|
return;
|
|
1635
2041
|
}
|
|
1636
2042
|
if (options.open) await openIde(resolved.platform, resolved.projectPath, { trustProject: options.trustProject });
|
|
2043
|
+
if (action === "info") {
|
|
2044
|
+
const result = await getWechatIdeToolInfo({ projectPath: resolved.projectPath });
|
|
2045
|
+
logger_default.info(formatIdeOutput(result, options));
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
if (action === "test-accounts") {
|
|
2049
|
+
const result = await getWechatIdeTestAccounts({ projectPath: resolved.projectPath });
|
|
2050
|
+
logger_default.info(formatIdeOutput(result, options));
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
if (action === "ticket") {
|
|
2054
|
+
const result = await getWechatIdeTicket({ projectPath: resolved.projectPath });
|
|
2055
|
+
logger_default.info(formatIdeOutput(result, options));
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
if (action === "ticket:set") {
|
|
2059
|
+
if (!options.ticket) throw new Error("`weapp-vite ide ticket:set` 需要提供 --ticket。");
|
|
2060
|
+
await setWechatIdeTicket({
|
|
2061
|
+
projectPath: resolved.projectPath,
|
|
2062
|
+
ticket: options.ticket
|
|
2063
|
+
});
|
|
2064
|
+
logger_default.info(`已设置微信开发者工具 ticket:${options.ticket}`);
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
if (action === "ticket:refresh") {
|
|
2068
|
+
await refreshWechatIdeTicket({ projectPath: resolved.projectPath });
|
|
2069
|
+
logger_default.info("已刷新微信开发者工具 ticket。");
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
1637
2072
|
const forwardConsoleOptions = await resolveForwardConsoleOptions({
|
|
1638
2073
|
...resolved.weappViteConfig,
|
|
1639
2074
|
forwardConsole: resolved.weappViteConfig?.forwardConsole === false ? { enabled: true } : {
|
|
@@ -1641,6 +2076,12 @@ async function runIdeCommand(action, root, options) {
|
|
|
1641
2076
|
enabled: true
|
|
1642
2077
|
}
|
|
1643
2078
|
});
|
|
2079
|
+
const latestHmrSummary = await readLatestHmrProfileSummary({
|
|
2080
|
+
cwd: resolved.cwd ?? process.cwd(),
|
|
2081
|
+
relativeCwd: (value) => resolved.cwd ? value.replace(`${resolved.cwd}/`, "") : value,
|
|
2082
|
+
weappViteConfig: resolved.weappViteConfig
|
|
2083
|
+
});
|
|
2084
|
+
if (latestHmrSummary) logger_default.info(latestHmrSummary.line);
|
|
1644
2085
|
const session = await startForwardConsoleBridge({
|
|
1645
2086
|
projectPath: resolved.projectPath,
|
|
1646
2087
|
agentName: void 0,
|
|
@@ -1657,7 +2098,7 @@ async function runIdeCommand(action, root, options) {
|
|
|
1657
2098
|
* @description 注册 IDE 相关子命令。
|
|
1658
2099
|
*/
|
|
1659
2100
|
function registerIdeCommand(cli) {
|
|
1660
|
-
cli.command("ide [action] [root]", "
|
|
2101
|
+
cli.command("ide [action] [root]", "run Wechat DevTools utility actions and log bridge commands").option("-o, --open", "[boolean] open ide before attaching log bridge").option("-p, --platform <platform>", "[string] target platform (weapp | h5)").option("--project-config <path>", "[string] project config path (miniprogram only)").option("--ticket <value>", "[string] ticket used by `ide ticket:set`").option("--trust-project", "[boolean] auto trust Wechat DevTools project on open", { default: true }).action(async (action, root, options) => {
|
|
1661
2102
|
await runIdeCommand(action, root, options);
|
|
1662
2103
|
});
|
|
1663
2104
|
}
|
|
@@ -1979,16 +2420,18 @@ async function resolveHttpUrl(options) {
|
|
|
1979
2420
|
}
|
|
1980
2421
|
async function confirmWrite() {
|
|
1981
2422
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2423
|
+
return await runWithSuspendedSharedInput(async () => {
|
|
2424
|
+
const rl = createInterface({
|
|
2425
|
+
input: process.stdin,
|
|
2426
|
+
output: process.stdout
|
|
2427
|
+
});
|
|
2428
|
+
try {
|
|
2429
|
+
const normalized = (await rl.question("是否写入配置文件?(Y/n) ")).trim().toLowerCase();
|
|
2430
|
+
return normalized === "" || normalized === "y" || normalized === "yes";
|
|
2431
|
+
} finally {
|
|
2432
|
+
rl.close();
|
|
2433
|
+
}
|
|
1985
2434
|
});
|
|
1986
|
-
try {
|
|
1987
|
-
const normalized = (await rl.question("是否写入配置文件?(Y/n) ")).trim().toLowerCase();
|
|
1988
|
-
return normalized === "" || normalized === "y" || normalized === "yes";
|
|
1989
|
-
} finally {
|
|
1990
|
-
rl.close();
|
|
1991
|
-
}
|
|
1992
2435
|
}
|
|
1993
2436
|
function resolveClientTransport(transport) {
|
|
1994
2437
|
return transport === "http" ? "http" : "command";
|
|
@@ -2072,7 +2515,7 @@ function registerMcpCommand(cli) {
|
|
|
2072
2515
|
function registerNpmCommand(cli) {
|
|
2073
2516
|
cli.command("npm").alias("build:npm").alias("build-npm").action(async () => {
|
|
2074
2517
|
try {
|
|
2075
|
-
await
|
|
2518
|
+
await buildWechatIdeNpm({ projectPath: process.cwd() });
|
|
2076
2519
|
} catch (error) {
|
|
2077
2520
|
logger_default.error(error);
|
|
2078
2521
|
}
|
|
@@ -2085,13 +2528,19 @@ function registerOpenCommand(cli) {
|
|
|
2085
2528
|
filterDuplicateOptions(options);
|
|
2086
2529
|
const configFile = resolveConfigFile(options);
|
|
2087
2530
|
const targets = resolveRuntimeTargets(options);
|
|
2088
|
-
const { platform, projectPath, mpDistRoot } = await resolveIdeCommandContext({
|
|
2531
|
+
const { cwd, platform, projectPath, mpDistRoot, weappViteConfig } = await resolveIdeCommandContext({
|
|
2089
2532
|
configFile,
|
|
2090
2533
|
mode: options.mode ?? "development",
|
|
2091
2534
|
platform: targets.mpPlatform,
|
|
2092
2535
|
projectPath: root,
|
|
2093
2536
|
cliPlatform: targets.rawPlatform
|
|
2094
2537
|
});
|
|
2538
|
+
const latestHmrSummary = await readLatestHmrProfileSummary({
|
|
2539
|
+
cwd: cwd ?? process.cwd(),
|
|
2540
|
+
relativeCwd: (value) => cwd ? value.replace(`${cwd}/`, "") : value,
|
|
2541
|
+
weappViteConfig
|
|
2542
|
+
});
|
|
2543
|
+
if (latestHmrSummary) logger_default.info(latestHmrSummary.line);
|
|
2095
2544
|
await openIde(platform, projectPath ?? resolveIdeProjectRoot(mpDistRoot, process.cwd()), { trustProject: options.trustProject });
|
|
2096
2545
|
});
|
|
2097
2546
|
}
|
|
@@ -2114,6 +2563,7 @@ function registerPrepareCommand(cli) {
|
|
|
2114
2563
|
isDev: false,
|
|
2115
2564
|
mode: typeof options.mode === "string" ? options.mode : "development",
|
|
2116
2565
|
configFile: resolveConfigFile(options),
|
|
2566
|
+
configLoader: "native",
|
|
2117
2567
|
syncSupportFiles: false,
|
|
2118
2568
|
preloadAppEntry: false
|
|
2119
2569
|
}));
|
|
@@ -2124,8 +2574,217 @@ function registerPrepareCommand(cli) {
|
|
|
2124
2574
|
});
|
|
2125
2575
|
}
|
|
2126
2576
|
//#endregion
|
|
2577
|
+
//#region src/cli/devHotkeys/devtools.ts
|
|
2578
|
+
async function appendLatestHmrSummary(baseSummary, options) {
|
|
2579
|
+
const summary = await readLatestHmrProfileSummary({
|
|
2580
|
+
cwd: options.cwd,
|
|
2581
|
+
relativeCwd: (value) => value.replace(`${options.cwd}/`, ""),
|
|
2582
|
+
weappViteConfig: options.weappViteConfig
|
|
2583
|
+
});
|
|
2584
|
+
if (!summary) return baseSummary;
|
|
2585
|
+
return `${baseSummary};${summary.line}`;
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* @description 重置当前 DevTools automator 共享会话。
|
|
2589
|
+
*/
|
|
2590
|
+
async function runResetDevtoolsSessionAction(options) {
|
|
2591
|
+
logger_default.info("[dev action] 正在重置当前 DevTools 会话...");
|
|
2592
|
+
await closeSharedMiniProgram(options.projectPath);
|
|
2593
|
+
logger_default.success("[dev action] 当前 DevTools 会话已重置。");
|
|
2594
|
+
return "已重置当前 DevTools 会话";
|
|
2595
|
+
}
|
|
2596
|
+
/**
|
|
2597
|
+
* @description 重置共享会话并重开当前项目,以恢复 DevTools 状态。
|
|
2598
|
+
*/
|
|
2599
|
+
async function runResetAndReopenDevtoolsAction(options) {
|
|
2600
|
+
if (!options.openIde) {
|
|
2601
|
+
logger_default.warn("[dev action] 当前 dev 会话未提供重新打开微信开发者工具的能力。");
|
|
2602
|
+
return "重置并重开微信开发者工具不可用";
|
|
2603
|
+
}
|
|
2604
|
+
logger_default.info("[dev action] 正在重置当前 DevTools 会话并重开项目...");
|
|
2605
|
+
await closeSharedMiniProgram(options.projectPath);
|
|
2606
|
+
const summary = await options.openIde();
|
|
2607
|
+
logger_default.success("[dev action] 当前 DevTools 会话已重置,并已重新打开项目。");
|
|
2608
|
+
return await appendLatestHmrSummary(summary ?? "已重置当前 DevTools 会话并重新打开项目", options);
|
|
2609
|
+
}
|
|
2610
|
+
/**
|
|
2611
|
+
* @description 手动触发一次当前小程序 dev 重新构建。
|
|
2612
|
+
*/
|
|
2613
|
+
async function runDevRebuildAction(options) {
|
|
2614
|
+
if (!options.rebuild) {
|
|
2615
|
+
logger_default.warn("[dev action] 当前 dev 会话未提供手动重新构建能力。");
|
|
2616
|
+
return "手动重新构建不可用";
|
|
2617
|
+
}
|
|
2618
|
+
logger_default.info("[dev action] 正在手动重新构建当前小程序产物...");
|
|
2619
|
+
const summary = await options.rebuild();
|
|
2620
|
+
logger_default.success("[dev action] 当前小程序产物已手动重新构建。");
|
|
2621
|
+
return summary ?? "已手动重新构建当前小程序产物";
|
|
2622
|
+
}
|
|
2623
|
+
/**
|
|
2624
|
+
* @description 重新打开当前微信开发者工具项目,并主动清理共享 automator 会话。
|
|
2625
|
+
*/
|
|
2626
|
+
async function runOpenIdeAction(options) {
|
|
2627
|
+
if (!options.openIde) {
|
|
2628
|
+
logger_default.warn("[dev action] 当前 dev 会话未提供重新打开微信开发者工具的能力。");
|
|
2629
|
+
return "重新打开微信开发者工具不可用";
|
|
2630
|
+
}
|
|
2631
|
+
logger_default.info(`[dev action] 正在重新打开微信开发者工具项目:${colors.cyan(options.projectPath)}`);
|
|
2632
|
+
await closeSharedMiniProgram(options.projectPath);
|
|
2633
|
+
const summary = await options.openIde();
|
|
2634
|
+
logger_default.success("[dev action] 微信开发者工具项目已重新打开。");
|
|
2635
|
+
return await appendLatestHmrSummary(summary ?? "已重新打开微信开发者工具项目", options);
|
|
2636
|
+
}
|
|
2637
|
+
//#endregion
|
|
2638
|
+
//#region src/cli/devHotkeys/screenshot.ts
|
|
2639
|
+
const DEV_SCREENSHOT_DIR = ".weapp-vite/dev-screenshots";
|
|
2640
|
+
const DEFAULT_SCREENSHOT_TIMEOUT = 3e4;
|
|
2641
|
+
/**
|
|
2642
|
+
* @description 生成开发态截图输出路径。
|
|
2643
|
+
*/
|
|
2644
|
+
function resolveDevScreenshotOutputPath(cwd, now = /* @__PURE__ */ new Date()) {
|
|
2645
|
+
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
2646
|
+
return path.join(cwd, DEV_SCREENSHOT_DIR, `screenshot-${stamp}.png`);
|
|
2647
|
+
}
|
|
2648
|
+
function formatLogPath(cwd, targetPath) {
|
|
2649
|
+
const relativePath = path.relative(cwd, targetPath);
|
|
2650
|
+
if (!relativePath || relativePath.startsWith("..")) return targetPath;
|
|
2651
|
+
return relativePath;
|
|
2652
|
+
}
|
|
2653
|
+
function formatResolvedScreenshotPath(cwd, fallbackPath, result) {
|
|
2654
|
+
return formatLogPath(cwd, result.path ?? fallbackPath);
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* @description 执行当前页面截图并输出结果日志。
|
|
2658
|
+
*/
|
|
2659
|
+
async function runScreenshotAction(options) {
|
|
2660
|
+
const outputPath = resolveDevScreenshotOutputPath(options.cwd);
|
|
2661
|
+
await fs$2.mkdir(path.dirname(outputPath), { recursive: true });
|
|
2662
|
+
logger_default.info(`[dev action] 正在截图当前页面,输出到 ${colors.cyan(formatLogPath(options.cwd, outputPath))}`);
|
|
2663
|
+
const result = await takeScreenshot({
|
|
2664
|
+
fullPage: true,
|
|
2665
|
+
projectPath: options.projectPath,
|
|
2666
|
+
sharedSession: true,
|
|
2667
|
+
outputPath,
|
|
2668
|
+
timeout: DEFAULT_SCREENSHOT_TIMEOUT
|
|
2669
|
+
});
|
|
2670
|
+
const resolvedPath = formatResolvedScreenshotPath(options.cwd, outputPath, result);
|
|
2671
|
+
logger_default.success(`[dev action] 当前页面截图完成:${colors.cyan(resolvedPath)}`);
|
|
2672
|
+
return resolvedPath;
|
|
2673
|
+
}
|
|
2674
|
+
//#endregion
|
|
2675
|
+
//#region src/cli/devHotkeys/actions.ts
|
|
2676
|
+
const DEV_HOTKEY_GROUPS = [
|
|
2677
|
+
{
|
|
2678
|
+
key: "development",
|
|
2679
|
+
title: "开发动作"
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
key: "devtools",
|
|
2683
|
+
title: "会话动作"
|
|
2684
|
+
},
|
|
2685
|
+
{
|
|
2686
|
+
key: "process",
|
|
2687
|
+
title: "进程控制"
|
|
2688
|
+
},
|
|
2689
|
+
{
|
|
2690
|
+
key: "help",
|
|
2691
|
+
title: "帮助"
|
|
2692
|
+
}
|
|
2693
|
+
];
|
|
2694
|
+
const DEV_HOTKEY_DEFINITIONS = [
|
|
2695
|
+
{
|
|
2696
|
+
description: "截图当前页面并保存到本地",
|
|
2697
|
+
group: "development",
|
|
2698
|
+
key: "s",
|
|
2699
|
+
label: "截图",
|
|
2700
|
+
pendingLabel: "正在截图当前页面",
|
|
2701
|
+
run: async ({ options }) => {
|
|
2702
|
+
return `截图已保存到 ${await runScreenshotAction(options)}`;
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
{
|
|
2706
|
+
description: "手动重新构建当前小程序产物",
|
|
2707
|
+
group: "development",
|
|
2708
|
+
key: "r",
|
|
2709
|
+
label: "手动重新构建产物",
|
|
2710
|
+
pendingLabel: "正在手动重新构建当前小程序产物",
|
|
2711
|
+
run: async ({ options }) => {
|
|
2712
|
+
return await runDevRebuildAction(options);
|
|
2713
|
+
}
|
|
2714
|
+
},
|
|
2715
|
+
{
|
|
2716
|
+
description: "重置当前 DevTools automator 会话",
|
|
2717
|
+
group: "devtools",
|
|
2718
|
+
key: "c",
|
|
2719
|
+
label: "重置 DevTools 会话",
|
|
2720
|
+
pendingLabel: "正在重置当前 DevTools 会话",
|
|
2721
|
+
run: async ({ options }) => {
|
|
2722
|
+
return await runResetDevtoolsSessionAction(options);
|
|
2723
|
+
}
|
|
2724
|
+
},
|
|
2725
|
+
{
|
|
2726
|
+
description: "重置会话并重开当前微信开发者工具项目",
|
|
2727
|
+
group: "devtools",
|
|
2728
|
+
key: "C",
|
|
2729
|
+
label: "重置并重开项目",
|
|
2730
|
+
pendingLabel: "正在重置当前 DevTools 会话并重开项目",
|
|
2731
|
+
run: async ({ options }) => {
|
|
2732
|
+
return await runResetAndReopenDevtoolsAction(options);
|
|
2733
|
+
}
|
|
2734
|
+
},
|
|
2735
|
+
{
|
|
2736
|
+
description: "重新打开当前微信开发者工具项目",
|
|
2737
|
+
group: "devtools",
|
|
2738
|
+
key: "o",
|
|
2739
|
+
label: "重新打开微信开发者工具",
|
|
2740
|
+
pendingLabel: "正在重新打开微信开发者工具项目",
|
|
2741
|
+
run: async ({ options }) => {
|
|
2742
|
+
return await runOpenIdeAction(options);
|
|
2743
|
+
}
|
|
2744
|
+
},
|
|
2745
|
+
{
|
|
2746
|
+
description: "开关 MCP 服务",
|
|
2747
|
+
group: "devtools",
|
|
2748
|
+
key: "m",
|
|
2749
|
+
label: "MCP 切换",
|
|
2750
|
+
run: async ({ toggleMcp }) => {
|
|
2751
|
+
return await toggleMcp();
|
|
2752
|
+
}
|
|
2753
|
+
},
|
|
2754
|
+
{
|
|
2755
|
+
description: "退出当前 dev",
|
|
2756
|
+
group: "process",
|
|
2757
|
+
key: "q"
|
|
2758
|
+
},
|
|
2759
|
+
{
|
|
2760
|
+
description: "强制中断当前 dev",
|
|
2761
|
+
group: "process",
|
|
2762
|
+
key: "Ctrl+C"
|
|
2763
|
+
},
|
|
2764
|
+
{
|
|
2765
|
+
description: "暂时挂起当前 dev,恢复终端控制",
|
|
2766
|
+
group: "process",
|
|
2767
|
+
key: "Ctrl+Z"
|
|
2768
|
+
},
|
|
2769
|
+
{
|
|
2770
|
+
description: "重新显示这份帮助",
|
|
2771
|
+
group: "help",
|
|
2772
|
+
key: "h"
|
|
2773
|
+
}
|
|
2774
|
+
];
|
|
2775
|
+
function resolveDevHotkeyRowsByGroup() {
|
|
2776
|
+
return DEV_HOTKEY_GROUPS.map((group) => ({
|
|
2777
|
+
key: group.key,
|
|
2778
|
+
rows: DEV_HOTKEY_DEFINITIONS.filter((action) => action.group === group.key),
|
|
2779
|
+
title: group.title
|
|
2780
|
+
}));
|
|
2781
|
+
}
|
|
2782
|
+
function resolveRunnableHotkeyDefinition(input) {
|
|
2783
|
+
return DEV_HOTKEY_DEFINITIONS.find((action) => action.run && action.key === input);
|
|
2784
|
+
}
|
|
2785
|
+
//#endregion
|
|
2127
2786
|
//#region package.json
|
|
2128
|
-
var version = "6.15.
|
|
2787
|
+
var version = "6.15.15";
|
|
2129
2788
|
//#endregion
|
|
2130
2789
|
//#region src/cli/devHotkeys/format.ts
|
|
2131
2790
|
const FULLWIDTH_ASCII_START = 65281;
|
|
@@ -2143,55 +2802,45 @@ function formatFooterLine(state) {
|
|
|
2143
2802
|
if (state.currentAction) return `执行中 ${state.currentAction}`;
|
|
2144
2803
|
return "就绪 等待操作...";
|
|
2145
2804
|
}
|
|
2805
|
+
function formatHotkeyRows(rows) {
|
|
2806
|
+
const key = (value) => colors.bold(colors.green(value));
|
|
2807
|
+
const formattedRows = rows.map((row) => ({
|
|
2808
|
+
description: row.description,
|
|
2809
|
+
key: key(row.key)
|
|
2810
|
+
}));
|
|
2811
|
+
const keyColumnWidth = Math.max(...formattedRows.map((row) => row.key.length));
|
|
2812
|
+
return formattedRows.map(({ key, description }) => `按 ${key.padEnd(keyColumnWidth)} ${description}`);
|
|
2813
|
+
}
|
|
2814
|
+
function formatExtraHotkeyRows(rows) {
|
|
2815
|
+
const key = (value) => colors.bold(colors.green(value));
|
|
2816
|
+
const formattedRows = rows.map(([hotkey, description]) => ({
|
|
2817
|
+
description,
|
|
2818
|
+
key: key(hotkey)
|
|
2819
|
+
}));
|
|
2820
|
+
const keyColumnWidth = Math.max(...formattedRows.map((row) => row.key.length));
|
|
2821
|
+
return formattedRows.map(({ key, description }) => `按 ${key.padEnd(keyColumnWidth)} ${description}`);
|
|
2822
|
+
}
|
|
2146
2823
|
/**
|
|
2147
2824
|
* @description 生成带状态的开发态快捷键帮助文本。
|
|
2148
2825
|
*/
|
|
2149
2826
|
function formatDevHotkeyHelpWithState(state) {
|
|
2150
|
-
const
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
{
|
|
2160
|
-
key: key("q"),
|
|
2161
|
-
description: "退出当前 dev"
|
|
2162
|
-
},
|
|
2163
|
-
{
|
|
2164
|
-
key: key("Ctrl+C"),
|
|
2165
|
-
description: "强制中断当前 dev"
|
|
2166
|
-
},
|
|
2167
|
-
{
|
|
2168
|
-
key: key("Ctrl+Z"),
|
|
2169
|
-
description: "暂时挂起当前 dev,恢复终端控制"
|
|
2170
|
-
}
|
|
2827
|
+
const sections = resolveDevHotkeyRowsByGroup().filter((section) => section.rows.length > 0).flatMap((section) => [
|
|
2828
|
+
"",
|
|
2829
|
+
section.title,
|
|
2830
|
+
...formatHotkeyRows(section.rows)
|
|
2831
|
+
]);
|
|
2832
|
+
const retrySections = [
|
|
2833
|
+
"",
|
|
2834
|
+
"登录重试",
|
|
2835
|
+
...formatExtraHotkeyRows([[RETRY_CONFIRM_KEYS.join(" / "), "微信开发者工具登录失效后确认重试当前命令"], [RETRY_CANCEL_KEYS.join(" / "), "取消当前登录重试提示"]])
|
|
2171
2836
|
];
|
|
2172
|
-
const helpRows = [{
|
|
2173
|
-
key: key("h"),
|
|
2174
|
-
description: "重新显示这份帮助"
|
|
2175
|
-
}];
|
|
2176
|
-
const keyColumnWidth = Math.max(...[
|
|
2177
|
-
...actionRows,
|
|
2178
|
-
...processRows,
|
|
2179
|
-
...helpRows
|
|
2180
|
-
].map((row) => row.key.length));
|
|
2181
|
-
const formatRows = (rows) => rows.map(({ key, description }) => `按 ${key.padEnd(keyColumnWidth)} ${description}`);
|
|
2182
2837
|
return [
|
|
2183
2838
|
`${colors.bold(colors.green("DEV"))} weapp-vite v${version} ${state.projectLabel ?? "weapp"}`,
|
|
2839
|
+
...sections,
|
|
2840
|
+
...retrySections,
|
|
2184
2841
|
"",
|
|
2185
|
-
"
|
|
2186
|
-
...
|
|
2187
|
-
"",
|
|
2188
|
-
"进程控制",
|
|
2189
|
-
...formatRows(processRows),
|
|
2190
|
-
"",
|
|
2191
|
-
"帮助",
|
|
2192
|
-
...formatRows(helpRows),
|
|
2193
|
-
"",
|
|
2194
|
-
`当前状态:${state.currentAction ?? "等待操作"} / MCP ${formatMcpStatus(state)}`
|
|
2842
|
+
`当前状态:${state.currentAction ?? "等待操作"} / MCP ${formatMcpStatus(state)}`,
|
|
2843
|
+
...state.lastAction ? [`最近操作:${state.lastAction}`] : []
|
|
2195
2844
|
].join("\n");
|
|
2196
2845
|
}
|
|
2197
2846
|
/**
|
|
@@ -2200,6 +2849,7 @@ function formatDevHotkeyHelpWithState(state) {
|
|
|
2200
2849
|
function formatDevHotkeyHintWithState(state) {
|
|
2201
2850
|
const key = (value) => colors.bold(colors.green(value));
|
|
2202
2851
|
if (state.currentAction) return `${formatFooterLine(state)},按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
|
|
2852
|
+
if (state.lastAction) return `开发快捷键已就绪,最近操作:${state.lastAction},按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
|
|
2203
2853
|
return `开发快捷键已就绪,按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
|
|
2204
2854
|
}
|
|
2205
2855
|
function normalizeInputChar(input) {
|
|
@@ -2252,43 +2902,6 @@ function createToggleMcpAction(options) {
|
|
|
2252
2902
|
};
|
|
2253
2903
|
}
|
|
2254
2904
|
//#endregion
|
|
2255
|
-
//#region src/cli/devHotkeys/screenshot.ts
|
|
2256
|
-
const DEV_SCREENSHOT_DIR = ".weapp-vite/dev-screenshots";
|
|
2257
|
-
const DEFAULT_SCREENSHOT_TIMEOUT = 3e4;
|
|
2258
|
-
/**
|
|
2259
|
-
* @description 生成开发态截图输出路径。
|
|
2260
|
-
*/
|
|
2261
|
-
function resolveDevScreenshotOutputPath(cwd, now = /* @__PURE__ */ new Date()) {
|
|
2262
|
-
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
2263
|
-
return path.join(cwd, DEV_SCREENSHOT_DIR, `screenshot-${stamp}.png`);
|
|
2264
|
-
}
|
|
2265
|
-
function formatLogPath(cwd, targetPath) {
|
|
2266
|
-
const relativePath = path.relative(cwd, targetPath);
|
|
2267
|
-
if (!relativePath || relativePath.startsWith("..")) return targetPath;
|
|
2268
|
-
return relativePath;
|
|
2269
|
-
}
|
|
2270
|
-
function formatResolvedScreenshotPath(cwd, fallbackPath, result) {
|
|
2271
|
-
return formatLogPath(cwd, result.path ?? fallbackPath);
|
|
2272
|
-
}
|
|
2273
|
-
/**
|
|
2274
|
-
* @description 执行当前页面截图并输出结果日志。
|
|
2275
|
-
*/
|
|
2276
|
-
async function runScreenshotAction(options) {
|
|
2277
|
-
const outputPath = resolveDevScreenshotOutputPath(options.cwd);
|
|
2278
|
-
await fs$2.mkdir(path.dirname(outputPath), { recursive: true });
|
|
2279
|
-
logger_default.info(`[dev action] 正在截图当前页面,输出到 ${colors.cyan(formatLogPath(options.cwd, outputPath))}`);
|
|
2280
|
-
const result = await takeScreenshot({
|
|
2281
|
-
fullPage: true,
|
|
2282
|
-
projectPath: options.projectPath,
|
|
2283
|
-
sharedSession: true,
|
|
2284
|
-
outputPath,
|
|
2285
|
-
timeout: DEFAULT_SCREENSHOT_TIMEOUT
|
|
2286
|
-
});
|
|
2287
|
-
const resolvedPath = formatResolvedScreenshotPath(options.cwd, outputPath, result);
|
|
2288
|
-
logger_default.success(`[dev action] 当前页面截图完成:${colors.cyan(resolvedPath)}`);
|
|
2289
|
-
return resolvedPath;
|
|
2290
|
-
}
|
|
2291
|
-
//#endregion
|
|
2292
2905
|
//#region src/cli/devHotkeys/index.ts
|
|
2293
2906
|
function forwardSigint() {
|
|
2294
2907
|
process.kill(process.pid, "SIGINT");
|
|
@@ -2301,10 +2914,6 @@ function forwardSigtstp() {
|
|
|
2301
2914
|
*/
|
|
2302
2915
|
function startDevHotkeys(options) {
|
|
2303
2916
|
if (options.platform !== "weapp" || !process.stdin.isTTY) return;
|
|
2304
|
-
emitKeypressEvents(process.stdin);
|
|
2305
|
-
const hasSetRawMode = typeof process.stdin.setRawMode === "function";
|
|
2306
|
-
if (hasSetRawMode) process.stdin.setRawMode(true);
|
|
2307
|
-
process.stdin.resume();
|
|
2308
2917
|
let closed = false;
|
|
2309
2918
|
let running = false;
|
|
2310
2919
|
let mcpHandle;
|
|
@@ -2323,27 +2932,20 @@ function startDevHotkeys(options) {
|
|
|
2323
2932
|
mcpRunning: Boolean(mcpHandle?.close),
|
|
2324
2933
|
projectLabel: resolveProjectLabel(options.cwd)
|
|
2325
2934
|
});
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
};
|
|
2335
|
-
const ensureTerminalActive = () => {
|
|
2336
|
-
if (closed) return;
|
|
2337
|
-
if (hasSetRawMode) process.stdin.setRawMode(true);
|
|
2338
|
-
process.stdin.resume();
|
|
2339
|
-
};
|
|
2935
|
+
let inputSession = createSharedInputSession({
|
|
2936
|
+
onData: (chunk) => {
|
|
2937
|
+
onData?.(chunk);
|
|
2938
|
+
},
|
|
2939
|
+
onKeypress: (str, key) => {
|
|
2940
|
+
onKeypress?.(str, key);
|
|
2941
|
+
}
|
|
2942
|
+
});
|
|
2340
2943
|
const close = () => {
|
|
2341
2944
|
if (closed) return;
|
|
2342
2945
|
closed = true;
|
|
2343
|
-
if (onData) process.stdin.off("data", onData);
|
|
2344
|
-
if (onKeypress) process.stdin.off("keypress", onKeypress);
|
|
2345
2946
|
if (onSigcont) process.off("SIGCONT", onSigcont);
|
|
2346
|
-
|
|
2947
|
+
inputSession?.close();
|
|
2948
|
+
inputSession = void 0;
|
|
2347
2949
|
if (mcpHandle?.close) mcpHandle.close().catch((error) => {
|
|
2348
2950
|
logger_default.warn(`[dev action] MCP 服务关闭失败:${error instanceof Error ? error.message : String(error)}`);
|
|
2349
2951
|
});
|
|
@@ -2358,24 +2960,18 @@ function startDevHotkeys(options) {
|
|
|
2358
2960
|
};
|
|
2359
2961
|
const printHelp = () => {
|
|
2360
2962
|
printPanel(formatDevHotkeyHelpWithState(getState()), true);
|
|
2361
|
-
ensureTerminalActive();
|
|
2362
2963
|
};
|
|
2363
2964
|
const printHint = () => {
|
|
2364
2965
|
printPanel(formatDevHotkeyHintWithState(getState()));
|
|
2365
|
-
ensureTerminalActive();
|
|
2366
2966
|
};
|
|
2367
2967
|
const restore = () => {
|
|
2368
2968
|
if (closed) return;
|
|
2369
|
-
|
|
2370
|
-
if (onKeypress) process.stdin.off("keypress", onKeypress);
|
|
2371
|
-
attachTerminal();
|
|
2372
|
-
if (onData) process.stdin.on("data", onData);
|
|
2373
|
-
if (onKeypress) process.stdin.on("keypress", onKeypress);
|
|
2969
|
+
inputSession?.resume();
|
|
2374
2970
|
printHint();
|
|
2375
2971
|
};
|
|
2376
2972
|
const suspend = () => {
|
|
2377
2973
|
lastRenderedPanel = "";
|
|
2378
|
-
|
|
2974
|
+
inputSession?.suspend();
|
|
2379
2975
|
forwardSigtstp();
|
|
2380
2976
|
};
|
|
2381
2977
|
const toggleMcp = createToggleMcpAction({
|
|
@@ -2386,6 +2982,9 @@ function startDevHotkeys(options) {
|
|
|
2386
2982
|
mcpHandle = handle;
|
|
2387
2983
|
}
|
|
2388
2984
|
});
|
|
2985
|
+
const resolvePendingLabel = (input) => {
|
|
2986
|
+
if (input === "m") return mcpHandle?.close ? "正在关闭 MCP 服务" : "正在启动 MCP 服务";
|
|
2987
|
+
};
|
|
2389
2988
|
const runAction = (label, pendingLabel, action) => {
|
|
2390
2989
|
if (running) {
|
|
2391
2990
|
const current = currentAction ?? "已有命令";
|
|
@@ -2427,14 +3026,13 @@ function startDevHotkeys(options) {
|
|
|
2427
3026
|
printHelp();
|
|
2428
3027
|
return;
|
|
2429
3028
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
3029
|
+
const action = resolveRunnableHotkeyDefinition(normalizedInput) ?? resolveRunnableHotkeyDefinition(normalized);
|
|
3030
|
+
const run = action?.run;
|
|
3031
|
+
if (run) runAction(action.label ?? action.description, resolvePendingLabel(normalized) ?? action.pendingLabel ?? `正在执行 ${action.description}`, async () => {
|
|
3032
|
+
return await run({
|
|
3033
|
+
options,
|
|
3034
|
+
toggleMcp
|
|
2433
3035
|
});
|
|
2434
|
-
return;
|
|
2435
|
-
}
|
|
2436
|
-
if (normalized === "m") runAction("MCP 切换", mcpHandle?.close ? "正在关闭 MCP 服务" : "正在启动 MCP 服务", async () => {
|
|
2437
|
-
return await toggleMcp();
|
|
2438
3036
|
});
|
|
2439
3037
|
};
|
|
2440
3038
|
const handleInputOnce = (input, source) => {
|
|
@@ -2454,7 +3052,6 @@ function startDevHotkeys(options) {
|
|
|
2454
3052
|
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
2455
3053
|
for (const char of text) handleInputOnce(char, "data");
|
|
2456
3054
|
};
|
|
2457
|
-
process.stdin.on("data", onData);
|
|
2458
3055
|
onKeypress = (str, key) => {
|
|
2459
3056
|
if (key?.ctrl && key.name === "c") {
|
|
2460
3057
|
handleInputOnce("", "keypress");
|
|
@@ -2466,7 +3063,6 @@ function startDevHotkeys(options) {
|
|
|
2466
3063
|
}
|
|
2467
3064
|
if (typeof str === "string" && str) handleInputOnce(str, "keypress");
|
|
2468
3065
|
};
|
|
2469
|
-
process.stdin.on("keypress", onKeypress);
|
|
2470
3066
|
onSigcont = () => {
|
|
2471
3067
|
restore();
|
|
2472
3068
|
};
|
|
@@ -2697,6 +3293,24 @@ function resolveWebHost(host) {
|
|
|
2697
3293
|
if (typeof host === "string") return host;
|
|
2698
3294
|
return String(host);
|
|
2699
3295
|
}
|
|
3296
|
+
/**
|
|
3297
|
+
* @description 为 serve 模式构造统一的小程序开发动作,供热键与命令复用。
|
|
3298
|
+
*/
|
|
3299
|
+
function createServeMiniProgramDevActions(options) {
|
|
3300
|
+
const projectPath = options.projectPath ?? options.fallbackProjectPath;
|
|
3301
|
+
return {
|
|
3302
|
+
projectPath,
|
|
3303
|
+
openIde: async () => {
|
|
3304
|
+
if (await options.tryReuseForwardConsole?.()) return "已通过控制台转发复用当前开发者工具会话";
|
|
3305
|
+
await options.openIde(projectPath);
|
|
3306
|
+
return "已重新打开微信开发者工具项目";
|
|
3307
|
+
},
|
|
3308
|
+
rebuild: async () => {
|
|
3309
|
+
await options.build();
|
|
3310
|
+
return "已手动重新构建当前小程序产物";
|
|
3311
|
+
}
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
2700
3314
|
function waitForServeShutdownSignal() {
|
|
2701
3315
|
return new Promise((resolve) => {
|
|
2702
3316
|
const onSignal = () => {
|
|
@@ -2758,12 +3372,36 @@ function registerServeCommand(cli) {
|
|
|
2758
3372
|
logRuntimeTarget(targets, { resolvedConfigPlatform: configService.platform });
|
|
2759
3373
|
const enableAnalyze = Boolean(isUiEnabled(options) && targets.runMini);
|
|
2760
3374
|
let analyzeHandle;
|
|
3375
|
+
const miniProgramDevActions = createServeMiniProgramDevActions({
|
|
3376
|
+
build: async () => {
|
|
3377
|
+
await buildService.build(options);
|
|
3378
|
+
},
|
|
3379
|
+
fallbackProjectPath: configService.cwd,
|
|
3380
|
+
openIde: async (projectPath) => {
|
|
3381
|
+
await openIde(configService.platform, projectPath, {
|
|
3382
|
+
reuseOpenedProject: false,
|
|
3383
|
+
trustProject: options.trustProject
|
|
3384
|
+
});
|
|
3385
|
+
},
|
|
3386
|
+
projectPath: resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd),
|
|
3387
|
+
tryReuseForwardConsole: async () => {
|
|
3388
|
+
return await maybeStartForwardConsole({
|
|
3389
|
+
platform: configService.platform,
|
|
3390
|
+
mpDistRoot: configService.mpDistRoot,
|
|
3391
|
+
cwd: configService.cwd,
|
|
3392
|
+
weappViteConfig: configService.weappViteConfig
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
});
|
|
2761
3396
|
const devHotkeysSession = targets.runMini ? startDevHotkeys({
|
|
2762
3397
|
cwd: configService.cwd,
|
|
2763
3398
|
mcpConfig: configService.weappViteConfig?.mcp,
|
|
3399
|
+
openIde: miniProgramDevActions.openIde,
|
|
2764
3400
|
platform: configService.platform,
|
|
2765
|
-
projectPath:
|
|
2766
|
-
|
|
3401
|
+
projectPath: miniProgramDevActions.projectPath ?? configService.cwd,
|
|
3402
|
+
rebuild: miniProgramDevActions.rebuild,
|
|
3403
|
+
silentStartupHint: true,
|
|
3404
|
+
weappViteConfig: configService.weappViteConfig
|
|
2767
3405
|
}) : void 0;
|
|
2768
3406
|
try {
|
|
2769
3407
|
const analyzeController = createAnalyzeController({
|
|
@@ -2824,15 +3462,7 @@ function registerServeCommand(cli) {
|
|
|
2824
3462
|
detail: "开发服务已就绪,准备打开 IDE 项目。",
|
|
2825
3463
|
tags: ["ide", "open"]
|
|
2826
3464
|
}]);
|
|
2827
|
-
|
|
2828
|
-
platform: configService.platform,
|
|
2829
|
-
mpDistRoot: configService.mpDistRoot,
|
|
2830
|
-
cwd: configService.cwd,
|
|
2831
|
-
weappViteConfig: configService.weappViteConfig
|
|
2832
|
-
})) await openIde(configService.platform, resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd), {
|
|
2833
|
-
reuseOpenedProject: false,
|
|
2834
|
-
trustProject: options.trustProject
|
|
2835
|
-
});
|
|
3465
|
+
await miniProgramDevActions.openIde();
|
|
2836
3466
|
devHotkeysSession?.restore();
|
|
2837
3467
|
}
|
|
2838
3468
|
if (analyzeHandle) await analyzeController.waitForExit();
|
|
@@ -2904,18 +3534,18 @@ async function tryRunIdeCommand(argv) {
|
|
|
2904
3534
|
if (!command) return false;
|
|
2905
3535
|
if (command === "ide") {
|
|
2906
3536
|
if (argv[1] === "logs") return false;
|
|
2907
|
-
await
|
|
3537
|
+
if (!await dispatchWechatCliCommand(argv.slice(1))) await executeWechatIdeCliCommand(argv.slice(1));
|
|
2908
3538
|
return true;
|
|
2909
3539
|
}
|
|
2910
3540
|
if (command.startsWith("-")) return false;
|
|
2911
3541
|
if (command === "help") {
|
|
2912
3542
|
const target = argv[1];
|
|
2913
3543
|
if (!target || WEAPP_VITE_NATIVE_COMMANDS.has(target) || !isWeappIdeTopLevelCommand(target)) return false;
|
|
2914
|
-
await
|
|
3544
|
+
await executeWechatIdeCliCommand(argv);
|
|
2915
3545
|
return true;
|
|
2916
3546
|
}
|
|
2917
3547
|
if (WEAPP_VITE_NATIVE_COMMANDS.has(command) || !isWeappIdeTopLevelCommand(command)) return false;
|
|
2918
|
-
await
|
|
3548
|
+
if (!await dispatchWechatCliCommand(argv)) await executeWechatIdeCliCommand(argv);
|
|
2919
3549
|
return true;
|
|
2920
3550
|
}
|
|
2921
3551
|
//#endregion
|