weapp-ide-cli 6.2.0 → 6.3.0
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 +59 -3
- package/dist/{automator-CRpB6_pA.js → automator-BOe32upt.js} +388 -142
- package/dist/automator-VSjgHujY.js +1 -0
- package/dist/automator-session-VOB03glx.js +186 -0
- package/dist/{sessionStore-zdzy2UFM.js → automatorProject-DjYLqMj6.js} +495 -219
- package/dist/{cli-ChMQqi5w.js → cli-CFlqFpIw.js} +372 -61
- package/dist/cli.js +2 -2
- package/dist/commands-DmirGBy4.js +1 -0
- package/dist/{commands-C1PF1B4P.js → commands-mslf7-sP.js} +3 -2
- package/dist/{devtoolsProjectOwnership-yDD6h0dO.js → devtoolsProjectOwnership-BMfSAg4n.js} +394 -98
- package/dist/index.d.ts +123 -39
- package/dist/index.js +8 -8
- package/dist/{managed-Sq8y8FzP.js → managed-B04Xo9U_.js} +107 -55
- package/dist/run-mcp-83-VKZVA.js +1 -0
- package/dist/{run-mcp-Dl_1TH1x.js → run-mcp-Ni139Z5C.js} +11 -1
- package/package.json +9 -9
- package/dist/automator-BkZZsGtn.js +0 -1
- package/dist/automator-session-gWEsSs_A.js +0 -558
- package/dist/commands-CZVPfkE9.js +0 -1
- package/dist/run-mcp-CPv8cesh.js +0 -1
|
@@ -1,124 +1,15 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { mutateLease } from "@weapp-vite/devtools-runtime";
|
|
2
3
|
import { execa } from "execa";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { fs as fs$1 } from "@weapp-core/shared/fs";
|
|
7
|
-
import baseLogger, { colors } from "@weapp-core/logger";
|
|
8
|
-
import os from "node:os";
|
|
9
7
|
import path$1 from "pathe";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
import baseLogger, { colors } from "@weapp-core/logger";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
10
12
|
import { Buffer } from "node:buffer";
|
|
11
|
-
//#region src/logger.ts
|
|
12
|
-
const logger = baseLogger.create({
|
|
13
|
-
stdout: process.stderr,
|
|
14
|
-
stderr: process.stderr
|
|
15
|
-
});
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region src/runtime/platform.ts
|
|
18
|
-
/**
|
|
19
|
-
* @description 官方微信开发者工具只支持 Windows、macOS,Linux 只有社区版。
|
|
20
|
-
* https://github.com/msojocs/wechat-web-devtools-linux
|
|
21
|
-
*/
|
|
22
|
-
const SupportedPlatformsMap = {
|
|
23
|
-
Windows_NT: "Windows_NT",
|
|
24
|
-
Darwin: "Darwin",
|
|
25
|
-
Linux: "Linux"
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* @description 判断当前系统是否支持微信开发者工具。
|
|
29
|
-
*/
|
|
30
|
-
function isOperatingSystemSupported(osName = os.type()) {
|
|
31
|
-
return osName === SupportedPlatformsMap.Windows_NT || osName === SupportedPlatformsMap.Darwin || osName === SupportedPlatformsMap.Linux;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* @description 当前系统名称。
|
|
35
|
-
*/
|
|
36
|
-
const operatingSystemName = os.type();
|
|
37
|
-
async function findFirstExistingPath(candidates) {
|
|
38
|
-
for (const candidate of candidates) {
|
|
39
|
-
if (!candidate) continue;
|
|
40
|
-
if (await fs$1.pathExists(candidate)) return candidate;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
async function getFirstBinaryPath(command) {
|
|
44
|
-
const pathDirs = (process.env.PATH || "").split(path$1.delimiter);
|
|
45
|
-
for (const dir of pathDirs) {
|
|
46
|
-
const fullPath = path$1.join(dir, command);
|
|
47
|
-
try {
|
|
48
|
-
await fs$1.access(fullPath, fs$1.constants.X_OK);
|
|
49
|
-
return fullPath;
|
|
50
|
-
} catch {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function createLinuxCliResolver() {
|
|
56
|
-
let resolvedPath;
|
|
57
|
-
let attempted = false;
|
|
58
|
-
let pending = null;
|
|
59
|
-
return async () => {
|
|
60
|
-
if (attempted) return resolvedPath;
|
|
61
|
-
if (!pending) pending = (async () => {
|
|
62
|
-
try {
|
|
63
|
-
const envPath = await getFirstBinaryPath("wechat-devtools-cli");
|
|
64
|
-
if (envPath) resolvedPath = envPath;
|
|
65
|
-
} catch (error) {
|
|
66
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
67
|
-
logger.warn(`获取 Linux wechat-devtools-cli 路径失败:${reason}`);
|
|
68
|
-
} finally {
|
|
69
|
-
attempted = true;
|
|
70
|
-
}
|
|
71
|
-
return resolvedPath;
|
|
72
|
-
})();
|
|
73
|
-
return pending;
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function createWindowsCliResolver() {
|
|
77
|
-
let resolvedPath;
|
|
78
|
-
let attempted = false;
|
|
79
|
-
let pending = null;
|
|
80
|
-
return async () => {
|
|
81
|
-
if (attempted) return resolvedPath;
|
|
82
|
-
if (!pending) pending = (async () => {
|
|
83
|
-
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
84
|
-
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
|
85
|
-
const localAppData = process.env.LOCALAPPDATA || path$1.join(os.homedir(), "AppData", "Local");
|
|
86
|
-
const candidates = [
|
|
87
|
-
path$1.join(programFilesX86, "Tencent", "微信web开发者工具", "cli.bat"),
|
|
88
|
-
path$1.join(programFiles, "Tencent", "微信web开发者工具", "cli.bat"),
|
|
89
|
-
path$1.join(localAppData, "Programs", "微信开发者工具", "cli.bat"),
|
|
90
|
-
path$1.join(localAppData, "Tencent", "微信web开发者工具", "cli.bat")
|
|
91
|
-
];
|
|
92
|
-
try {
|
|
93
|
-
resolvedPath = await findFirstExistingPath(candidates);
|
|
94
|
-
} catch (error) {
|
|
95
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
96
|
-
logger.warn(`获取 Windows 微信开发者工具 CLI 路径失败:${reason}`);
|
|
97
|
-
} finally {
|
|
98
|
-
attempted = true;
|
|
99
|
-
}
|
|
100
|
-
return resolvedPath;
|
|
101
|
-
})();
|
|
102
|
-
return pending;
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
const linuxCliResolver = createLinuxCliResolver();
|
|
106
|
-
const windowsCliResolver = createWindowsCliResolver();
|
|
107
|
-
const DARWIN_DEFAULT_CLI = "/Applications/wechatwebdevtools.app/Contents/MacOS/cli";
|
|
108
|
-
const cliPathResolvers = {
|
|
109
|
-
[SupportedPlatformsMap.Windows_NT]: windowsCliResolver,
|
|
110
|
-
[SupportedPlatformsMap.Darwin]: async () => DARWIN_DEFAULT_CLI,
|
|
111
|
-
[SupportedPlatformsMap.Linux]: linuxCliResolver
|
|
112
|
-
};
|
|
113
|
-
/**
|
|
114
|
-
* @description 获取默认 CLI 路径(按系统)。
|
|
115
|
-
*/
|
|
116
|
-
async function getDefaultCliPath(targetOs = operatingSystemName) {
|
|
117
|
-
if (!isOperatingSystemSupported(targetOs)) return;
|
|
118
|
-
const resolver = cliPathResolvers[targetOs];
|
|
119
|
-
return await resolver();
|
|
120
|
-
}
|
|
121
|
-
//#endregion
|
|
122
13
|
//#region src/utils/path.ts
|
|
123
14
|
/**
|
|
124
15
|
* @description 解析为绝对路径(基于当前工作目录)
|
|
@@ -221,72 +112,124 @@ async function overwriteCustomConfig(config) {
|
|
|
221
112
|
await writeCustomConfig(nextConfig, { replace: true });
|
|
222
113
|
}
|
|
223
114
|
//#endregion
|
|
224
|
-
//#region src/
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
115
|
+
//#region src/devtoolsTarget/context.ts
|
|
116
|
+
const wechatDevtoolsSelectionContext = new AsyncLocalStorage();
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/logger.ts
|
|
119
|
+
const logger = baseLogger.create({
|
|
120
|
+
stdout: process.stderr,
|
|
121
|
+
stderr: process.stderr
|
|
122
|
+
});
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/runtime/platform.ts
|
|
228
125
|
/**
|
|
229
|
-
* @description
|
|
126
|
+
* @description 官方微信开发者工具只支持 Windows、macOS,Linux 只有社区版。
|
|
127
|
+
* https://github.com/msojocs/wechat-web-devtools-linux
|
|
230
128
|
*/
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
129
|
+
const SupportedPlatformsMap = {
|
|
130
|
+
Windows_NT: "Windows_NT",
|
|
131
|
+
Darwin: "Darwin",
|
|
132
|
+
Linux: "Linux"
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* @description 判断当前系统是否支持微信开发者工具。
|
|
136
|
+
*/
|
|
137
|
+
function isOperatingSystemSupported(osName = os.type()) {
|
|
138
|
+
return osName === SupportedPlatformsMap.Windows_NT || osName === SupportedPlatformsMap.Darwin || osName === SupportedPlatformsMap.Linux;
|
|
236
139
|
}
|
|
237
140
|
/**
|
|
238
|
-
* @description
|
|
141
|
+
* @description 当前系统名称。
|
|
239
142
|
*/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
autoTrustProject,
|
|
257
|
-
source: selectedCliPath ? "environment" : "custom"
|
|
258
|
-
};
|
|
143
|
+
const operatingSystemName = os.type();
|
|
144
|
+
async function findFirstExistingPath(candidates) {
|
|
145
|
+
for (const candidate of candidates) {
|
|
146
|
+
if (!candidate) continue;
|
|
147
|
+
if (await fs$1.pathExists(candidate)) return candidate;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function getFirstBinaryPath(command) {
|
|
151
|
+
const pathDirs = (process.env.PATH || "").split(path$1.delimiter);
|
|
152
|
+
for (const dir of pathDirs) {
|
|
153
|
+
const fullPath = path$1.join(dir, command);
|
|
154
|
+
try {
|
|
155
|
+
await fs$1.access(fullPath, fs$1.constants.X_OK);
|
|
156
|
+
return fullPath;
|
|
157
|
+
} catch {
|
|
158
|
+
continue;
|
|
259
159
|
}
|
|
260
|
-
logger.warn("自定义配置文件缺少有效的 CLI 路径,将尝试使用默认路径。");
|
|
261
|
-
} catch (error) {
|
|
262
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
263
|
-
logger.warn(`解析自定义配置失败,${selectedCliPath ? "继续使用进程指定路径" : "将尝试使用默认路径"}。原因:${reason}`);
|
|
264
160
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
161
|
+
}
|
|
162
|
+
function createLinuxCliResolver() {
|
|
163
|
+
let resolvedPath;
|
|
164
|
+
let attempted = false;
|
|
165
|
+
let pending = null;
|
|
166
|
+
return async () => {
|
|
167
|
+
if (attempted) return resolvedPath;
|
|
168
|
+
if (!pending) pending = (async () => {
|
|
169
|
+
try {
|
|
170
|
+
const envPath = await getFirstBinaryPath("wechat-devtools-cli");
|
|
171
|
+
if (envPath) resolvedPath = envPath;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
174
|
+
logger.warn(`获取 Linux wechat-devtools-cli 路径失败:${reason}`);
|
|
175
|
+
} finally {
|
|
176
|
+
attempted = true;
|
|
177
|
+
}
|
|
178
|
+
return resolvedPath;
|
|
179
|
+
})();
|
|
180
|
+
return pending;
|
|
276
181
|
};
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
182
|
+
}
|
|
183
|
+
function createWindowsCliResolver() {
|
|
184
|
+
let resolvedPath;
|
|
185
|
+
let attempted = false;
|
|
186
|
+
let pending = null;
|
|
187
|
+
return async () => {
|
|
188
|
+
if (attempted) return resolvedPath;
|
|
189
|
+
if (!pending) pending = (async () => {
|
|
190
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
191
|
+
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
|
192
|
+
const localAppData = process.env.LOCALAPPDATA || path$1.join(os.homedir(), "AppData", "Local");
|
|
193
|
+
const candidates = [
|
|
194
|
+
path$1.join(programFilesX86, "Tencent", "微信web开发者工具", "cli.bat"),
|
|
195
|
+
path$1.join(programFiles, "Tencent", "微信web开发者工具", "cli.bat"),
|
|
196
|
+
path$1.join(localAppData, "Programs", "微信开发者工具", "cli.bat"),
|
|
197
|
+
path$1.join(localAppData, "Tencent", "微信web开发者工具", "cli.bat")
|
|
198
|
+
];
|
|
199
|
+
try {
|
|
200
|
+
resolvedPath = await findFirstExistingPath(candidates);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
203
|
+
logger.warn(`获取 Windows 微信开发者工具 CLI 路径失败:${reason}`);
|
|
204
|
+
} finally {
|
|
205
|
+
attempted = true;
|
|
206
|
+
}
|
|
207
|
+
return resolvedPath;
|
|
208
|
+
})();
|
|
209
|
+
return pending;
|
|
283
210
|
};
|
|
284
211
|
}
|
|
212
|
+
const linuxCliResolver = createLinuxCliResolver();
|
|
213
|
+
const windowsCliResolver = createWindowsCliResolver();
|
|
214
|
+
const DARWIN_DEFAULT_CLI = "/Applications/wechatwebdevtools.app/Contents/MacOS/cli";
|
|
215
|
+
const cliPathResolvers = {
|
|
216
|
+
[SupportedPlatformsMap.Windows_NT]: windowsCliResolver,
|
|
217
|
+
[SupportedPlatformsMap.Darwin]: async () => DARWIN_DEFAULT_CLI,
|
|
218
|
+
[SupportedPlatformsMap.Linux]: linuxCliResolver
|
|
219
|
+
};
|
|
285
220
|
/**
|
|
286
|
-
* @description
|
|
221
|
+
* @description 获取默认 CLI 路径(按系统)。
|
|
287
222
|
*/
|
|
288
|
-
async function
|
|
289
|
-
|
|
223
|
+
async function getDefaultCliPath(targetOs = operatingSystemName) {
|
|
224
|
+
if (!isOperatingSystemSupported(targetOs)) return;
|
|
225
|
+
const resolver = cliPathResolvers[targetOs];
|
|
226
|
+
return await resolver();
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/utils/powershell.ts
|
|
230
|
+
/** 固定重定向 stdout 的 UTF-8 编码,避免默认代码页损坏进程路径和安装身份。 */
|
|
231
|
+
function withPowerShellUtf8Output(script) {
|
|
232
|
+
return `[Console]::OutputEncoding=[Text.UTF8Encoding]::new($false); ${script}`;
|
|
290
233
|
}
|
|
291
234
|
//#endregion
|
|
292
235
|
//#region src/devtoolsTarget/metadata.ts
|
|
@@ -330,9 +273,9 @@ async function readAsarPackage(asarPath) {
|
|
|
330
273
|
async function readWechatDevtoolsMetadata(appPath) {
|
|
331
274
|
const value = appPath.endsWith(".asar") ? await readAsarPackage(appPath) : await readPackageFile(path.join(appPath, "package.json"));
|
|
332
275
|
if (!record(value)) throw new Error("Invalid DevTools package metadata.");
|
|
333
|
-
if (![value.name, value.productName].filter((name) => typeof name === "string").some((name) => name === "微信开发者工具" || /wechat(?:web)?devtools/i.test(name))) throw new Error("The selected application metadata does not identify WeChat DevTools.");
|
|
276
|
+
if (![value.name, value.productName].filter((name) => typeof name === "string").some((name) => name === "微信开发者工具" || /wechat(?:web)?devtools/i.test(name))) throw Object.assign(/* @__PURE__ */ new Error("The selected application metadata does not identify WeChat DevTools."), { code: "WECHAT_DEVTOOLS_UNRELATED_APPLICATION" });
|
|
334
277
|
return {
|
|
335
|
-
version: typeof value.version === "string" && /^\d
|
|
278
|
+
version: typeof value.version === "string" && /^\d+(?:\.\d+){2,}$/.test(value.version) ? value.version : void 0,
|
|
336
279
|
channel: String(value.versionType) === "0" ? "stable" : String(value.versionType) === "1" ? "rc" : String(value.versionType) === "2" ? "nightly" : "unknown"
|
|
337
280
|
};
|
|
338
281
|
}
|
|
@@ -347,17 +290,203 @@ function resolveWechatDevtoolsProfileDir(appPath, options = {}) {
|
|
|
347
290
|
return path.join(baseDir, createHash("md5").update(appPath).digest("hex"));
|
|
348
291
|
}
|
|
349
292
|
//#endregion
|
|
350
|
-
//#region src/
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
293
|
+
//#region src/devtoolsTarget/installation.ts
|
|
294
|
+
async function resolveApplicationPath(cliPath, platform) {
|
|
295
|
+
const root = platform === "darwin" && path.basename(path.dirname(cliPath)) === "MacOS" ? path.join(path.dirname(path.dirname(cliPath)), "Resources") : path.dirname(cliPath);
|
|
296
|
+
const candidates = platform === "darwin" ? [
|
|
297
|
+
path.join(root, "app.asar"),
|
|
298
|
+
path.join(root, "app"),
|
|
299
|
+
path.join(root, "package.nw")
|
|
300
|
+
] : [
|
|
301
|
+
path.join(root, "resources", "app.asar"),
|
|
302
|
+
path.join(root, "resources", "app"),
|
|
303
|
+
path.join(root, "package.nw"),
|
|
304
|
+
path.join(root, "resources", "package.nw")
|
|
305
|
+
];
|
|
306
|
+
for (const candidate of candidates) try {
|
|
307
|
+
const stat = await fs.stat(candidate);
|
|
308
|
+
if (candidate.endsWith(".asar") ? stat.isFile() : stat.isDirectory()) return await fs.realpath(candidate);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (error.code !== "ENOENT") throw error;
|
|
311
|
+
}
|
|
312
|
+
throw new Error("Cannot identify the selected WeChat DevTools installation. Select its official CLI path.");
|
|
313
|
+
}
|
|
314
|
+
/** 只读取明确路径的安装元数据,不读取配置或启动宿主。 */
|
|
315
|
+
async function inspectWechatDevtoolsInstallation(selected, options = {}) {
|
|
316
|
+
const cliPath = await fs.realpath(selected);
|
|
317
|
+
if (!(await fs.stat(cliPath)).isFile()) throw new Error("The selected WeChat DevTools CLI is not a file.");
|
|
318
|
+
const appPath = await resolveApplicationPath(cliPath, options.platform ?? process.platform);
|
|
319
|
+
return {
|
|
320
|
+
cliPath,
|
|
321
|
+
appPath,
|
|
322
|
+
installationId: createHash("sha256").update(appPath).digest("hex"),
|
|
323
|
+
profileDir: resolveWechatDevtoolsProfileDir(appPath, options),
|
|
324
|
+
...await readWechatDevtoolsMetadata(appPath)
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/devtoolsTarget/registry.ts
|
|
329
|
+
const defaultWechatDevtoolsRegistryPath = path.join(defaultCustomConfigDirPath, "installations.json");
|
|
330
|
+
/** 登记只保存路径;安装渠道和版本每次从实际应用重新读取。 */
|
|
331
|
+
async function readWechatDevtoolsRegistry(registryPath = defaultWechatDevtoolsRegistryPath) {
|
|
332
|
+
try {
|
|
333
|
+
const value = JSON.parse(await fs.readFile(registryPath, "utf8"));
|
|
334
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string" && entry.trim())) throw new Error("Invalid DevTools installation registry: expected CLI paths.");
|
|
335
|
+
return value;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (error.code === "ENOENT") return [];
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
async function updateRegistry(registryPath, update) {
|
|
342
|
+
await fs.mkdir(path.dirname(registryPath), { recursive: true });
|
|
343
|
+
await mutateLease(`${registryPath}.lock`, async () => {
|
|
344
|
+
const paths = update(await readWechatDevtoolsRegistry(registryPath));
|
|
345
|
+
const temporary = `${registryPath}.${process.pid}.tmp`;
|
|
346
|
+
try {
|
|
347
|
+
await fs.writeFile(temporary, `${JSON.stringify(paths, null, 2)}\n`);
|
|
348
|
+
await fs.rename(temporary, registryPath);
|
|
349
|
+
} finally {
|
|
350
|
+
await fs.rm(temporary, { force: true });
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/** 校验安装后登记真实 CLI 路径,不更改默认选择或登录数据。 */
|
|
355
|
+
async function registerWechatDevtoolsInstallation(cliPath, registryPath = defaultWechatDevtoolsRegistryPath) {
|
|
356
|
+
const target = await inspectWechatDevtoolsInstallation(cliPath);
|
|
357
|
+
await updateRegistry(registryPath, (paths) => [.../* @__PURE__ */ new Set([...paths, target.cliPath])]);
|
|
358
|
+
return target;
|
|
359
|
+
}
|
|
360
|
+
/** 允许移除已经失效的登记;不会删除应用或用户配置。 */
|
|
361
|
+
async function unregisterWechatDevtoolsInstallation(cliPath, registryPath = defaultWechatDevtoolsRegistryPath) {
|
|
362
|
+
const selected = await fs.realpath(cliPath).catch(() => path.resolve(cliPath));
|
|
363
|
+
await updateRegistry(registryPath, (paths) => paths.filter((entry) => entry !== selected && entry !== path.resolve(cliPath)));
|
|
364
|
+
}
|
|
365
|
+
//#endregion
|
|
366
|
+
//#region src/devtoolsTarget/discovery.ts
|
|
367
|
+
/** 产品版本按数字段比较,避免日期版本的字符串排序漂移。 */
|
|
368
|
+
function compareWechatDevtoolsVersions(a, b) {
|
|
369
|
+
const left = a.split(".").map(Number);
|
|
370
|
+
const right = b.split(".").map(Number);
|
|
371
|
+
for (let index = 0; index < Math.max(left.length, right.length); index++) {
|
|
372
|
+
const difference = (left[index] ?? 0) - (right[index] ?? 0);
|
|
373
|
+
if (difference) return difference;
|
|
374
|
+
}
|
|
375
|
+
return 0;
|
|
376
|
+
}
|
|
377
|
+
async function directories(root) {
|
|
378
|
+
try {
|
|
379
|
+
return await fs.readdir(root, { withFileTypes: true });
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (["ENOENT", "EACCES"].includes(error.code ?? "")) return [];
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async function windowsRegistryRoots() {
|
|
386
|
+
const result = await execa("powershell.exe", [
|
|
387
|
+
"-NoProfile",
|
|
388
|
+
"-NonInteractive",
|
|
389
|
+
"-Command",
|
|
390
|
+
withPowerShellUtf8Output("$ErrorActionPreference='Stop'; @('HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*','HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*','HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*') | ForEach-Object { Get-ItemProperty $_ -ErrorAction SilentlyContinue } | Where-Object { $_.DisplayName -match '微信.*开发者|wechat.*devtools' -and $_.InstallLocation } | Select-Object -ExpandProperty InstallLocation | ConvertTo-Json -Compress")
|
|
391
|
+
], {
|
|
392
|
+
reject: false,
|
|
393
|
+
windowsHide: true,
|
|
394
|
+
timeout: 1e4
|
|
395
|
+
});
|
|
396
|
+
if (result.exitCode !== 0) throw new Error("Unable to inspect Windows DevTools installation registry.");
|
|
397
|
+
const parsed = JSON.parse(result.stdout.trim() || "[]");
|
|
398
|
+
return (Array.isArray(parsed) ? parsed : [parsed]).filter((value) => typeof value === "string");
|
|
399
|
+
}
|
|
400
|
+
/** 只扫描已知安装根,不递归遍历磁盘,也不执行候选应用。 */
|
|
401
|
+
async function listWechatDevtoolsInstallations(options = {}) {
|
|
402
|
+
const platform = options.platform ?? process.platform;
|
|
403
|
+
const home = options.homeDir ?? os.homedir();
|
|
404
|
+
const registered = await readWechatDevtoolsRegistry(options.registryPath);
|
|
405
|
+
const config = await readCustomConfig();
|
|
406
|
+
const candidates = new Set([
|
|
407
|
+
...registered,
|
|
408
|
+
config.cliPath,
|
|
409
|
+
process.env.WEAPP_IDE_CLI_PATH
|
|
410
|
+
].filter((value) => !!value));
|
|
411
|
+
const fallback = options.searchRoots || platform === "linux" ? void 0 : await getDefaultCliPath(platform === "darwin" ? "Darwin" : "Windows_NT");
|
|
412
|
+
if (fallback) candidates.add(fallback);
|
|
413
|
+
const roots = options.searchRoots ?? (platform === "darwin" ? ["/Applications", path.join(home, "Applications")] : platform === "win32" ? [
|
|
414
|
+
process.env.ProgramFiles ?? "C:\\Program Files",
|
|
415
|
+
process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)",
|
|
416
|
+
options.localAppDataDir ?? process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local")
|
|
417
|
+
] : []);
|
|
418
|
+
if (platform === "win32" && !options.searchRoots) for (const root of await windowsRegistryRoots().catch(() => [])) candidates.add(path.join(root, "cli.bat"));
|
|
419
|
+
const explicitCandidates = new Set(candidates);
|
|
420
|
+
for (const root of roots) {
|
|
421
|
+
const entries = await directories(root);
|
|
422
|
+
if (platform === "darwin") for (const entry of entries.filter((entry) => /\.app$/i.test(entry.name))) {
|
|
423
|
+
const candidate = path.join(root, entry.name, "Contents", "MacOS", "cli");
|
|
424
|
+
if (await fs.stat(candidate).then((stat) => stat.isFile(), () => false)) candidates.add(candidate);
|
|
425
|
+
}
|
|
426
|
+
else if (platform === "win32") for (const directory of [root, ...entries.filter((entry) => entry.isDirectory()).map((entry) => path.join(root, entry.name))]) for (const candidateRoot of [directory, ...(await directories(directory)).filter((entry) => entry.isDirectory()).map((entry) => path.join(directory, entry.name))]) {
|
|
427
|
+
const candidate = path.join(candidateRoot, "cli.bat");
|
|
428
|
+
if (await fs.stat(candidate).then((stat) => stat.isFile(), () => false)) candidates.add(candidate);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const seen = /* @__PURE__ */ new Set();
|
|
432
|
+
const result = [];
|
|
433
|
+
for (const cliPath of candidates) try {
|
|
434
|
+
const target = await inspectWechatDevtoolsInstallation(cliPath, options);
|
|
435
|
+
if (!seen.has(target.installationId)) {
|
|
436
|
+
seen.add(target.installationId);
|
|
437
|
+
result.push({
|
|
438
|
+
cliPath: target.cliPath,
|
|
439
|
+
target
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (!explicitCandidates.has(cliPath)) continue;
|
|
444
|
+
if (error.code === "WECHAT_DEVTOOLS_UNRELATED_APPLICATION" && !registered.includes(cliPath) && cliPath !== config.cliPath && cliPath !== process.env.WEAPP_IDE_CLI_PATH) continue;
|
|
445
|
+
result.push({
|
|
446
|
+
cliPath,
|
|
447
|
+
error: error instanceof Error ? error.message : String(error)
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
return result.sort((a, b) => compareWechatDevtoolsVersions(b.target?.version ?? "0", a.target?.version ?? "0") || a.cliPath.localeCompare(b.cliPath));
|
|
451
|
+
}
|
|
452
|
+
/** 渠道必须显式 opt-in;找不到或存在同版本歧义时不静默换安装。 */
|
|
453
|
+
async function selectInstalledWechatDevtools(options = {}) {
|
|
454
|
+
const channel = options.devtoolsChannel ?? (options.devtoolsVersion ? void 0 : "stable");
|
|
455
|
+
const matches = (await listWechatDevtoolsInstallations(options)).flatMap((entry) => entry.target?.version && (!channel || entry.target.channel === channel) && (!options.devtoolsVersion || entry.target.version === options.devtoolsVersion) ? [entry.target] : []);
|
|
456
|
+
if (!matches.length) throw Object.assign(/* @__PURE__ */ new Error(`No installed WeChat DevTools matches ${channel ?? "any channel"}${options.devtoolsVersion ? `@${options.devtoolsVersion}` : ""}. Register its CLI with weapp devtools add <path-to-cli>.`), { code: "WECHAT_DEVTOOLS_INSTALLATION_NOT_FOUND" });
|
|
457
|
+
if (matches.filter((target) => compareWechatDevtoolsVersions(target.version, matches[0].version) === 0).length !== 1) throw Object.assign(/* @__PURE__ */ new Error("Multiple DevTools installations match the selected version. Specify --cli-path."), { code: "WECHAT_DEVTOOLS_INSTALLATION_AMBIGUOUS" });
|
|
458
|
+
return matches[0];
|
|
459
|
+
}
|
|
460
|
+
//#endregion
|
|
461
|
+
//#region src/devtoolsTarget/official.ts
|
|
462
|
+
const WECHAT_DEVTOOLS_OFFICIAL_CHANNELS_URL = "https://devtools.wxqcloud.qq.com.cn/WechatWebDev/nightly/versions/config.json";
|
|
463
|
+
/** 官方查询只提供版本证据,默认选择不依赖网络可用性。 */
|
|
464
|
+
async function readOfficialWechatDevtoolsVersion(channel = "stable") {
|
|
465
|
+
const queriedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
466
|
+
const response = await fetch(WECHAT_DEVTOOLS_OFFICIAL_CHANNELS_URL, { signal: AbortSignal.timeout(15e3) });
|
|
467
|
+
if (!response.ok) throw new Error(`Unable to query official DevTools versions: HTTP ${response.status}`);
|
|
468
|
+
const value = await response.json();
|
|
469
|
+
const entry = (value && typeof value === "object" && "channels" in value && Array.isArray(value.channels) ? value.channels : []).find((entry) => entry && typeof entry === "object" && "id" in entry && entry.id === channel);
|
|
470
|
+
if (!entry || typeof entry !== "object" || !("version" in entry) || typeof entry.version !== "string" || !/^\d+(?:\.\d+){2,}$/.test(entry.version)) throw new Error(`Official DevTools metadata has no valid ${channel} version.`);
|
|
471
|
+
return {
|
|
472
|
+
version: entry.version,
|
|
473
|
+
source: WECHAT_DEVTOOLS_OFFICIAL_CHANNELS_URL,
|
|
474
|
+
queriedAt
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
/** 仅显式严格模式要求所选安装匹配官方渠道最新版本。 */
|
|
478
|
+
async function assertOfficialWechatDevtoolsVersion(target) {
|
|
479
|
+
if (!target.channel || target.channel === "unknown") throw new Error("Cannot verify the official latest version for an unknown DevTools channel.");
|
|
480
|
+
const official = await readOfficialWechatDevtoolsVersion(target.channel);
|
|
481
|
+
if (target.version !== official.version) throw new Error(`DEVTOOLS_OFFICIAL_LATEST_REQUIRED: selected ${target.version}, official ${target.channel}@${official.version}.`);
|
|
482
|
+
return official;
|
|
354
483
|
}
|
|
355
484
|
//#endregion
|
|
356
485
|
//#region src/devtoolsTarget/inspection.ts
|
|
357
|
-
/** Windows
|
|
486
|
+
/** Windows 查询预算包含 PowerShell 冷启动及内核/CIM 初始化;显式较小预算始终优先。 */
|
|
358
487
|
function resolveWechatInspectionTimeout(platform, timeout) {
|
|
359
488
|
if (timeout !== void 0 && (Number.isNaN(timeout) || timeout <= 0)) throw new RangeError("DevTools inspection timeout must be positive.");
|
|
360
|
-
const maximum = platform === "win32" ?
|
|
489
|
+
const maximum = platform === "win32" ? 3e4 : 3e3;
|
|
361
490
|
return Math.min(timeout ?? maximum, maximum);
|
|
362
491
|
}
|
|
363
492
|
//#endregion
|
|
@@ -366,8 +495,7 @@ function ownershipError(message) {
|
|
|
366
495
|
return Object.assign(new Error(message), { code: "WECHAT_DEVTOOLS_HOST_IDENTITY_MISMATCH" });
|
|
367
496
|
}
|
|
368
497
|
function normalize(value, platform) {
|
|
369
|
-
|
|
370
|
-
return platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
498
|
+
return (platform === "win32" ? value.replaceAll("\\", "/").toLowerCase() : value).replace(/\/$/, "");
|
|
371
499
|
}
|
|
372
500
|
/** 复用宿主归属检查的安装根规则,不能把 app.asar 当作可执行文件目录。 */
|
|
373
501
|
function resolveWechatDevtoolsInstallationRoot(target, platform) {
|
|
@@ -493,53 +621,76 @@ async function assertWechatDevtoolsPort(target, port, options = {}) {
|
|
|
493
621
|
}
|
|
494
622
|
//#endregion
|
|
495
623
|
//#region src/devtoolsTarget/index.ts
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
624
|
+
function conflict(message) {
|
|
625
|
+
throw Object.assign(new Error(message), { code: "WECHAT_DEVTOOLS_INSTALLATION_SELECTION_CONFLICT" });
|
|
626
|
+
}
|
|
627
|
+
function validateWechatDevtoolsSelection(options) {
|
|
628
|
+
if (options.cliPath !== void 0 && !options.cliPath.trim()) throw new Error("cliPath must not be empty.");
|
|
629
|
+
if (options.devtoolsChannel !== void 0 && ![
|
|
630
|
+
"stable",
|
|
631
|
+
"rc",
|
|
632
|
+
"nightly"
|
|
633
|
+
].includes(options.devtoolsChannel)) throw new Error("devtoolsChannel must be stable, rc or nightly.");
|
|
634
|
+
if (options.devtoolsVersion !== void 0 && !/^\d+(?:\.\d+){2,}$/.test(options.devtoolsVersion)) throw new Error("devtoolsVersion must be a complete DevTools product version.");
|
|
635
|
+
}
|
|
636
|
+
async function assertSelection(target, options) {
|
|
637
|
+
if (options.cliPath?.trim() && await fs.realpath(options.cliPath.trim()) !== await fs.realpath(target.cliPath)) conflict("The explicit CLI path conflicts with the selected DevTools installation; no installation was changed.");
|
|
638
|
+
if (options.devtoolsChannel && options.devtoolsChannel !== target.channel) conflict("The requested DevTools channel conflicts with the selected installation.");
|
|
639
|
+
if (options.devtoolsVersion && options.devtoolsVersion !== target.version) conflict("The requested DevTools version conflicts with the selected installation.");
|
|
640
|
+
if (options.requireOfficialLatest) await assertOfficialWechatDevtoolsVersion(target);
|
|
641
|
+
return target;
|
|
642
|
+
}
|
|
643
|
+
function readWechatDevtoolsSelectionEnvironment(env = process.env) {
|
|
644
|
+
const options = {
|
|
645
|
+
cliPath: env.WEAPP_IDE_CLI_PATH?.trim() || void 0,
|
|
646
|
+
devtoolsChannel: env.WEAPP_IDE_DEVTOOLS_CHANNEL?.trim() || void 0,
|
|
647
|
+
devtoolsVersion: env.WEAPP_IDE_DEVTOOLS_VERSION?.trim() || void 0,
|
|
648
|
+
requireOfficialLatest: env.WEAPP_IDE_REQUIRE_OFFICIAL_LATEST === "1"
|
|
649
|
+
};
|
|
650
|
+
validateWechatDevtoolsSelection(options);
|
|
651
|
+
return options;
|
|
515
652
|
}
|
|
516
|
-
/**
|
|
653
|
+
/** 每次操作只解析一次安装,显式选择覆盖继承的配置但不写回全局设置。 */
|
|
517
654
|
async function resolveWechatDevtoolsTarget(options = {}) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
655
|
+
validateWechatDevtoolsSelection(options);
|
|
656
|
+
const active = wechatDevtoolsSelectionContext.getStore();
|
|
657
|
+
if (active && options.target && active.installationId !== options.target.installationId) conflict("The fixed target conflicts with the active operation installation.");
|
|
658
|
+
const frozen = active ?? options.target;
|
|
659
|
+
if (frozen) return await assertSelection(frozen, options);
|
|
660
|
+
let selection = { ...options };
|
|
661
|
+
if (!options.cliPath?.trim() && !options.devtoolsChannel && !options.devtoolsVersion) {
|
|
662
|
+
selection = readWechatDevtoolsSelectionEnvironment();
|
|
663
|
+
selection.requireOfficialLatest = options.requireOfficialLatest ?? selection.requireOfficialLatest;
|
|
664
|
+
if (!selection.cliPath && !selection.devtoolsChannel && !selection.devtoolsVersion) selection = {
|
|
665
|
+
...selection,
|
|
666
|
+
cliPath: (await readCustomConfig()).cliPath
|
|
667
|
+
};
|
|
525
668
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
669
|
+
selection.requireOfficialLatest ??= process.env.WEAPP_IDE_REQUIRE_OFFICIAL_LATEST === "1";
|
|
670
|
+
return await assertSelection(selection.cliPath?.trim() ? await inspectWechatDevtoolsInstallation(selection.cliPath.trim(), options) : await selectInstalledWechatDevtools({
|
|
671
|
+
...options,
|
|
672
|
+
...selection
|
|
673
|
+
}), selection);
|
|
674
|
+
}
|
|
675
|
+
/** 命令的所有异步阶段复用同一安装,彼此并发的调用不会污染环境变量。 */
|
|
676
|
+
async function withWechatDevtoolsSelection(options, runner) {
|
|
677
|
+
const target = await resolveWechatDevtoolsTarget(options);
|
|
678
|
+
return await wechatDevtoolsSelectionContext.run(target, () => runner(target));
|
|
679
|
+
}
|
|
680
|
+
/** 读取当前操作固定的安装;没有活动选择时不触发发现或启动。 */
|
|
681
|
+
function getActiveWechatDevtoolsTarget() {
|
|
682
|
+
return wechatDevtoolsSelectionContext.getStore();
|
|
539
683
|
}
|
|
540
684
|
//#endregion
|
|
685
|
+
//#region src/devtoolsProjectOwnership/journal/constants.ts
|
|
686
|
+
/** 受管项目日志的环境变量名称;基础设施预检可在构建包之前读取。 */
|
|
687
|
+
const MANAGED_PROJECT_JOURNAL_ENV = "WEAPP_IDE_MANAGED_PROJECT_JOURNAL";
|
|
688
|
+
/** 仅供显式受管任务声明双项目窗口验收;不允许无限扩大并行窗口。 */
|
|
689
|
+
const MANAGED_PROJECT_MAX_WINDOWS_ENV = "WEAPP_IDE_MANAGED_PROJECT_MAX_WINDOWS";
|
|
690
|
+
//#endregion
|
|
541
691
|
//#region src/cli/automator/sessionStore.ts
|
|
542
692
|
const SESSION_DIRECTORY = path.join(os.tmpdir(), "weapp-vite-automator-sessions");
|
|
693
|
+
/** 内部工具复用会话键;不从包入口公开缓存路径契约。 */
|
|
543
694
|
function resolveAutomatorSessionFilePath(options) {
|
|
544
695
|
const key = JSON.stringify([
|
|
545
696
|
options.installationId,
|
|
@@ -602,4 +753,129 @@ async function readPersistedAutomatorSession(options) {
|
|
|
602
753
|
}
|
|
603
754
|
}
|
|
604
755
|
//#endregion
|
|
605
|
-
|
|
756
|
+
//#region src/cli/automatorProject.ts
|
|
757
|
+
const WRAPPER_ROOT = path.join(os.tmpdir(), "weapp-ide-cli-automator-projects");
|
|
758
|
+
function normalizeProjectRelativeRoot(rawRoot) {
|
|
759
|
+
if (typeof rawRoot !== "string") return;
|
|
760
|
+
const normalized = rawRoot.trim().replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
761
|
+
if (!normalized || normalized === ".") return;
|
|
762
|
+
if (normalized.split("/").includes("..")) return;
|
|
763
|
+
return normalized;
|
|
764
|
+
}
|
|
765
|
+
async function pathExists(filePath) {
|
|
766
|
+
try {
|
|
767
|
+
await fs.access(filePath);
|
|
768
|
+
return true;
|
|
769
|
+
} catch {
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
async function readJsonObject(filePath) {
|
|
774
|
+
try {
|
|
775
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
776
|
+
const value = JSON.parse(raw);
|
|
777
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
778
|
+
} catch {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
async function writeJsonObject(filePath, value) {
|
|
783
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
784
|
+
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
785
|
+
}
|
|
786
|
+
async function copyProjectRoot(sourceProjectPath, wrapperProjectPath, relativeRoot) {
|
|
787
|
+
const sourcePath = path.join(sourceProjectPath, relativeRoot);
|
|
788
|
+
if (!await pathExists(sourcePath)) return;
|
|
789
|
+
await fs.cp(sourcePath, path.join(wrapperProjectPath, relativeRoot), {
|
|
790
|
+
dereference: true,
|
|
791
|
+
force: true,
|
|
792
|
+
recursive: true
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
function createWrapperProjectConfig(source, patch) {
|
|
796
|
+
const { miniprogramRoot: _miniprogramRoot, qcloudRoot: _qcloudRoot, srcMiniprogramRoot: _srcMiniprogramRoot, ...rest } = source;
|
|
797
|
+
const sourceSetting = source.setting && typeof source.setting === "object" ? source.setting : {};
|
|
798
|
+
const patchSetting = patch.setting && typeof patch.setting === "object" ? patch.setting : {};
|
|
799
|
+
return {
|
|
800
|
+
...rest,
|
|
801
|
+
...patch,
|
|
802
|
+
setting: {
|
|
803
|
+
...sourceSetting,
|
|
804
|
+
...patchSetting,
|
|
805
|
+
packNpmManually: false,
|
|
806
|
+
packNpmRelationList: []
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
async function ensureWrapperAppConfig(wrapperProjectPath) {
|
|
811
|
+
const appConfigPath = path.join(wrapperProjectPath, "app.json");
|
|
812
|
+
const appConfig = await readJsonObject(appConfigPath);
|
|
813
|
+
if (!appConfig) return;
|
|
814
|
+
await writeJsonObject(appConfigPath, {
|
|
815
|
+
...appConfig,
|
|
816
|
+
subPackages: Array.isArray(appConfig.subPackages) ? appConfig.subPackages : []
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
/** 只读解析布局,受管路径必须先通过窗口预算再创建快照。 */
|
|
820
|
+
async function readAutomatorProjectPlan(projectPath) {
|
|
821
|
+
const sourceProjectPath = path.resolve(projectPath);
|
|
822
|
+
const projectConfig = await readJsonObject(path.join(sourceProjectPath, "project.config.json"));
|
|
823
|
+
const miniprogramRoot = normalizeProjectRelativeRoot(projectConfig?.miniprogramRoot);
|
|
824
|
+
if (!miniprogramRoot) return;
|
|
825
|
+
const distRoot = path.join(sourceProjectPath, miniprogramRoot);
|
|
826
|
+
if (!await pathExists(path.join(distRoot, "app.json"))) return;
|
|
827
|
+
return {
|
|
828
|
+
sourceProjectPath,
|
|
829
|
+
distRoot,
|
|
830
|
+
projectConfig: projectConfig ?? {}
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
/** 仅转换已分配快照的布局;目录所有权与删除由调用方持有。 */
|
|
834
|
+
async function populateAutomatorProjectSnapshot(plan, wrapperProjectPath) {
|
|
835
|
+
const { sourceProjectPath, distRoot, projectConfig: sourceProjectConfig } = plan;
|
|
836
|
+
await fs.cp(distRoot, wrapperProjectPath, {
|
|
837
|
+
dereference: true,
|
|
838
|
+
force: true,
|
|
839
|
+
recursive: true
|
|
840
|
+
});
|
|
841
|
+
const pluginRoot = normalizeProjectRelativeRoot(sourceProjectConfig.pluginRoot);
|
|
842
|
+
if (pluginRoot) await copyProjectRoot(sourceProjectPath, wrapperProjectPath, pluginRoot);
|
|
843
|
+
const projectPrivateConfig = await readJsonObject(path.join(sourceProjectPath, "project.private.config.json")) ?? {};
|
|
844
|
+
await writeJsonObject(path.join(wrapperProjectPath, "project.config.json"), createWrapperProjectConfig(sourceProjectConfig, {
|
|
845
|
+
...projectPrivateConfig,
|
|
846
|
+
compileType: sourceProjectConfig.compileType === "plugin" ? "plugin" : "miniprogram",
|
|
847
|
+
miniprogramRoot: "./",
|
|
848
|
+
srcMiniprogramRoot: "./"
|
|
849
|
+
}));
|
|
850
|
+
await fs.rm(path.join(wrapperProjectPath, "project.private.config.json"), { force: true });
|
|
851
|
+
await ensureWrapperAppConfig(wrapperProjectPath);
|
|
852
|
+
}
|
|
853
|
+
/** 为普通非受管 automator 保留既有稳定项目路径合同。 */
|
|
854
|
+
async function resolveAutomatorProjectPath(projectPath) {
|
|
855
|
+
const sourceProjectPath = path.resolve(projectPath);
|
|
856
|
+
const plan = await readAutomatorProjectPlan(sourceProjectPath);
|
|
857
|
+
if (!plan) return {
|
|
858
|
+
projectPath: sourceProjectPath,
|
|
859
|
+
sourceProjectPath
|
|
860
|
+
};
|
|
861
|
+
const wrapperHash = createHash("sha1").update(sourceProjectPath).update("\0").update(path.resolve(plan.distRoot)).digest("hex").slice(0, 16);
|
|
862
|
+
const wrapperProjectPath = path.join(WRAPPER_ROOT, wrapperHash);
|
|
863
|
+
await fs.rm(wrapperProjectPath, {
|
|
864
|
+
recursive: true,
|
|
865
|
+
force: true
|
|
866
|
+
});
|
|
867
|
+
await fs.mkdir(wrapperProjectPath, { recursive: true });
|
|
868
|
+
await populateAutomatorProjectSnapshot(plan, wrapperProjectPath);
|
|
869
|
+
return {
|
|
870
|
+
cleanup: async () => {
|
|
871
|
+
await fs.rm(wrapperProjectPath, {
|
|
872
|
+
recursive: true,
|
|
873
|
+
force: true
|
|
874
|
+
});
|
|
875
|
+
},
|
|
876
|
+
projectPath: wrapperProjectPath,
|
|
877
|
+
sourceProjectPath
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
//#endregion
|
|
881
|
+
export { operatingSystemName as A, removeCustomConfigKey as B, registerWechatDevtoolsInstallation as C, SupportedPlatformsMap as D, withPowerShellUtf8Output as E, createAutoTrustProjectConfig as F, defaultCustomConfigFilePath as H, createCustomConfig as I, createLocaleConfig as L, logger as M, wechatDevtoolsSelectionContext as N, getDefaultCliPath as O, createAutoBootstrapDevtoolsConfig as P, overwriteCustomConfig as R, selectInstalledWechatDevtools as S, inspectWechatDevtoolsInstallation as T, resolvePath as U, defaultCustomConfigDirPath as V, WECHAT_DEVTOOLS_OFFICIAL_CHANNELS_URL as _, readPersistedAutomatorSession as a, compareWechatDevtoolsVersions as b, getActiveWechatDevtoolsTarget as c, validateWechatDevtoolsSelection as d, withWechatDevtoolsSelection as f, resolveWechatInspectionTimeout as g, resolveWechatDevtoolsInstallationRoot as h, persistAutomatorSession as i, colors as j, isOperatingSystemSupported as k, readWechatDevtoolsSelectionEnvironment as l, assertWechatDevtoolsPort as m, readAutomatorProjectPlan as n, MANAGED_PROJECT_JOURNAL_ENV as o, assertWechatDevtoolsHost as p, resolveAutomatorProjectPath as r, MANAGED_PROJECT_MAX_WINDOWS_ENV as s, populateAutomatorProjectSnapshot as t, resolveWechatDevtoolsTarget as u, assertOfficialWechatDevtoolsVersion as v, unregisterWechatDevtoolsInstallation as w, listWechatDevtoolsInstallations as x, readOfficialWechatDevtoolsVersion as y, readCustomConfig as z };
|