weapp-vite 7.3.0 → 7.4.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 +104 -6
- package/dist/adapters-BwnMMIZR.mjs +2 -0
- package/dist/auto-routes.mjs +1 -1
- package/dist/cli.mjs +594 -631
- package/dist/{config-1J4rToMR.d.mts → config-Dbvj5yK7.d.mts} +332 -9
- package/dist/config.d.mts +2 -2
- package/dist/constants-WOsG3Eqd.mjs +85 -0
- package/dist/{createContext-cqr9Y4EV.mjs → createContext-UOEgeYnA.mjs} +3637 -1051
- package/dist/dashboard/vite.d.mts +9 -0
- package/dist/dashboard/vite.mjs +44 -0
- package/dist/dashboard-BzaVesmQ.mjs +558 -0
- package/dist/dashboard.d.mts +2 -0
- package/dist/dashboard.mjs +2 -0
- package/dist/docs/README.md +104 -6
- package/dist/docs/getting-started.md +50 -1
- package/dist/docs/index.md +3 -1
- package/dist/docs/upload.md +189 -0
- package/dist/docs/weapp-config.md +320 -2
- package/dist/file-D19nbDoN.mjs +2 -0
- package/dist/{file-CKB0Tn8Z.mjs → file-giE9UMZa.mjs} +3 -86
- package/dist/getInstance-CYF1vl5l.mjs +2 -0
- package/dist/index-DbqVupg0.d.mts +246 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/json.d.mts +1 -1
- package/dist/mcp.d.mts +1 -1
- package/dist/platform-DTWs5QRi.mjs +175 -0
- package/dist/test.mjs +1 -1
- package/dist/types.d.mts +2 -2
- package/dist/upload-D0RcEWrq.mjs +403 -0
- package/dist/upload-worker.d.mts +1 -0
- package/dist/upload-worker.mjs +28 -0
- package/package.json +46 -24
- package/dist/file-CvgkbGIx.mjs +0 -2
- package/dist/getInstance-CZ1s63zd.mjs +0 -2
package/dist/cli.mjs
CHANGED
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { f as
|
|
1
|
+
import { a as resolveWeappViteHostMeta } from "./pluginHost--CaeyWpA.mjs";
|
|
2
|
+
import { C as collectPreloadPages, D as loadViteConfigFile, E as suggestPreloadRules, O as createCjsConfigLoadError, S as getProjectConfigFileName, T as resolvePreloadPackageIdentifier, _ as SHARED_CHUNK_VIRTUAL_PREFIX, a as formatBytes, b as resolveWeappConfigFile, d as getBackendForCapability, f as resolveBackendExecution, g as createSharedBuildConfig, h as createGlassEaselAnalyzeResult, k as isPathInside, m as syncManagedTsconfigBootstrapFiles, p as syncProjectSupportFiles, t as createCompilerContext, v as resolveHmrProfileJsonPath, w as findPreloadRuleKey, x as checkRuntime } from "./createContext-UOEgeYnA.mjs";
|
|
3
|
+
import { l as getProjectPlatformOptions, n as getDefaultIdeProjectRoot, w as shouldPassPlatformArgToIdeOpen } from "./platform-DTWs5QRi.mjs";
|
|
4
|
+
import { i as findJsEntry, o as findTemplateEntry, s as findVueEntry } from "./file-giE9UMZa.mjs";
|
|
5
|
+
import { t as VERSION } from "./constants-WOsG3Eqd.mjs";
|
|
3
6
|
import { r as logger_default, t as colors } from "./logger-mt4mSTqV.mjs";
|
|
7
|
+
import { a as createDashboardArtifactSnapshot, i as resolveDashboardRoot, n as ANALYZE_DASHBOARD_PACKAGE_NAME, t as createAnalyzeDashboardDevframe } from "./dashboard-BzaVesmQ.mjs";
|
|
8
|
+
import { n as resolveUploadPlatforms, r as redactUploadSecrets, t as prepareUpload } from "./upload-D0RcEWrq.mjs";
|
|
4
9
|
import { c as startWeappViteMcpServer, l as detectAiDevelopmentEnvironment, s as resolveWeappMcpConfig } from "./mcp-BG6TliEg.mjs";
|
|
5
|
-
import { createRequire } from "node:module";
|
|
6
|
-
import fs from "node:fs";
|
|
7
10
|
import path, { posix } from "pathe";
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
8
13
|
import path$1 from "node:path";
|
|
9
14
|
import { defu } from "@weapp-core/shared";
|
|
10
15
|
import { fs as fs$1 } from "@weapp-core/shared/fs";
|
|
11
16
|
import process from "node:process";
|
|
12
17
|
import { parse } from "vue/compiler-sfc";
|
|
13
|
-
import fs$2, { mkdir, writeFile } from "node:fs/promises";
|
|
18
|
+
import fs$2, { access, mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
14
19
|
import { build, createServer } from "vite";
|
|
15
|
-
import {
|
|
16
|
-
import { promisify } from "node:util";
|
|
20
|
+
import { randomUUID } from "node:crypto";
|
|
21
|
+
import { parseArgs, parseEnv, promisify } from "node:util";
|
|
17
22
|
import os from "node:os";
|
|
18
|
-
import { execFile, spawn } from "node:child_process";
|
|
23
|
+
import { execFile, fork, spawn } from "node:child_process";
|
|
19
24
|
import { Buffer } from "node:buffer";
|
|
20
25
|
import { cac } from "cac";
|
|
21
26
|
import { RETRY_CANCEL_KEYS, RETRY_CONFIRM_KEYS, bootstrapWechatDevtoolsSettings, buildWechatIdeNpm, clearWechatIdeCache, clearWechatIdeCacheByAutomator, closeSharedMiniProgram, closeWechatIdeProject, compileWechatIdeByAutomator, connectOpenedAutomator, createSharedInputSession, createWechatIdeLoginRequiredExitError, defaultCustomConfigFilePath, detectWechatDevtoolsServicePort, dispatchWechatCliCommand, formatAutomatorLoginError, getConfig, getWechatIdeTestAccounts, getWechatIdeTicket, getWechatIdeToolInfo, isAutomatorLoginError, isRetryableAutomatorLaunchError, isWeappIdeTopLevelCommand, isWechatIdeEngineBuildEndpointMissingError, isWechatIdeLoggedIn, isWechatIdeLoginRequiredError, isWechatIdeLoginRequiredExitError, launchAutomator, parse as parse$1, promptRetryKeypress, promptWechatIdeLoginRetry, quitWechatIde, refreshWechatIdeTicket, resetWechatIdeFileUtilsByHttp, resolveCliPath, resolveProjectAutomatorPort, runRetryableCommand, runWechatIdeEngineBuild, runWithSuspendedSharedInput, setWechatIdeTicket, startForwardConsole, takeScreenshot } from "weapp-ide-cli";
|
|
22
27
|
import { brotliCompressSync, gzipSync } from "node:zlib";
|
|
28
|
+
import { defineRpcFunction } from "devframe";
|
|
23
29
|
import { buildOtpAuthUrl, refreshTempAuthCode } from "devframe/node/auth";
|
|
24
30
|
import { resolveCommand } from "package-manager-detector/commands";
|
|
25
|
-
import { defineDevframe, defineRpcFunction } from "devframe";
|
|
26
31
|
import { Server } from "node:http";
|
|
32
|
+
import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_SSE_ROUTE, DEVFRAME_WS_ROUTE } from "devframe/constants";
|
|
27
33
|
import { initDevframe } from "devframe/initiate";
|
|
34
|
+
import semverInc from "semver/functions/inc.js";
|
|
35
|
+
import { x } from "tinyexec";
|
|
36
|
+
import { expand } from "dotenv-expand";
|
|
28
37
|
import { generateJs, generateJson, generateWxml, generateWxss } from "@weapp-core/schematics";
|
|
29
38
|
import { determineAgent } from "@vercel/detect-agent";
|
|
30
39
|
import { initConfig } from "@weapp-core/init";
|
|
@@ -449,10 +458,11 @@ async function runWechatIdeOpenWithRetry(argv) {
|
|
|
449
458
|
});
|
|
450
459
|
}
|
|
451
460
|
/**
|
|
452
|
-
* @description
|
|
461
|
+
* @description 根据代码输出目录推导 IDE 项目目录;内联项目配置与 app.json 同目录。
|
|
453
462
|
*/
|
|
454
|
-
function resolveIdeProjectPath(mpDistRoot) {
|
|
463
|
+
function resolveIdeProjectPath(mpDistRoot, generatedProjectConfig = false) {
|
|
455
464
|
if (!mpDistRoot || !mpDistRoot.trim()) return;
|
|
465
|
+
if (generatedProjectConfig) return mpDistRoot;
|
|
456
466
|
const parent = path.dirname(mpDistRoot);
|
|
457
467
|
if (!parent || parent === "." || parent === "/") return;
|
|
458
468
|
return parent;
|
|
@@ -460,8 +470,9 @@ function resolveIdeProjectPath(mpDistRoot) {
|
|
|
460
470
|
/**
|
|
461
471
|
* @description 结合 mpDistRoot 与配置根目录解析最终 IDE 项目目录。
|
|
462
472
|
*/
|
|
463
|
-
function resolveIdeProjectRoot(mpDistRoot, cwd) {
|
|
464
|
-
|
|
473
|
+
function resolveIdeProjectRoot(mpDistRoot, cwd, generatedProjectConfig = false) {
|
|
474
|
+
const projectPath = resolveIdeProjectPath(mpDistRoot, generatedProjectConfig);
|
|
475
|
+
return generatedProjectConfig && projectPath && cwd ? path.resolve(cwd, projectPath) : projectPath ?? cwd;
|
|
465
476
|
}
|
|
466
477
|
async function closeIde() {
|
|
467
478
|
return await closeIde$1();
|
|
@@ -722,7 +733,7 @@ async function resolveIdeCommandContext(options) {
|
|
|
722
733
|
cliPlatform: options.cliPlatform
|
|
723
734
|
});
|
|
724
735
|
platform ??= ctx.configService.platform;
|
|
725
|
-
if (!projectPath) projectPath = resolveIdeProjectRoot(ctx.configService.mpDistRoot, ctx.configService.cwd);
|
|
736
|
+
if (!projectPath) projectPath = resolveIdeProjectRoot(ctx.configService.mpDistRoot, ctx.configService.cwd, ctx.configService.multiPlatform.projectConfigs !== void 0);
|
|
726
737
|
return {
|
|
727
738
|
cwd: ctx.configService.cwd,
|
|
728
739
|
platform,
|
|
@@ -2259,316 +2270,7 @@ function createAnalyzeMarkdownReport(result, previousResult) {
|
|
|
2259
2270
|
].join("\n");
|
|
2260
2271
|
}
|
|
2261
2272
|
//#endregion
|
|
2262
|
-
//#region src/cli/analyze/
|
|
2263
|
-
const MAX_DASHBOARD_FILE_CONTENT_BYTES = 2097152;
|
|
2264
|
-
/** 保留同一次分析的产物内容,不读取或改写开发构建目录。 */
|
|
2265
|
-
function createDashboardArtifactSnapshot() {
|
|
2266
|
-
const files = /* @__PURE__ */ new Map();
|
|
2267
|
-
let retainedBytes = 0;
|
|
2268
|
-
return {
|
|
2269
|
-
files,
|
|
2270
|
-
capture(fileName, source) {
|
|
2271
|
-
const key = fileName.replaceAll("\\", "/");
|
|
2272
|
-
const previous = files.get(key);
|
|
2273
|
-
if (previous?.content !== void 0) retainedBytes -= previous.size;
|
|
2274
|
-
const size = typeof source === "string" ? Buffer.byteLength(source, "utf8") : source.byteLength;
|
|
2275
|
-
if (size > 2097152) {
|
|
2276
|
-
files.set(key, {
|
|
2277
|
-
size,
|
|
2278
|
-
error: `文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`
|
|
2279
|
-
});
|
|
2280
|
-
return;
|
|
2281
|
-
}
|
|
2282
|
-
if (retainedBytes + size > 33554432) {
|
|
2283
|
-
files.set(key, {
|
|
2284
|
-
size,
|
|
2285
|
-
error: "分析产物内容超过快照容量上限,已拒绝读取。"
|
|
2286
|
-
});
|
|
2287
|
-
return;
|
|
2288
|
-
}
|
|
2289
|
-
const content = typeof source === "string" ? source : Buffer.from(source.buffer, source.byteOffset, source.byteLength).toString("utf8");
|
|
2290
|
-
files.set(key, {
|
|
2291
|
-
content,
|
|
2292
|
-
size
|
|
2293
|
-
});
|
|
2294
|
-
retainedBytes += size;
|
|
2295
|
-
}
|
|
2296
|
-
};
|
|
2297
|
-
}
|
|
2298
|
-
//#endregion
|
|
2299
|
-
//#region src/cli/analyze/dashboardDevframe/paths.ts
|
|
2300
|
-
function normalizeDashboardRelativePath(value) {
|
|
2301
|
-
return value.replaceAll("\\", "/");
|
|
2302
|
-
}
|
|
2303
|
-
function stripDashboardFileQuery(value) {
|
|
2304
|
-
const queryIndex = value.indexOf("?");
|
|
2305
|
-
return queryIndex === -1 ? value : value.slice(0, queryIndex);
|
|
2306
|
-
}
|
|
2307
|
-
function addDashboardAllowedPath(paths, value) {
|
|
2308
|
-
if (!value || value.includes("\0")) return;
|
|
2309
|
-
const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(value));
|
|
2310
|
-
if (!normalizedPath || path.isAbsolute(normalizedPath)) return;
|
|
2311
|
-
paths.add(normalizedPath);
|
|
2312
|
-
}
|
|
2313
|
-
function addDashboardAllowedSourcePath(paths, value, rootKind) {
|
|
2314
|
-
if (!value || value.includes("\0")) return;
|
|
2315
|
-
const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(value));
|
|
2316
|
-
if (!normalizedPath || path.isAbsolute(normalizedPath)) return;
|
|
2317
|
-
const rootKinds = paths.get(normalizedPath) ?? /* @__PURE__ */ new Set();
|
|
2318
|
-
rootKinds.add(rootKind);
|
|
2319
|
-
paths.set(normalizedPath, rootKinds);
|
|
2320
|
-
}
|
|
2321
|
-
function resolveDashboardSourceRootKind(sourceType) {
|
|
2322
|
-
if (sourceType === "plugin") return "plugin";
|
|
2323
|
-
if (sourceType === "workspace" || sourceType === "node_modules") return "project";
|
|
2324
|
-
return "src";
|
|
2325
|
-
}
|
|
2326
|
-
function createDashboardContentAllowlist(result) {
|
|
2327
|
-
const artifactPaths = /* @__PURE__ */ new Set();
|
|
2328
|
-
const sourcePaths = /* @__PURE__ */ new Map();
|
|
2329
|
-
for (const packageReport of result.packages) for (const file of packageReport.files) {
|
|
2330
|
-
addDashboardAllowedPath(artifactPaths, file.file);
|
|
2331
|
-
const fileSourceType = file.sourceType ?? file.modules?.find((module) => module.source === file.source)?.sourceType;
|
|
2332
|
-
addDashboardAllowedSourcePath(sourcePaths, file.source, resolveDashboardSourceRootKind(fileSourceType));
|
|
2333
|
-
for (const module of file.modules ?? []) addDashboardAllowedSourcePath(sourcePaths, module.source, module.sourceType === "src" ? "src" : module.sourceType === "plugin" ? "plugin" : "project");
|
|
2334
|
-
}
|
|
2335
|
-
return {
|
|
2336
|
-
artifactPaths,
|
|
2337
|
-
sourcePaths
|
|
2338
|
-
};
|
|
2339
|
-
}
|
|
2340
|
-
function resolveDashboardContentPath(root, requestPath, options) {
|
|
2341
|
-
if (!root || !requestPath || requestPath.includes("\0")) return;
|
|
2342
|
-
const normalizedRequestPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
|
|
2343
|
-
if (path.isAbsolute(normalizedRequestPath) || !options.allowedPaths.has(normalizedRequestPath)) return;
|
|
2344
|
-
const rootPath = path.resolve(root);
|
|
2345
|
-
const absolutePath = path.resolve(rootPath, normalizedRequestPath);
|
|
2346
|
-
const relativePath = path.relative(rootPath, absolutePath);
|
|
2347
|
-
const allowParent = options.allowParent === true;
|
|
2348
|
-
if (!relativePath || !allowParent && (relativePath.startsWith("..") || path.isAbsolute(relativePath))) return;
|
|
2349
|
-
return {
|
|
2350
|
-
absolutePath,
|
|
2351
|
-
allowParent,
|
|
2352
|
-
relativePath: allowParent ? normalizedRequestPath : normalizeDashboardRelativePath(relativePath),
|
|
2353
|
-
rootPath
|
|
2354
|
-
};
|
|
2355
|
-
}
|
|
2356
|
-
function resolveDashboardSourceContentPaths(roots, requestPath, allowedPaths) {
|
|
2357
|
-
const normalizedRequestPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
|
|
2358
|
-
const rootKinds = allowedPaths.get(normalizedRequestPath);
|
|
2359
|
-
if (!rootKinds || rootKinds.size !== 1) return [];
|
|
2360
|
-
const [rootKind] = rootKinds;
|
|
2361
|
-
if (rootKind === "project") {
|
|
2362
|
-
const resolved = resolveDashboardContentPath(roots.projectRoot, normalizedRequestPath, {
|
|
2363
|
-
allowParent: true,
|
|
2364
|
-
allowedPaths: /* @__PURE__ */ new Set([normalizedRequestPath])
|
|
2365
|
-
});
|
|
2366
|
-
return resolved ? [resolved] : [];
|
|
2367
|
-
}
|
|
2368
|
-
if (rootKind === "plugin") {
|
|
2369
|
-
if (!roots.pluginRoot) return [];
|
|
2370
|
-
const pluginBase = path.basename(roots.pluginRoot);
|
|
2371
|
-
if (normalizedRequestPath !== pluginBase && !normalizedRequestPath.startsWith(`${pluginBase}/`)) return [];
|
|
2372
|
-
const pluginRelativePath = normalizedRequestPath === pluginBase ? "" : normalizedRequestPath.slice(pluginBase.length + 1);
|
|
2373
|
-
const resolved = resolveDashboardContentPath(roots.pluginRoot, pluginRelativePath, { allowedPaths: /* @__PURE__ */ new Set([pluginRelativePath]) });
|
|
2374
|
-
return resolved ? [{
|
|
2375
|
-
...resolved,
|
|
2376
|
-
relativePath: normalizedRequestPath
|
|
2377
|
-
}] : [];
|
|
2378
|
-
}
|
|
2379
|
-
if (!roots.srcRoot) return [];
|
|
2380
|
-
const candidates = (normalizedRequestPath.startsWith("src/") ? [normalizedRequestPath, normalizedRequestPath.slice(4)] : [normalizedRequestPath]).flatMap((relativePath) => {
|
|
2381
|
-
const resolved = resolveDashboardContentPath(roots.srcRoot, relativePath, { allowedPaths: /* @__PURE__ */ new Set([relativePath]) });
|
|
2382
|
-
return resolved ? [{
|
|
2383
|
-
...resolved,
|
|
2384
|
-
relativePath: normalizedRequestPath
|
|
2385
|
-
}] : [];
|
|
2386
|
-
});
|
|
2387
|
-
return [...new Map(candidates.map((candidate) => [candidate.absolutePath, candidate])).values()];
|
|
2388
|
-
}
|
|
2389
|
-
function resolveDashboardArtifactPath(requestPath, allowedPaths) {
|
|
2390
|
-
const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
|
|
2391
|
-
if (!normalizedPath || normalizedPath.includes("\0") || path.isAbsolute(normalizedPath) || path.posix.normalize(normalizedPath).startsWith("..") || !allowedPaths.has(normalizedPath)) return;
|
|
2392
|
-
return normalizedPath;
|
|
2393
|
-
}
|
|
2394
|
-
//#endregion
|
|
2395
|
-
//#region src/cli/analyze/dashboardDevframe/content.ts
|
|
2396
|
-
function isPathWithin(rootPath, targetPath) {
|
|
2397
|
-
const relativePath = path.relative(rootPath, targetPath);
|
|
2398
|
-
return !relativePath || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
|
2399
|
-
}
|
|
2400
|
-
function resolveInspectionRoot(resolved) {
|
|
2401
|
-
let inspectionRoot = resolved.rootPath;
|
|
2402
|
-
if (!resolved.allowParent) return inspectionRoot;
|
|
2403
|
-
while (!isPathWithin(inspectionRoot, resolved.absolutePath)) {
|
|
2404
|
-
const parent = path.dirname(inspectionRoot);
|
|
2405
|
-
if (parent === inspectionRoot) return;
|
|
2406
|
-
inspectionRoot = parent;
|
|
2407
|
-
}
|
|
2408
|
-
return inspectionRoot;
|
|
2409
|
-
}
|
|
2410
|
-
async function assertDashboardPathHasNoSymlink(resolved) {
|
|
2411
|
-
const inspectionRoot = resolveInspectionRoot(resolved);
|
|
2412
|
-
if (!inspectionRoot) throw new Error("文件路径超出允许范围。");
|
|
2413
|
-
const relativePath = path.relative(inspectionRoot, resolved.absolutePath);
|
|
2414
|
-
let currentPath = inspectionRoot;
|
|
2415
|
-
for (const segment of relativePath.split(path.sep).filter(Boolean)) {
|
|
2416
|
-
currentPath = path.join(currentPath, segment);
|
|
2417
|
-
if ((await fs.promises.lstat(currentPath)).isSymbolicLink()) throw new Error("文件路径包含不允许的符号链接。");
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
function isSameOpenedFile(pathStat, handleStat) {
|
|
2421
|
-
if (pathStat.dev !== handleStat.dev) return false;
|
|
2422
|
-
return pathStat.ino === 0 || handleStat.ino === 0 || pathStat.ino === handleStat.ino;
|
|
2423
|
-
}
|
|
2424
|
-
async function openDashboardFile(resolved) {
|
|
2425
|
-
await assertDashboardPathHasNoSymlink(resolved);
|
|
2426
|
-
const flags = process.platform === "win32" ? fs.constants.O_RDONLY : fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW;
|
|
2427
|
-
const handle = await fs.promises.open(resolved.absolutePath, flags);
|
|
2428
|
-
try {
|
|
2429
|
-
await assertDashboardPathHasNoSymlink(resolved);
|
|
2430
|
-
const [pathStat, handleStat] = await Promise.all([fs.promises.stat(resolved.absolutePath), handle.stat()]);
|
|
2431
|
-
if (!isSameOpenedFile(pathStat, handleStat)) throw new Error("文件路径在读取期间发生变化。");
|
|
2432
|
-
return handle;
|
|
2433
|
-
} catch (error) {
|
|
2434
|
-
await handle.close();
|
|
2435
|
-
throw error;
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
async function readDashboardFileHandle(handle) {
|
|
2439
|
-
const stat = await handle.stat();
|
|
2440
|
-
if (!stat.isFile()) throw new Error("目标路径不是文件。");
|
|
2441
|
-
if (stat.size > 2097152) throw new Error(`文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`);
|
|
2442
|
-
let content = Buffer.allocUnsafe(Math.min(Math.max(stat.size + 1, 1), 2097153));
|
|
2443
|
-
let offset = 0;
|
|
2444
|
-
while (true) {
|
|
2445
|
-
if (offset === content.length) {
|
|
2446
|
-
if (content.length === 2097153) throw new Error(`文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`);
|
|
2447
|
-
const expanded = Buffer.allocUnsafe(Math.min(content.length * 2, 2097153));
|
|
2448
|
-
content.copy(expanded, 0, 0, offset);
|
|
2449
|
-
content = expanded;
|
|
2450
|
-
}
|
|
2451
|
-
const { bytesRead } = await handle.read(content, offset, content.length - offset, offset);
|
|
2452
|
-
if (bytesRead === 0) break;
|
|
2453
|
-
offset += bytesRead;
|
|
2454
|
-
if (offset > 2097152) throw new Error(`文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`);
|
|
2455
|
-
}
|
|
2456
|
-
return {
|
|
2457
|
-
content: content.subarray(0, offset).toString("utf8"),
|
|
2458
|
-
size: offset
|
|
2459
|
-
};
|
|
2460
|
-
}
|
|
2461
|
-
function resolveDashboardFileLanguage(filePath) {
|
|
2462
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
2463
|
-
if ([
|
|
2464
|
-
".js",
|
|
2465
|
-
".mjs",
|
|
2466
|
-
".cjs",
|
|
2467
|
-
".wxs",
|
|
2468
|
-
".sjs"
|
|
2469
|
-
].includes(extension)) return "javascript";
|
|
2470
|
-
if ([
|
|
2471
|
-
".ts",
|
|
2472
|
-
".mts",
|
|
2473
|
-
".cts"
|
|
2474
|
-
].includes(extension)) return "typescript";
|
|
2475
|
-
if ([".json", ".map"].includes(extension)) return "json";
|
|
2476
|
-
if ([
|
|
2477
|
-
".css",
|
|
2478
|
-
".wxss",
|
|
2479
|
-
".scss",
|
|
2480
|
-
".sass",
|
|
2481
|
-
".less"
|
|
2482
|
-
].includes(extension)) return "css";
|
|
2483
|
-
if ([
|
|
2484
|
-
".vue",
|
|
2485
|
-
".wxml",
|
|
2486
|
-
".html"
|
|
2487
|
-
].includes(extension)) return "html";
|
|
2488
|
-
return "plaintext";
|
|
2489
|
-
}
|
|
2490
|
-
function normalizeDashboardFileRequest(input) {
|
|
2491
|
-
if (!input || typeof input !== "object" || !("kind" in input) || !("path" in input)) throw new Error("必须传入合法的 kind 和相对路径。");
|
|
2492
|
-
const kind = input.kind;
|
|
2493
|
-
const requestPath = input.path;
|
|
2494
|
-
if (kind !== "source" && kind !== "artifact" || typeof requestPath !== "string") throw new Error("必须传入合法的 kind 和相对路径。");
|
|
2495
|
-
return {
|
|
2496
|
-
kind,
|
|
2497
|
-
path: requestPath
|
|
2498
|
-
};
|
|
2499
|
-
}
|
|
2500
|
-
async function readAllowedDashboardFile(input, roots, allowlist, artifacts) {
|
|
2501
|
-
const request = normalizeDashboardFileRequest(input);
|
|
2502
|
-
if (request.kind === "artifact") {
|
|
2503
|
-
const filePath = resolveDashboardArtifactPath(request.path, allowlist.artifactPaths);
|
|
2504
|
-
if (!filePath) throw new Error("必须传入合法的 kind 和相对路径。");
|
|
2505
|
-
const file = artifacts.get(filePath);
|
|
2506
|
-
if (!file) throw new Error("分析快照中没有此产物内容。");
|
|
2507
|
-
if (file.error || file.content === void 0) throw new Error(file.error ?? "分析快照中没有此产物内容。");
|
|
2508
|
-
return {
|
|
2509
|
-
kind: request.kind,
|
|
2510
|
-
path: filePath,
|
|
2511
|
-
language: resolveDashboardFileLanguage(filePath),
|
|
2512
|
-
size: file.size,
|
|
2513
|
-
content: file.content
|
|
2514
|
-
};
|
|
2515
|
-
}
|
|
2516
|
-
const resolvedCandidates = resolveDashboardSourceContentPaths(roots, request.path, allowlist.sourcePaths);
|
|
2517
|
-
if (resolvedCandidates.length === 0) throw new Error("必须传入合法的 kind 和相对路径。");
|
|
2518
|
-
const openedCandidates = [];
|
|
2519
|
-
try {
|
|
2520
|
-
for (const resolved of resolvedCandidates) try {
|
|
2521
|
-
openedCandidates.push({
|
|
2522
|
-
handle: await openDashboardFile(resolved),
|
|
2523
|
-
resolved
|
|
2524
|
-
});
|
|
2525
|
-
} catch (error) {
|
|
2526
|
-
if ((typeof error === "object" && error && "code" in error ? String(error.code) : "") !== "ENOENT") throw error;
|
|
2527
|
-
}
|
|
2528
|
-
if (openedCandidates.length === 0) throw new Error("文件不存在。");
|
|
2529
|
-
if (openedCandidates.length > 1) throw new Error("源码路径存在多个候选文件,已拒绝读取。");
|
|
2530
|
-
const [{ handle, resolved }] = openedCandidates;
|
|
2531
|
-
const file = await readDashboardFileHandle(handle);
|
|
2532
|
-
return {
|
|
2533
|
-
kind: request.kind,
|
|
2534
|
-
path: resolved.relativePath,
|
|
2535
|
-
language: resolveDashboardFileLanguage(resolved.relativePath),
|
|
2536
|
-
size: file.size,
|
|
2537
|
-
content: file.content
|
|
2538
|
-
};
|
|
2539
|
-
} catch (error) {
|
|
2540
|
-
if (error instanceof Error && (error.message === "文件不存在。" || error.message === "源码路径存在多个候选文件,已拒绝读取。" || error.message === "目标路径不是文件。" || error.message.startsWith("文件超过 ") || error.message.startsWith("文件路径"))) throw error;
|
|
2541
|
-
throw new Error("读取文件失败。");
|
|
2542
|
-
} finally {
|
|
2543
|
-
await Promise.allSettled(openedCandidates.map((candidate) => candidate.handle.close()));
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
function createDashboardFileReader(roots, result, artifacts) {
|
|
2547
|
-
let allowlist = createDashboardContentAllowlist(result);
|
|
2548
|
-
let currentArtifacts = artifacts;
|
|
2549
|
-
let disposed = false;
|
|
2550
|
-
return {
|
|
2551
|
-
read: async (input) => {
|
|
2552
|
-
if (disposed) throw new Error("Dashboard 文件读取会话已关闭。");
|
|
2553
|
-
return await readAllowedDashboardFile(input, roots, allowlist, currentArtifacts);
|
|
2554
|
-
},
|
|
2555
|
-
update(nextResult, nextArtifacts) {
|
|
2556
|
-
allowlist = createDashboardContentAllowlist(nextResult);
|
|
2557
|
-
currentArtifacts = nextArtifacts;
|
|
2558
|
-
},
|
|
2559
|
-
dispose() {
|
|
2560
|
-
disposed = true;
|
|
2561
|
-
currentArtifacts = /* @__PURE__ */ new Map();
|
|
2562
|
-
allowlist.artifactPaths.clear();
|
|
2563
|
-
allowlist.sourcePaths.clear();
|
|
2564
|
-
}
|
|
2565
|
-
};
|
|
2566
|
-
}
|
|
2567
|
-
//#endregion
|
|
2568
|
-
//#region src/cli/analyze/dashboardDevframe/index.ts
|
|
2569
|
-
const DEVFRAME_ID = "weapp-vite";
|
|
2570
|
-
const MAX_DASHBOARD_ANALYZE_PAGE_CHARACTERS = 65536;
|
|
2571
|
-
const STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE = "Analyze revision 已变化,请重新获取 Dashboard 状态。";
|
|
2273
|
+
//#region src/cli/analyze/dashboardPolicy.ts
|
|
2572
2274
|
const rejectDashboardSharedStateSet = defineRpcFunction({
|
|
2573
2275
|
name: "devframe:rpc:server-state:set",
|
|
2574
2276
|
type: "action",
|
|
@@ -2583,145 +2285,30 @@ const rejectDashboardSharedStatePatch = defineRpcFunction({
|
|
|
2583
2285
|
throw new Error("Dashboard Devframe shared state 只允许服务端修改。");
|
|
2584
2286
|
}
|
|
2585
2287
|
});
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
return {
|
|
2589
|
-
descriptor: {
|
|
2590
|
-
characters: value.length,
|
|
2591
|
-
hash: createHash("sha256").update(value).digest("hex"),
|
|
2592
|
-
pages: Math.max(1, Math.ceil(value.length / MAX_DASHBOARD_ANALYZE_PAGE_CHARACTERS))
|
|
2593
|
-
},
|
|
2594
|
-
source: result,
|
|
2595
|
-
value
|
|
2596
|
-
};
|
|
2597
|
-
}
|
|
2598
|
-
function serializeDashboardAnalyzeSnapshot(snapshot, cached) {
|
|
2599
|
-
const previous = snapshot.previous ? cached?.current.source === snapshot.previous ? cached.current : cached?.previous?.source === snapshot.previous ? cached.previous : serializeDashboardAnalyzePayload(snapshot.previous) : null;
|
|
2600
|
-
return {
|
|
2601
|
-
current: cached?.current.source === snapshot.current ? cached.current : serializeDashboardAnalyzePayload(snapshot.current),
|
|
2602
|
-
previous
|
|
2603
|
-
};
|
|
2604
|
-
}
|
|
2605
|
-
function createDashboardState(revision, snapshot, runtimeEvents) {
|
|
2606
|
-
return {
|
|
2607
|
-
analyze: {
|
|
2608
|
-
current: snapshot.current.descriptor,
|
|
2609
|
-
previous: snapshot.previous?.descriptor ?? null
|
|
2610
|
-
},
|
|
2611
|
-
revision,
|
|
2612
|
-
runtimeEvents: [...runtimeEvents]
|
|
2613
|
-
};
|
|
2614
|
-
}
|
|
2615
|
-
function normalizeAnalyzePageRequest(input) {
|
|
2616
|
-
if (!input || typeof input !== "object") throw new Error("必须传入合法的 Analyze 分页请求。");
|
|
2617
|
-
if (!("revision" in input) || !("target" in input) || !("index" in input)) throw new Error("必须传入合法的 Analyze 分页请求。");
|
|
2618
|
-
const revision = input.revision;
|
|
2619
|
-
const target = input.target;
|
|
2620
|
-
const index = input.index;
|
|
2621
|
-
if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 0 || target !== "current" && target !== "previous" || typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) throw new Error("必须传入合法的 Analyze 分页请求。");
|
|
2622
|
-
return {
|
|
2623
|
-
revision,
|
|
2624
|
-
target,
|
|
2625
|
-
index
|
|
2626
|
-
};
|
|
2627
|
-
}
|
|
2628
|
-
function readDashboardAnalyzePage(input, revision, snapshot) {
|
|
2629
|
-
const request = normalizeAnalyzePageRequest(input);
|
|
2630
|
-
if (request.revision !== revision) throw new Error(STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE);
|
|
2631
|
-
const payload = request.target === "current" ? snapshot.current : snapshot.previous;
|
|
2632
|
-
if (!payload || request.index >= payload.descriptor.pages) throw new Error("Analyze 分页不存在。");
|
|
2633
|
-
const start = request.index * MAX_DASHBOARD_ANALYZE_PAGE_CHARACTERS;
|
|
2288
|
+
/** 独立宿主在开放 RPC 前禁止全局共享状态写入,不影响嵌入宿主的策略。 */
|
|
2289
|
+
function withStandaloneDashboardPolicy(definition) {
|
|
2634
2290
|
return {
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
};
|
|
2641
|
-
}
|
|
2642
|
-
function createAnalyzeDashboardDevframe(options) {
|
|
2643
|
-
let revision = 0;
|
|
2644
|
-
let disposed = false;
|
|
2645
|
-
let serializedSnapshot = serializeDashboardAnalyzeSnapshot(options.getAnalyzeSnapshot());
|
|
2646
|
-
let broadcastDashboardState;
|
|
2647
|
-
const initialSnapshot = options.getAnalyzeSnapshot();
|
|
2648
|
-
const fileReader = createDashboardFileReader(options.roots, initialSnapshot.current, initialSnapshot.artifacts);
|
|
2649
|
-
const getDashboardState = defineRpcFunction({
|
|
2650
|
-
name: "get-dashboard-state",
|
|
2651
|
-
type: "query",
|
|
2652
|
-
jsonSerializable: true,
|
|
2653
|
-
handler: () => createDashboardState(revision, serializedSnapshot, options.getRuntimeEvents())
|
|
2654
|
-
});
|
|
2655
|
-
const getAnalyzePage = defineRpcFunction({
|
|
2656
|
-
name: "get-analyze-page",
|
|
2657
|
-
type: "query",
|
|
2658
|
-
jsonSerializable: true,
|
|
2659
|
-
handler: (input) => readDashboardAnalyzePage(input, revision, serializedSnapshot)
|
|
2660
|
-
});
|
|
2661
|
-
const readDashboardFile = defineRpcFunction({
|
|
2662
|
-
name: "read-dashboard-file",
|
|
2663
|
-
type: "query",
|
|
2664
|
-
jsonSerializable: true,
|
|
2665
|
-
handler: async (input) => {
|
|
2666
|
-
if (!input || typeof input !== "object" || !("revision" in input) || typeof input.revision !== "number" || !Number.isSafeInteger(input.revision) || input.revision < 0) throw new Error("必须传入合法的文件读取 revision。");
|
|
2667
|
-
const requestedRevision = input.revision;
|
|
2668
|
-
if (disposed || requestedRevision !== revision) throw new Error(STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE);
|
|
2669
|
-
const content = await fileReader.read(input);
|
|
2670
|
-
if (disposed || requestedRevision !== revision) throw new Error(STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE);
|
|
2671
|
-
return content;
|
|
2672
|
-
}
|
|
2673
|
-
});
|
|
2674
|
-
return {
|
|
2675
|
-
definition: defineDevframe({
|
|
2676
|
-
id: DEVFRAME_ID,
|
|
2677
|
-
name: "weapp-vite",
|
|
2678
|
-
version: VERSION,
|
|
2679
|
-
packageName: "weapp-vite",
|
|
2680
|
-
importMetaUrl: import.meta.url,
|
|
2681
|
-
homepage: "https://vite.weapp.dev/",
|
|
2682
|
-
description: "weapp-vite 构建分析与小程序开发工具。",
|
|
2683
|
-
icon: "ph:rocket-launch-duotone",
|
|
2684
|
-
async setup(ctx) {
|
|
2685
|
-
ctx.rpc.register(rejectDashboardSharedStateSet, true);
|
|
2686
|
-
ctx.rpc.register(rejectDashboardSharedStatePatch, true);
|
|
2687
|
-
const dashboard = ctx.scope(DEVFRAME_ID);
|
|
2688
|
-
dashboard.rpc.register(getDashboardState);
|
|
2689
|
-
dashboard.rpc.register(getAnalyzePage);
|
|
2690
|
-
dashboard.rpc.register(readDashboardFile);
|
|
2691
|
-
broadcastDashboardState = async () => {
|
|
2692
|
-
await dashboard.rpc.broadcast({
|
|
2693
|
-
method: "dashboard-state-updated",
|
|
2694
|
-
args: [createDashboardState(revision, serializedSnapshot, options.getRuntimeEvents())],
|
|
2695
|
-
event: true
|
|
2696
|
-
});
|
|
2697
|
-
};
|
|
2698
|
-
}
|
|
2699
|
-
}),
|
|
2700
|
-
notifyAnalyzeUpdate() {
|
|
2701
|
-
if (disposed) return;
|
|
2702
|
-
revision += 1;
|
|
2703
|
-
const snapshot = options.getAnalyzeSnapshot();
|
|
2704
|
-
serializedSnapshot = serializeDashboardAnalyzeSnapshot(snapshot, serializedSnapshot);
|
|
2705
|
-
fileReader.update(snapshot.current, snapshot.artifacts);
|
|
2706
|
-
broadcastDashboardState?.();
|
|
2707
|
-
},
|
|
2708
|
-
syncRuntimeEvents() {
|
|
2709
|
-
broadcastDashboardState?.();
|
|
2710
|
-
},
|
|
2711
|
-
dispose() {
|
|
2712
|
-
disposed = true;
|
|
2713
|
-
fileReader.dispose();
|
|
2714
|
-
broadcastDashboardState = void 0;
|
|
2291
|
+
...definition,
|
|
2292
|
+
async setup(ctx, info) {
|
|
2293
|
+
ctx.rpc.register(rejectDashboardSharedStateSet, true);
|
|
2294
|
+
ctx.rpc.register(rejectDashboardSharedStatePatch, true);
|
|
2295
|
+
await definition.setup?.(ctx, info);
|
|
2715
2296
|
}
|
|
2716
2297
|
};
|
|
2717
2298
|
}
|
|
2718
2299
|
//#endregion
|
|
2719
2300
|
//#region src/cli/analyze/dashboardViteBridge.ts
|
|
2720
2301
|
const ANALYZE_DASHBOARD_DEVFRAME_BASE = "/__weapp-vite/";
|
|
2721
|
-
|
|
2302
|
+
const transportPaths = /* @__PURE__ */ new Set([
|
|
2303
|
+
`${ANALYZE_DASHBOARD_DEVFRAME_BASE}${DEVFRAME_CONNECTION_META_FILENAME}`,
|
|
2304
|
+
`${ANALYZE_DASHBOARD_DEVFRAME_BASE}${DEVFRAME_SSE_ROUTE}`,
|
|
2305
|
+
`${ANALYZE_DASHBOARD_DEVFRAME_BASE}${DEVFRAME_WS_ROUTE}`
|
|
2306
|
+
]);
|
|
2307
|
+
function createAnalyzeDashboardViteBridge(controller) {
|
|
2722
2308
|
let instance;
|
|
2723
2309
|
let closing;
|
|
2724
2310
|
const closeInstance = async () => {
|
|
2311
|
+
controller.dispose();
|
|
2725
2312
|
if (closing) return await closing;
|
|
2726
2313
|
const current = instance;
|
|
2727
2314
|
instance = void 0;
|
|
@@ -2735,9 +2322,8 @@ function createAnalyzeDashboardViteBridge(definition) {
|
|
|
2735
2322
|
name: "weapp-vite:dashboard-devframe",
|
|
2736
2323
|
apply: "serve",
|
|
2737
2324
|
async configureServer(server) {
|
|
2738
|
-
await closeInstance();
|
|
2739
2325
|
const httpServer = server.httpServer instanceof Server ? server.httpServer : void 0;
|
|
2740
|
-
const created = initDevframe(definition, {
|
|
2326
|
+
const created = initDevframe(withStandaloneDashboardPolicy(controller.definition), {
|
|
2741
2327
|
base: ANALYZE_DASHBOARD_DEVFRAME_BASE,
|
|
2742
2328
|
distDir: false,
|
|
2743
2329
|
...httpServer ? { server: httpServer } : {
|
|
@@ -2748,12 +2334,19 @@ function createAnalyzeDashboardViteBridge(definition) {
|
|
|
2748
2334
|
auth: true,
|
|
2749
2335
|
mcp: false
|
|
2750
2336
|
});
|
|
2751
|
-
server.middlewares.use(created.nodeMiddleware);
|
|
2752
2337
|
try {
|
|
2338
|
+
server.middlewares.use((request, response, next) => {
|
|
2339
|
+
const url = request.url ?? "/";
|
|
2340
|
+
const queryIndex = url.indexOf("?");
|
|
2341
|
+
const pathname = queryIndex < 0 ? url : url.slice(0, queryIndex);
|
|
2342
|
+
if (transportPaths.has(pathname)) created.nodeMiddleware(request, response, next);
|
|
2343
|
+
else next();
|
|
2344
|
+
});
|
|
2753
2345
|
await created.ready;
|
|
2754
2346
|
instance = created;
|
|
2755
2347
|
} catch (error) {
|
|
2756
|
-
|
|
2348
|
+
controller.dispose();
|
|
2349
|
+
await created.close().catch(() => {});
|
|
2757
2350
|
throw error;
|
|
2758
2351
|
}
|
|
2759
2352
|
server.httpServer?.once("close", () => {
|
|
@@ -2767,195 +2360,109 @@ function createAnalyzeDashboardViteBridge(definition) {
|
|
|
2767
2360
|
}
|
|
2768
2361
|
//#endregion
|
|
2769
2362
|
//#region src/cli/analyze/dashboard.ts
|
|
2770
|
-
const ANALYZE_DASHBOARD_PACKAGE_NAME = "@weapp-vite/dashboard";
|
|
2771
|
-
const require = createRequire(import.meta.url);
|
|
2772
2363
|
function createInstallCommand(agent) {
|
|
2773
2364
|
const resolved = resolveCommand(agent ?? "npm", "install", [ANALYZE_DASHBOARD_PACKAGE_NAME]);
|
|
2774
2365
|
if (!resolved) return `npm install ${ANALYZE_DASHBOARD_PACKAGE_NAME}`;
|
|
2775
2366
|
return `${resolved.command} ${resolved.args.join(" ")}`;
|
|
2776
2367
|
}
|
|
2777
|
-
function formatEventTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
2778
|
-
return date.toLocaleTimeString("zh-CN", { hour12: false });
|
|
2779
|
-
}
|
|
2780
|
-
function createDashboardRuntimeEvent(input) {
|
|
2781
|
-
return {
|
|
2782
|
-
id: `dashboard:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`,
|
|
2783
|
-
kind: input.kind,
|
|
2784
|
-
level: input.level,
|
|
2785
|
-
title: input.title,
|
|
2786
|
-
detail: input.detail,
|
|
2787
|
-
timestamp: formatEventTimestamp(),
|
|
2788
|
-
source: input.source ?? "weapp-vite",
|
|
2789
|
-
durationMs: input.durationMs,
|
|
2790
|
-
tags: input.tags,
|
|
2791
|
-
profile: input.profile
|
|
2792
|
-
};
|
|
2793
|
-
}
|
|
2794
|
-
function readDashboardManifest(packageJsonPath) {
|
|
2795
|
-
try {
|
|
2796
|
-
return parseCommentJson(fs.readFileSync(packageJsonPath, "utf8"));
|
|
2797
|
-
} catch {
|
|
2798
|
-
return;
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
function resolveDashboardDistRoot(packageRoot, manifest) {
|
|
2802
|
-
const distDir = manifest?.weappViteDashboard?.distDir ?? "dist";
|
|
2803
|
-
const distRoot = path.resolve(packageRoot, distDir);
|
|
2804
|
-
if (!fs.existsSync(distRoot)) return;
|
|
2805
|
-
return { root: distRoot };
|
|
2806
|
-
}
|
|
2807
|
-
function resolveDashboardDevRoot(packageRoot, manifest) {
|
|
2808
|
-
const devRoot = manifest?.weappViteDashboard?.devRoot;
|
|
2809
|
-
const devConfigFile = manifest?.weappViteDashboard?.devConfigFile;
|
|
2810
|
-
if (!devRoot || !devConfigFile) return;
|
|
2811
|
-
const root = path.resolve(packageRoot, devRoot);
|
|
2812
|
-
const configFile = path.resolve(root, devConfigFile);
|
|
2813
|
-
if (!fs.existsSync(root) || !fs.existsSync(configFile)) return;
|
|
2814
|
-
return {
|
|
2815
|
-
root,
|
|
2816
|
-
configFile
|
|
2817
|
-
};
|
|
2818
|
-
}
|
|
2819
|
-
function resolveDashboardRoot(options) {
|
|
2820
|
-
const resolvePaths = options?.cwd && options.cwd !== process.cwd() ? [options.cwd, process.cwd()] : options?.cwd ? [options.cwd] : void 0;
|
|
2821
|
-
let dashboardPackageRoot;
|
|
2822
|
-
let dashboardManifest;
|
|
2823
|
-
try {
|
|
2824
|
-
const dashboardPackageJsonPath = require.resolve(`${ANALYZE_DASHBOARD_PACKAGE_NAME}/package.json`, { paths: resolvePaths });
|
|
2825
|
-
dashboardPackageRoot = path.dirname(dashboardPackageJsonPath);
|
|
2826
|
-
dashboardManifest = readDashboardManifest(dashboardPackageJsonPath);
|
|
2827
|
-
} catch {
|
|
2828
|
-
dashboardPackageRoot = void 0;
|
|
2829
|
-
dashboardManifest = void 0;
|
|
2830
|
-
}
|
|
2831
|
-
if (dashboardPackageRoot) {
|
|
2832
|
-
const devResolved = resolveDashboardDevRoot(dashboardPackageRoot, dashboardManifest);
|
|
2833
|
-
const distResolved = resolveDashboardDistRoot(dashboardPackageRoot, dashboardManifest);
|
|
2834
|
-
const resolved = options?.watch ? devResolved ?? distResolved : distResolved ?? devResolved;
|
|
2835
|
-
if (resolved) return resolved;
|
|
2836
|
-
}
|
|
2837
|
-
logger_default.warn(`[weapp-vite ui] 未安装可选仪表盘包 ${colors.bold(colors.green(ANALYZE_DASHBOARD_PACKAGE_NAME))},已自动降级关闭 dashboard 能力。`);
|
|
2838
|
-
logger_default.info(`如需启用,请执行 ${colors.bold(colors.green(createInstallCommand(options?.packageManagerAgent)))}`);
|
|
2839
|
-
}
|
|
2840
|
-
async function waitForServerExit(server) {
|
|
2841
|
-
let resolved = false;
|
|
2842
|
-
const cleanup = async () => {
|
|
2843
|
-
if (resolved) return;
|
|
2844
|
-
resolved = true;
|
|
2845
|
-
try {
|
|
2846
|
-
await server.close();
|
|
2847
|
-
} catch (error) {
|
|
2848
|
-
logger_default.error(error);
|
|
2849
|
-
}
|
|
2850
|
-
};
|
|
2851
|
-
const signals = ["SIGINT", "SIGTERM"];
|
|
2852
|
-
await new Promise((resolvePromise) => {
|
|
2853
|
-
const resolveOnce = async () => {
|
|
2854
|
-
await cleanup();
|
|
2855
|
-
signals.forEach((signal) => {
|
|
2856
|
-
process.removeListener(signal, resolveOnce);
|
|
2857
|
-
});
|
|
2858
|
-
resolvePromise();
|
|
2859
|
-
};
|
|
2860
|
-
signals.forEach((signal) => {
|
|
2861
|
-
process.once(signal, resolveOnce);
|
|
2862
|
-
});
|
|
2863
|
-
server.httpServer?.once("close", resolveOnce);
|
|
2864
|
-
});
|
|
2865
|
-
}
|
|
2866
2368
|
async function startAnalyzeDashboard(result, options) {
|
|
2867
2369
|
const resolved = resolveDashboardRoot(options);
|
|
2868
|
-
if (!resolved)
|
|
2370
|
+
if (!resolved) {
|
|
2371
|
+
logger_default.warn(`[weapp-vite ui] 未安装可选仪表盘包 ${colors.bold(colors.green(ANALYZE_DASHBOARD_PACKAGE_NAME))},已自动降级关闭 dashboard 能力。`);
|
|
2372
|
+
logger_default.info(`如需启用,请执行 ${colors.bold(colors.green(createInstallCommand(options.packageManagerAgent)))}`);
|
|
2373
|
+
return;
|
|
2374
|
+
}
|
|
2869
2375
|
const { root, configFile } = resolved;
|
|
2870
|
-
const state = {
|
|
2871
|
-
current: result,
|
|
2872
|
-
previous: options.previousResult ?? null,
|
|
2873
|
-
artifacts: options.artifacts
|
|
2874
|
-
};
|
|
2875
|
-
const runtimeEvents = { current: [createDashboardRuntimeEvent({
|
|
2876
|
-
kind: "command",
|
|
2877
|
-
level: "success",
|
|
2878
|
-
title: options?.watch ? "dashboard watch session started" : "dashboard static session started",
|
|
2879
|
-
detail: options?.watch ? "weapp-vite UI 已进入实时分析模式,后续 analyze 结果会继续推送到 dashboard。" : "weapp-vite UI 已进入静态分析模式,当前页面展示的是一次性分析结果。",
|
|
2880
|
-
tags: options?.watch ? ["watch", "analyze"] : ["static", "analyze"]
|
|
2881
|
-
}), ...(options?.initialEvents ?? []).map((event) => createDashboardRuntimeEvent(event))] };
|
|
2882
2376
|
const devframe = createAnalyzeDashboardDevframe({
|
|
2883
|
-
|
|
2884
|
-
|
|
2377
|
+
snapshot: {
|
|
2378
|
+
current: result,
|
|
2379
|
+
previous: options.previousResult ?? null,
|
|
2380
|
+
artifacts: options.artifacts
|
|
2381
|
+
},
|
|
2382
|
+
initialEvents: [{
|
|
2383
|
+
kind: "command",
|
|
2384
|
+
level: "success",
|
|
2385
|
+
title: options.watch ? "dashboard watch session started" : "dashboard static session started",
|
|
2386
|
+
detail: options.watch ? "weapp-vite UI 已进入实时分析模式,后续 analyze 结果会继续推送到 dashboard。" : "weapp-vite UI 已进入静态分析模式,当前页面展示的是一次性分析结果。",
|
|
2387
|
+
tags: options.watch ? ["watch", "analyze"] : ["static", "analyze"]
|
|
2388
|
+
}, ...options.initialEvents ?? []],
|
|
2885
2389
|
roots: {
|
|
2886
|
-
pluginRoot: options
|
|
2887
|
-
projectRoot: options
|
|
2888
|
-
srcRoot: options
|
|
2390
|
+
pluginRoot: options.pluginRoot,
|
|
2391
|
+
projectRoot: options.cwd,
|
|
2392
|
+
srcRoot: options.srcRoot ?? (options.cwd ? path.resolve(options.cwd, "src") : void 0)
|
|
2889
2393
|
}
|
|
2890
2394
|
});
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2395
|
+
let server;
|
|
2396
|
+
try {
|
|
2397
|
+
server = await createServer({
|
|
2398
|
+
root,
|
|
2399
|
+
base: ANALYZE_DASHBOARD_DEVFRAME_BASE,
|
|
2400
|
+
configFile: configFile ?? false,
|
|
2401
|
+
clearScreen: false,
|
|
2402
|
+
appType: "spa",
|
|
2403
|
+
publicDir: false,
|
|
2404
|
+
plugins: [{
|
|
2405
|
+
name: "weapp-vite:dashboard-lifetime",
|
|
2406
|
+
enforce: "pre",
|
|
2407
|
+
configureServer(createdServer) {
|
|
2408
|
+
server = createdServer;
|
|
2409
|
+
}
|
|
2410
|
+
}, createAnalyzeDashboardViteBridge(devframe)],
|
|
2411
|
+
server: {
|
|
2412
|
+
host: "127.0.0.1",
|
|
2413
|
+
port: 0,
|
|
2414
|
+
watch: { ignored: ["**/*"] }
|
|
2415
|
+
},
|
|
2416
|
+
logLevel: "error"
|
|
2417
|
+
});
|
|
2418
|
+
await server.listen(0);
|
|
2419
|
+
} catch (error) {
|
|
2420
|
+
devframe.dispose();
|
|
2421
|
+
await server?.close().catch((closeError) => logger_default.error(closeError));
|
|
2422
|
+
throw error;
|
|
2423
|
+
}
|
|
2424
|
+
const activeServer = server;
|
|
2425
|
+
const urls = [...activeServer.resolvedUrls?.local ?? [], ...activeServer.resolvedUrls?.network ?? []];
|
|
2914
2426
|
const authCode = refreshTempAuthCode();
|
|
2915
2427
|
const authenticatedUrls = urls.map((url) => buildOtpAuthUrl(url, authCode));
|
|
2916
|
-
let
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
devframe.dispose();
|
|
2428
|
+
let closing;
|
|
2429
|
+
let resolveExit;
|
|
2430
|
+
const waitPromise = new Promise((resolve) => {
|
|
2431
|
+
resolveExit = resolve;
|
|
2921
2432
|
});
|
|
2922
|
-
const
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2433
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
2434
|
+
let onExit;
|
|
2435
|
+
const close = () => {
|
|
2436
|
+
if (!closing) {
|
|
2437
|
+
devframe.dispose();
|
|
2438
|
+
closing = Promise.resolve().then(() => activeServer.close()).finally(() => {
|
|
2439
|
+
for (const signal of signals) process.removeListener(signal, onExit);
|
|
2440
|
+
activeServer.httpServer?.removeListener("close", onExit);
|
|
2441
|
+
resolveExit();
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
return closing;
|
|
2445
|
+
};
|
|
2446
|
+
onExit = async () => {
|
|
2447
|
+
await close().catch((error) => logger_default.error(error));
|
|
2927
2448
|
};
|
|
2449
|
+
for (const signal of signals) process.once(signal, onExit);
|
|
2450
|
+
activeServer.httpServer?.once("close", onExit);
|
|
2928
2451
|
const handle = {
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
state.previous = previousResult ?? state.current;
|
|
2932
|
-
state.current = nextResult;
|
|
2933
|
-
state.artifacts = artifacts;
|
|
2934
|
-
emitRuntimeEvents([{
|
|
2935
|
-
kind: "build",
|
|
2936
|
-
level: "info",
|
|
2937
|
-
title: "analyze payload refreshed",
|
|
2938
|
-
detail: `已推送新的 analyze 结果,当前包含 ${nextResult.packages.length} 个包与 ${nextResult.modules.length} 个模块。`,
|
|
2939
|
-
tags: ["analyze", "refresh"]
|
|
2940
|
-
}]);
|
|
2941
|
-
devframe.notifyAnalyzeUpdate();
|
|
2942
|
-
},
|
|
2943
|
-
emitRuntimeEvents,
|
|
2452
|
+
update: devframe.update,
|
|
2453
|
+
emitRuntimeEvents: devframe.emitRuntimeEvents,
|
|
2944
2454
|
waitForExit: () => waitPromise,
|
|
2945
|
-
close
|
|
2946
|
-
closed = true;
|
|
2947
|
-
await server.close();
|
|
2948
|
-
},
|
|
2455
|
+
close,
|
|
2949
2456
|
urls: authenticatedUrls
|
|
2950
2457
|
};
|
|
2951
|
-
if (options
|
|
2458
|
+
if (options.watch) {
|
|
2952
2459
|
if (!options.silentStartupLog) {
|
|
2953
2460
|
logger_default.info("weapp-vite UI 已启动(分析视图,实时模式),按 Ctrl+C 退出。");
|
|
2954
2461
|
for (const url of handle.urls) logger_default.info(` ➜ ${colors.bold(colors.cyan(url))}`);
|
|
2955
2462
|
}
|
|
2956
2463
|
return handle;
|
|
2957
2464
|
}
|
|
2958
|
-
if (!options
|
|
2465
|
+
if (!options.silentStartupLog) {
|
|
2959
2466
|
logger_default.info("weapp-vite UI 已启动(分析视图,静态模式),按 Ctrl+C 退出。");
|
|
2960
2467
|
for (const url of handle.urls) logger_default.info(` ➜ ${colors.bold(colors.cyan(url))}`);
|
|
2961
2468
|
}
|
|
@@ -3453,6 +2960,335 @@ function setCommandNodeEnv(nodeEnv) {
|
|
|
3453
2960
|
process.env.NODE_ENV = nodeEnv;
|
|
3454
2961
|
}
|
|
3455
2962
|
//#endregion
|
|
2963
|
+
//#region src/cli/upload/autoMetadata.ts
|
|
2964
|
+
/** 只读取命令根目录的清单,不向父目录查找或修改文件。 */
|
|
2965
|
+
async function readPackageVersion(packagePath) {
|
|
2966
|
+
let manifest;
|
|
2967
|
+
try {
|
|
2968
|
+
manifest = JSON.parse(await readFile(packagePath, "utf8"));
|
|
2969
|
+
} catch (cause) {
|
|
2970
|
+
throw new Error("--bump 无法读取命令根目录的 package.json。", { cause });
|
|
2971
|
+
}
|
|
2972
|
+
if (manifest === null || typeof manifest !== "object" || !("version" in manifest) || typeof manifest.version !== "string") throw new Error("--bump 要求命令根目录 package.json 的 version 为有效的 SemVer 字符串。");
|
|
2973
|
+
return manifest.version;
|
|
2974
|
+
}
|
|
2975
|
+
/** 在首次编译配置求值前准备一次元数据;预演不写文件,实际版本由 npm 同步。 */
|
|
2976
|
+
async function prepareAutoUploadMetadata(cwd, options, action) {
|
|
2977
|
+
const { bump, gitDesc } = options;
|
|
2978
|
+
if (action === "preview" && (bump !== void 0 || gitDesc !== void 0)) throw new Error("--bump 和 --git-desc 仅支持 upload 或 build --upload,不支持 preview。");
|
|
2979
|
+
if (bump === void 0 && !gitDesc) return options;
|
|
2980
|
+
if (bump !== void 0 && options.uv !== void 0) throw new Error("--bump 不能与 --uv 同时使用。");
|
|
2981
|
+
if (gitDesc && options.desc !== void 0) throw new Error("--git-desc 不能与 --desc 同时使用。");
|
|
2982
|
+
if (bump !== void 0 && bump !== "patch" && bump !== "minor" && bump !== "major") throw new Error("--bump 仅支持 patch、minor 或 major。");
|
|
2983
|
+
const root = path$1.resolve(cwd);
|
|
2984
|
+
const packagePath = path$1.join(root, "package.json");
|
|
2985
|
+
let currentVersion;
|
|
2986
|
+
let nextVersion;
|
|
2987
|
+
if (bump !== void 0) {
|
|
2988
|
+
currentVersion = await readPackageVersion(packagePath);
|
|
2989
|
+
nextVersion = semverInc(currentVersion, bump) ?? void 0;
|
|
2990
|
+
if (!nextVersion) throw new Error("--bump 要求命令根目录 package.json 的 version 为有效的 SemVer 字符串。");
|
|
2991
|
+
}
|
|
2992
|
+
let desc;
|
|
2993
|
+
if (gitDesc) {
|
|
2994
|
+
try {
|
|
2995
|
+
desc = (await x("git", [
|
|
2996
|
+
"log",
|
|
2997
|
+
"-1",
|
|
2998
|
+
"--format=%s"
|
|
2999
|
+
], {
|
|
3000
|
+
nodePath: false,
|
|
3001
|
+
throwOnError: true,
|
|
3002
|
+
nodeOptions: { cwd: root }
|
|
3003
|
+
})).stdout.trim();
|
|
3004
|
+
} catch (cause) {
|
|
3005
|
+
throw new Error("--git-desc 无法读取最新 Git 提交主题,请确认 Git 可用且仓库已有提交。", { cause });
|
|
3006
|
+
}
|
|
3007
|
+
if (!desc) throw new Error("--git-desc 要求最新 Git 提交主题非空。");
|
|
3008
|
+
}
|
|
3009
|
+
if (nextVersion) {
|
|
3010
|
+
if (options.dryRun) logger_default.info(`[upload] dry-run:计划版本 ${currentVersion} → ${nextVersion},未修改 package.json 或锁文件。`);
|
|
3011
|
+
else {
|
|
3012
|
+
try {
|
|
3013
|
+
await x("npm", [
|
|
3014
|
+
"version",
|
|
3015
|
+
nextVersion,
|
|
3016
|
+
"--no-git-tag-version",
|
|
3017
|
+
"--ignore-scripts",
|
|
3018
|
+
"--workspaces=false",
|
|
3019
|
+
"--prefix",
|
|
3020
|
+
root
|
|
3021
|
+
], {
|
|
3022
|
+
nodePath: false,
|
|
3023
|
+
throwOnError: true,
|
|
3024
|
+
nodeOptions: { cwd: root }
|
|
3025
|
+
});
|
|
3026
|
+
} catch (cause) {
|
|
3027
|
+
throw new Error("--bump 执行 npm version 失败,请检查本地版本与锁文件后重试。", { cause });
|
|
3028
|
+
}
|
|
3029
|
+
if (await readPackageVersion(packagePath) !== nextVersion) throw new Error("--bump 执行 npm version 后,命令根目录的 package.json 版本未按预期更新。");
|
|
3030
|
+
logger_default.info(`[upload] 版本已更新:${currentVersion} → ${nextVersion}。后续构建或上传失败不会回退;重试同一版本时请移除 --bump。`);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
if (gitDesc) logger_default.info("[upload] 使用最新 Git 提交主题作为上传说明。");
|
|
3034
|
+
const metadata = { ...options };
|
|
3035
|
+
if (nextVersion !== void 0) metadata.uv = nextVersion;
|
|
3036
|
+
if (desc !== void 0) metadata.desc = desc;
|
|
3037
|
+
return metadata;
|
|
3038
|
+
}
|
|
3039
|
+
//#endregion
|
|
3040
|
+
//#region src/cli/upload/env.ts
|
|
3041
|
+
/** 凭据不经过 Vite 的 env 调试日志;文件优先级和进程环境覆盖规则保持一致。 */
|
|
3042
|
+
async function loadUploadEnv(cwd, mode, root, envDir) {
|
|
3043
|
+
if (envDir === false) return { ...process.env };
|
|
3044
|
+
if (mode === "local") throw new Error("local 不能作为 mode,因为它与 .env.local 文件后缀冲突。");
|
|
3045
|
+
const directory = path$1.resolve(cwd, root ?? ".", envDir ?? ".");
|
|
3046
|
+
const parsed = {};
|
|
3047
|
+
for (const file of [
|
|
3048
|
+
".env",
|
|
3049
|
+
".env.local",
|
|
3050
|
+
`.env.${mode}`,
|
|
3051
|
+
`.env.${mode}.local`
|
|
3052
|
+
]) {
|
|
3053
|
+
const content = await readFile(path$1.join(directory, file), "utf8").catch((error) => {
|
|
3054
|
+
if (error.code === "ENOENT") return "";
|
|
3055
|
+
throw error;
|
|
3056
|
+
});
|
|
3057
|
+
Object.assign(parsed, parseEnv(content));
|
|
3058
|
+
}
|
|
3059
|
+
const processEnv = { ...parsed };
|
|
3060
|
+
for (const [key, value] of Object.entries(process.env)) if (value !== void 0) processEnv[key] = value;
|
|
3061
|
+
expand({
|
|
3062
|
+
parsed,
|
|
3063
|
+
processEnv
|
|
3064
|
+
});
|
|
3065
|
+
return {
|
|
3066
|
+
...parsed,
|
|
3067
|
+
...process.env
|
|
3068
|
+
};
|
|
3069
|
+
}
|
|
3070
|
+
//#endregion
|
|
3071
|
+
//#region src/cli/upload/result.ts
|
|
3072
|
+
/** 只接受可展示的预览结果,文件必须由本次任务生成,不能沿用旧二维码。 */
|
|
3073
|
+
async function validatePreviewResult(value, context) {
|
|
3074
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("预览工具未返回二维码或预览链接。");
|
|
3075
|
+
const input = value;
|
|
3076
|
+
const result = {};
|
|
3077
|
+
for (const key of ["qrCodeUrl", "previewUrl"]) {
|
|
3078
|
+
if (input[key] === void 0) continue;
|
|
3079
|
+
if (typeof input[key] !== "string" || !input[key].trim()) throw new Error("预览工具返回了无效链接。");
|
|
3080
|
+
let url;
|
|
3081
|
+
try {
|
|
3082
|
+
url = new URL(input[key]);
|
|
3083
|
+
} catch {
|
|
3084
|
+
throw new Error("预览工具返回了无效链接。");
|
|
3085
|
+
}
|
|
3086
|
+
if (url.username || url.password || key === "qrCodeUrl" && url.protocol !== "http:" && url.protocol !== "https:" || [
|
|
3087
|
+
"javascript:",
|
|
3088
|
+
"data:",
|
|
3089
|
+
"file:"
|
|
3090
|
+
].includes(url.protocol)) throw new Error("预览工具返回了不支持的链接类型。");
|
|
3091
|
+
result[key] = input[key];
|
|
3092
|
+
}
|
|
3093
|
+
if (input.qrCodeFile !== void 0) {
|
|
3094
|
+
if (typeof input.qrCodeFile !== "string" || !context.qrCodePath || path$1.resolve(input.qrCodeFile) !== path$1.resolve(context.qrCodePath)) throw new Error("预览工具返回的二维码文件不是本次任务的输出路径。");
|
|
3095
|
+
const file = await stat(input.qrCodeFile);
|
|
3096
|
+
if (!file.isFile() || file.size === 0) throw new Error("预览二维码文件不存在或为空。");
|
|
3097
|
+
result.qrCodeFile = input.qrCodeFile;
|
|
3098
|
+
}
|
|
3099
|
+
if (!result.qrCodeUrl && !result.previewUrl && !result.qrCodeFile) throw new Error("预览工具未返回二维码或预览链接。");
|
|
3100
|
+
return result;
|
|
3101
|
+
}
|
|
3102
|
+
//#endregion
|
|
3103
|
+
//#region src/cli/upload/process.ts
|
|
3104
|
+
/** 隔离官方 SDK 的日志和进程级副作用,主进程通过 stdin 传入上下文。 */
|
|
3105
|
+
async function executeUpload(platform, context, secrets, action = "upload") {
|
|
3106
|
+
let result;
|
|
3107
|
+
const output = await new Promise((resolve, reject) => {
|
|
3108
|
+
const child = fork(fileURLToPath(new URL("./upload-worker.mjs", import.meta.url)), [], {
|
|
3109
|
+
cwd: context.cwd,
|
|
3110
|
+
env: context.env,
|
|
3111
|
+
execArgv: [],
|
|
3112
|
+
stdio: [
|
|
3113
|
+
"pipe",
|
|
3114
|
+
"pipe",
|
|
3115
|
+
"pipe",
|
|
3116
|
+
"ipc"
|
|
3117
|
+
]
|
|
3118
|
+
});
|
|
3119
|
+
let completed = false;
|
|
3120
|
+
let exceeded = false;
|
|
3121
|
+
let captured = "";
|
|
3122
|
+
let cancelled = false;
|
|
3123
|
+
const cancel = () => {
|
|
3124
|
+
cancelled = true;
|
|
3125
|
+
child.kill();
|
|
3126
|
+
};
|
|
3127
|
+
process.once("SIGINT", cancel);
|
|
3128
|
+
process.once("SIGTERM", cancel);
|
|
3129
|
+
const collect = (chunk) => {
|
|
3130
|
+
if (exceeded) return;
|
|
3131
|
+
captured += chunk;
|
|
3132
|
+
if (captured.length > 4194304) {
|
|
3133
|
+
exceeded = true;
|
|
3134
|
+
child.kill();
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
child.stdout?.setEncoding("utf8").on("data", collect);
|
|
3138
|
+
child.stderr?.setEncoding("utf8").on("data", collect);
|
|
3139
|
+
child.on("message", (message) => {
|
|
3140
|
+
if (message !== null && typeof message === "object" && "type" in message && message.type === "completed" && "action" in message && message.action === action) {
|
|
3141
|
+
completed = true;
|
|
3142
|
+
result = "result" in message ? message.result : void 0;
|
|
3143
|
+
}
|
|
3144
|
+
});
|
|
3145
|
+
child.once("error", reject);
|
|
3146
|
+
child.once("close", (code, signal) => {
|
|
3147
|
+
process.off("SIGINT", cancel);
|
|
3148
|
+
process.off("SIGTERM", cancel);
|
|
3149
|
+
const safeOutput = redactUploadSecrets(captured, secrets).trim();
|
|
3150
|
+
if (code !== 0 || !completed || exceeded || cancelled) {
|
|
3151
|
+
reject(/* @__PURE__ */ new Error(`${platform} ${action === "preview" ? "预览" : "上传"}失败(${exceeded ? "日志超限" : signal ?? code ?? "unknown"},${completed ? "进程异常结束" : "未收到完成确认"})${safeOutput ? `\n${safeOutput}` : ""}`));
|
|
3152
|
+
return;
|
|
3153
|
+
}
|
|
3154
|
+
resolve(safeOutput);
|
|
3155
|
+
});
|
|
3156
|
+
child.stdin?.on("error", () => {});
|
|
3157
|
+
child.stdin?.end(JSON.stringify({
|
|
3158
|
+
platform,
|
|
3159
|
+
context,
|
|
3160
|
+
action
|
|
3161
|
+
}));
|
|
3162
|
+
});
|
|
3163
|
+
if (output) logger_default.info(output);
|
|
3164
|
+
return action === "preview" ? validatePreviewResult(result, context) : void 0;
|
|
3165
|
+
}
|
|
3166
|
+
//#endregion
|
|
3167
|
+
//#region src/cli/upload/project.ts
|
|
3168
|
+
/** 确保官方工具读取的目录正是本次构建输出,拒绝误传旧目录或同名配置。 */
|
|
3169
|
+
async function validateUploadProject(options) {
|
|
3170
|
+
const { projectConfigFileName } = getProjectPlatformOptions(options.platform);
|
|
3171
|
+
if (options.sourceConfigPath && path$1.basename(options.sourceConfigPath) !== projectConfigFileName) throw new Error(`上传工具只读取 ${projectConfigFileName},请使用此标准文件名,避免读取其他项目配置。`);
|
|
3172
|
+
const configPath = path$1.join(options.projectPath, projectConfigFileName);
|
|
3173
|
+
const config = JSON.parse(await readFile(configPath, "utf8"));
|
|
3174
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) throw new TypeError(`${projectConfigFileName} 必须是 JSON 对象。`);
|
|
3175
|
+
const rootKey = options.platform === "swan" ? "smartProgramRoot" : "miniprogramRoot";
|
|
3176
|
+
const root = config[rootKey];
|
|
3177
|
+
if (typeof root !== "string" || !root.trim()) throw new Error(`${projectConfigFileName} 缺少代码目录 ${rootKey}。`);
|
|
3178
|
+
const sdkRoot = path$1.resolve(options.projectPath, root);
|
|
3179
|
+
const [actualRoot, outputRoot] = await Promise.all([realpath(sdkRoot), realpath(options.outDir)]);
|
|
3180
|
+
if (actualRoot !== outputRoot) throw new Error(`${projectConfigFileName} 的代码目录与本次构建输出不一致,已阻止上传旧产物。请检查项目配置根目录与 build.outDir。`);
|
|
3181
|
+
await access(path$1.join(outputRoot, "app.json"));
|
|
3182
|
+
}
|
|
3183
|
+
//#endregion
|
|
3184
|
+
//#region src/cli/upload/builtProject.ts
|
|
3185
|
+
/** 仅由显式上传或预览入口读取参数,不执行构建或调用平台工具。 */
|
|
3186
|
+
async function createUploadTarget(config, options, action) {
|
|
3187
|
+
if (config.weappLibConfig?.enabled || config.pluginOnly) throw new Error(`${action} 仅支持完整小程序项目,不支持组件库或独立插件构建。`);
|
|
3188
|
+
const platform = resolveUploadPlatforms(config.platform)[0];
|
|
3189
|
+
const uploadConfig = action === "upload" ? config.weappViteConfig.upload : void 0;
|
|
3190
|
+
const version = (options.uv ?? uploadConfig?.version ?? config.packageJson.version)?.trim() ?? "";
|
|
3191
|
+
if (action === "upload" && !version) throw new Error("请通过 --uv、weapp.upload.version 或项目 package.json 的 version 指定上传版本。");
|
|
3192
|
+
const desc = (options.desc ?? uploadConfig?.desc)?.trim() || `${config.packageJson.name ?? platform}${version ? `@${version}` : ""}`;
|
|
3193
|
+
const env = options.dryRun ? {} : await loadUploadEnv(config.cwd, config.mode, config.inlineConfig.root, config.inlineConfig.envDir);
|
|
3194
|
+
const { projectConfigFileName } = getProjectPlatformOptions(config.platform);
|
|
3195
|
+
const generatedProjectConfig = config.multiPlatform.projectConfigs !== void 0;
|
|
3196
|
+
const projectPath = generatedProjectConfig ? config.outDir : config.multiPlatform.enabled ? path$1.dirname(config.outDir) : path$1.dirname(config.projectConfigPath ?? path$1.join(config.cwd, projectConfigFileName));
|
|
3197
|
+
const appid = config.projectConfig.appid ?? config.projectConfig.appId;
|
|
3198
|
+
const target = {
|
|
3199
|
+
platform,
|
|
3200
|
+
sourceConfigPath: config.projectConfigPath,
|
|
3201
|
+
context: {
|
|
3202
|
+
cwd: config.cwd,
|
|
3203
|
+
projectPath,
|
|
3204
|
+
appid: typeof appid === "string" ? appid : void 0,
|
|
3205
|
+
version,
|
|
3206
|
+
desc,
|
|
3207
|
+
qrCodePath: action === "preview" && !options.dryRun ? path$1.join(config.cwd, ".weapp-vite", "preview", `${platform}-${randomUUID()}.png`) : void 0,
|
|
3208
|
+
env: {
|
|
3209
|
+
...process.env,
|
|
3210
|
+
...env
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
};
|
|
3214
|
+
(config.inlineConfig.plugins ??= []).push({
|
|
3215
|
+
name: "weapp-vite:upload-output",
|
|
3216
|
+
apply: (viteConfig) => resolveWeappViteHostMeta(viteConfig)?.runtime === "miniprogram",
|
|
3217
|
+
writeBundle(output, bundle) {
|
|
3218
|
+
if (output.dir && Object.hasOwn(bundle, "app.json")) {
|
|
3219
|
+
target.outDir = path$1.resolve(output.dir);
|
|
3220
|
+
if (generatedProjectConfig) target.context.projectPath = target.outDir;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
});
|
|
3224
|
+
return target;
|
|
3225
|
+
}
|
|
3226
|
+
/** 只消费本次构建的产物;产物校验通过前不会准备凭据或调用平台工具。 */
|
|
3227
|
+
async function executeUploadTarget(target, options, action) {
|
|
3228
|
+
const { platform, context } = target;
|
|
3229
|
+
if (!target.outDir) throw new Error("本次构建未写出 app.json,已阻止上传或预览旧产物。");
|
|
3230
|
+
await validateUploadProject({
|
|
3231
|
+
platform,
|
|
3232
|
+
projectPath: context.projectPath,
|
|
3233
|
+
outDir: target.outDir,
|
|
3234
|
+
sourceConfigPath: target.sourceConfigPath
|
|
3235
|
+
});
|
|
3236
|
+
if (options.dryRun) {
|
|
3237
|
+
logger_default.success(`[${action}:${platform}] dry-run:构建完成,未校验凭据或调用平台工具。项目:${path$1.relative(context.cwd, context.projectPath) || "."}`);
|
|
3238
|
+
return;
|
|
3239
|
+
}
|
|
3240
|
+
const upload = await prepareUpload(platform, context, action);
|
|
3241
|
+
const result = await executeUpload(platform, context, upload.secrets, action);
|
|
3242
|
+
if (action === "preview") {
|
|
3243
|
+
if (!result) throw new Error("预览工具未返回二维码或预览链接。");
|
|
3244
|
+
for (const [label, value] of [
|
|
3245
|
+
["二维码图片", result.qrCodeUrl],
|
|
3246
|
+
["预览链接", result.previewUrl],
|
|
3247
|
+
["二维码文件", result.qrCodeFile && path$1.relative(context.cwd, result.qrCodeFile)]
|
|
3248
|
+
]) if (value) logger_default.info(`[preview:${platform}] ${label}:${redactUploadSecrets(value, upload.secrets)}`);
|
|
3249
|
+
logger_default.success(`[preview:${platform}] 预览已生成(未上传开发版本、未提审、未正式发布)。`);
|
|
3250
|
+
} else logger_default.success(`[upload:${platform}] ${context.version} 上传完成(未提审、未正式发布)。`);
|
|
3251
|
+
}
|
|
3252
|
+
//#endregion
|
|
3253
|
+
//#region src/cli/upload/options.ts
|
|
3254
|
+
function readUploadMetadata(cli) {
|
|
3255
|
+
for (const name of [
|
|
3256
|
+
"uv",
|
|
3257
|
+
"desc",
|
|
3258
|
+
"bump"
|
|
3259
|
+
]) {
|
|
3260
|
+
const value = cli.options[name];
|
|
3261
|
+
if (typeof value === "boolean" || Array.isArray(value) && value.some((item) => typeof item === "boolean")) throw new Error(`--${name} 需要指定字符串参数。`);
|
|
3262
|
+
}
|
|
3263
|
+
const { values } = parseArgs({
|
|
3264
|
+
args: cli.rawArgs.slice(2),
|
|
3265
|
+
allowPositionals: true,
|
|
3266
|
+
strict: false,
|
|
3267
|
+
options: {
|
|
3268
|
+
uv: { type: "string" },
|
|
3269
|
+
desc: { type: "string" },
|
|
3270
|
+
bump: { type: "string" }
|
|
3271
|
+
}
|
|
3272
|
+
});
|
|
3273
|
+
return {
|
|
3274
|
+
uv: typeof values.uv === "string" ? values.uv : void 0,
|
|
3275
|
+
desc: typeof values.desc === "string" ? values.desc : void 0,
|
|
3276
|
+
bump: typeof values.bump === "string" ? values.bump : void 0
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
/** 普通构建不读取上传元数据;上传必须是显式的一次性构建操作。 */
|
|
3280
|
+
function resolveBuildUploadOptions(cli, options) {
|
|
3281
|
+
if (options.upload !== true) {
|
|
3282
|
+
if (options.uv !== void 0 || options.desc !== void 0 || options.bump !== void 0 || options.gitDesc !== void 0 || options.dryRun !== void 0) throw new Error("--uv、--desc、--bump、--git-desc 和 --dry-run 仅能与 --upload 一起使用。");
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
if (options.watch) throw new Error("--upload 不能与 --watch 一起使用,请执行一次性构建。");
|
|
3286
|
+
return {
|
|
3287
|
+
...options,
|
|
3288
|
+
...readUploadMetadata(cli)
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3291
|
+
//#endregion
|
|
3456
3292
|
//#region src/cli/commands/build.ts
|
|
3457
3293
|
function emitDashboardEvents$1(handle, events) {
|
|
3458
3294
|
handle?.emitRuntimeEvents(events);
|
|
@@ -3488,17 +3324,21 @@ function scheduleCompletedProductionBuildExit(options, analyzeHandle) {
|
|
|
3488
3324
|
}, 0).unref?.();
|
|
3489
3325
|
}
|
|
3490
3326
|
function registerBuildCommand(cli) {
|
|
3491
|
-
cli.command("build [root]", "build for production").option("--target <target>", `[string] transpile target (default: 'modules')`).option("--outDir <dir>", `[string] output directory (default: dist)`).option("-p, --platform <platform>", `[string] target platform (weapp | web | all)`).option("--project-config <path>", `[string] project config path (miniprogram only)`).option("--sourcemap [output]", `[boolean | "inline" | "hidden"] output source maps for build (default: false)`).option("--minify [minifier]", "[boolean | \"terser\" | \"esbuild\"] enable/disable minification, or specify minifier to use (default: esbuild)").option("--emptyOutDir", `[boolean] force empty outDir when it's outside of root`).option("-w, --watch", `[boolean] rebuilds when modules have changed on disk`).option("--skipNpm", `[boolean] if skip npm build`).option("-o, --open", `[boolean] open ide`).option("--trust-project", "[boolean] auto trust Wechat DevTools project on open", { default: true }).option("--ide-open-strategy <strategy>", "[string] IDE open strategy (cli | automator)", { default: "cli" }).option("--no-open-recovery", "[boolean] disable automatic Wechat DevTools close-and-reopen recovery").option("--ui", `[boolean] 启动调试 UI(当前提供分析视图)`, { default: false }).option("--analyze", `[boolean] 输出分包分析仪表盘`, { default: false }).option("--scope <scope>", `[string] 局部构建范围,例如 main,packages/order`).action(async (root, options) => {
|
|
3327
|
+
cli.command("build [root]", "build for production").option("--target <target>", `[string] transpile target (default: 'modules')`).option("--outDir <dir>", `[string] output directory (default: dist)`).option("-p, --platform <platform>", `[string] target platform (weapp | web | all)`).option("--project-config <path>", `[string] project config path (miniprogram only)`).option("--sourcemap [output]", `[boolean | "inline" | "hidden"] output source maps for build (default: false)`).option("--minify [minifier]", "[boolean | \"terser\" | \"esbuild\"] enable/disable minification, or specify minifier to use (default: esbuild)").option("--emptyOutDir", `[boolean] force empty outDir when it's outside of root`).option("-w, --watch", `[boolean] rebuilds when modules have changed on disk`).option("--skipNpm", `[boolean] if skip npm build`).option("-o, --open", `[boolean] open ide`).option("--trust-project", "[boolean] auto trust Wechat DevTools project on open", { default: true }).option("--ide-open-strategy <strategy>", "[string] IDE open strategy (cli | automator)", { default: "cli" }).option("--no-open-recovery", "[boolean] disable automatic Wechat DevTools close-and-reopen recovery").option("--ui", `[boolean] 启动调试 UI(当前提供分析视图)`, { default: false }).option("--analyze", `[boolean] 输出分包分析仪表盘`, { default: false }).option("--scope <scope>", `[string] 局部构建范围,例如 main,packages/order`).option("--upload", "[boolean] upload the mini program after this build succeeds").option("--uv <version>", "[string] upload version override (requires --upload)").option("--desc <text>", "[string] upload description override (requires --upload)").option("--bump <release>", "[string] increment local package version (patch | minor | major; requires --upload)").option("--git-desc", "[boolean] use the latest Git commit subject (requires --upload)").option("--dry-run", "[boolean] validate upload output without credentials or SDK calls (requires --upload)").action(async (root, options) => {
|
|
3492
3328
|
let analyzeHandle;
|
|
3493
3329
|
let ctx;
|
|
3494
3330
|
let targets;
|
|
3495
3331
|
let buildCompleted = false;
|
|
3496
3332
|
try {
|
|
3333
|
+
options = { ...options };
|
|
3497
3334
|
filterDuplicateOptions(options);
|
|
3335
|
+
let uploadOptions = resolveBuildUploadOptions(cli, options);
|
|
3498
3336
|
setCommandNodeEnv("production");
|
|
3499
3337
|
const cwd = root ?? process.cwd();
|
|
3500
3338
|
const configFile = resolveConfigFile(options);
|
|
3501
3339
|
targets = resolveRuntimeTargets(options);
|
|
3340
|
+
if (uploadOptions && !getBackendForCapability(targets, "miniprogram", "build")) throw new Error("--upload 仅支持包含小程序目标的构建,不能用于纯 Web 构建。");
|
|
3341
|
+
if (uploadOptions) uploadOptions = await prepareAutoUploadMetadata(cwd, uploadOptions, "upload");
|
|
3502
3342
|
const inlineConfig = createInlineConfig(targets, {
|
|
3503
3343
|
scope: options.scope,
|
|
3504
3344
|
inlineConfig: createBuildInlineConfig(options)
|
|
@@ -3514,6 +3354,7 @@ function registerBuildCommand(cli) {
|
|
|
3514
3354
|
preloadAppEntry: false
|
|
3515
3355
|
});
|
|
3516
3356
|
const { configService } = ctx;
|
|
3357
|
+
const uploadTarget = uploadOptions ? await createUploadTarget(configService, uploadOptions, "upload") : void 0;
|
|
3517
3358
|
const miniBackend = getBackendForCapability(targets, "miniprogram", "build");
|
|
3518
3359
|
const webBackend = getBackendForCapability(targets, "web", "build");
|
|
3519
3360
|
logRuntimeTarget(targets, { resolvedConfigPlatform: configService.platform });
|
|
@@ -3591,6 +3432,7 @@ function registerBuildCommand(cli) {
|
|
|
3591
3432
|
throw error;
|
|
3592
3433
|
}
|
|
3593
3434
|
}
|
|
3435
|
+
if (uploadTarget && uploadOptions) await executeUploadTarget(uploadTarget, uploadOptions, "upload");
|
|
3594
3436
|
if (miniBackend) logBuildAppFinish(configService, void 0, { skipWeb: !webBackend });
|
|
3595
3437
|
if (options.open && getBackendForCapability(targets, "miniprogram", "ide")) {
|
|
3596
3438
|
emitDashboardEvents$1(analyzeHandle, [{
|
|
@@ -3600,7 +3442,8 @@ function registerBuildCommand(cli) {
|
|
|
3600
3442
|
detail: "构建完成后准备打开 IDE 项目。",
|
|
3601
3443
|
tags: ["ide", "open"]
|
|
3602
3444
|
}]);
|
|
3603
|
-
|
|
3445
|
+
const generatedProjectConfig = configService.multiPlatform.projectConfigs !== void 0;
|
|
3446
|
+
await openIde(configService.platform, resolveIdeProjectPath(generatedProjectConfig ? configService.outDir : configService.mpDistRoot, generatedProjectConfig), {
|
|
3604
3447
|
openRecovery: options.openRecovery,
|
|
3605
3448
|
trustProject: options.trustProject,
|
|
3606
3449
|
openStrategy: options.ideOpenStrategy
|
|
@@ -3987,7 +3830,9 @@ async function closeActiveForwardConsole() {
|
|
|
3987
3830
|
*/
|
|
3988
3831
|
async function maybeStartForwardConsole(options) {
|
|
3989
3832
|
if (options.platform !== "weapp") return false;
|
|
3990
|
-
const
|
|
3833
|
+
const multiPlatform = options.weappViteConfig?.multiPlatform;
|
|
3834
|
+
const generatedProjectConfig = typeof multiPlatform === "object" && multiPlatform.projectConfigs !== void 0;
|
|
3835
|
+
const projectPath = resolveIdeProjectRoot(options.mpDistRoot, options.cwd, generatedProjectConfig);
|
|
3991
3836
|
if (!projectPath) return false;
|
|
3992
3837
|
if (activeForwardConsoleSession) return true;
|
|
3993
3838
|
if (activeForwardConsoleStart) return await activeForwardConsoleStart;
|
|
@@ -5370,7 +5215,7 @@ function resolveRunnableHotkeyDefinition(input) {
|
|
|
5370
5215
|
}
|
|
5371
5216
|
//#endregion
|
|
5372
5217
|
//#region package.json
|
|
5373
|
-
var version = "7.
|
|
5218
|
+
var version = "7.4.0";
|
|
5374
5219
|
//#endregion
|
|
5375
5220
|
//#region src/cli/devHotkeys/format.ts
|
|
5376
5221
|
const FULLWIDTH_ASCII_START = 65281;
|
|
@@ -6110,6 +5955,7 @@ function registerServeCommand(cli) {
|
|
|
6110
5955
|
logRuntimeTarget(targets, { resolvedConfigPlatform: configService.platform });
|
|
6111
5956
|
const enableAnalyze = Boolean(isUiEnabled(options) && miniBackend);
|
|
6112
5957
|
let analyzeHandle;
|
|
5958
|
+
const generatedProjectConfig = configService.multiPlatform.projectConfigs !== void 0;
|
|
6113
5959
|
const miniProgramDevActions = createServeMiniProgramDevActions({
|
|
6114
5960
|
build: async () => {
|
|
6115
5961
|
await miniBackend?.driver.dev(ctx, options);
|
|
@@ -6134,7 +5980,7 @@ function registerServeCommand(cli) {
|
|
|
6134
5980
|
});
|
|
6135
5981
|
writePostOpenSeparator();
|
|
6136
5982
|
},
|
|
6137
|
-
projectPath: resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd),
|
|
5983
|
+
projectPath: resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd, generatedProjectConfig),
|
|
6138
5984
|
startForwardConsole: async (openOptions) => {
|
|
6139
5985
|
if (resolveServeIdeOpenStrategy(openOptions, options.ideOpenStrategy) === "automator") return await maybeStartForwardConsole({
|
|
6140
5986
|
openedOnly: true,
|
|
@@ -6150,7 +5996,7 @@ function registerServeCommand(cli) {
|
|
|
6150
5996
|
cwd: configService.cwd,
|
|
6151
5997
|
preferOpenedSession: false,
|
|
6152
5998
|
recoverAutomatorSession: async () => {
|
|
6153
|
-
const projectPath = resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd);
|
|
5999
|
+
const projectPath = resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd, generatedProjectConfig);
|
|
6154
6000
|
await openIde(configService.platform, projectPath, {
|
|
6155
6001
|
loginRetry: options.loginRetry,
|
|
6156
6002
|
loginRetryTimeout: options.loginRetryTimeout,
|
|
@@ -6292,6 +6138,56 @@ function registerServeCommand(cli) {
|
|
|
6292
6138
|
});
|
|
6293
6139
|
}
|
|
6294
6140
|
//#endregion
|
|
6141
|
+
//#region src/cli/commands/upload.ts
|
|
6142
|
+
async function buildUploadTarget(cwd, platform, options, action) {
|
|
6143
|
+
const targets = resolveRuntimeTargets({ platform });
|
|
6144
|
+
const ctx = await createCompilerContext({
|
|
6145
|
+
cwd,
|
|
6146
|
+
mode: options.mode ?? "production",
|
|
6147
|
+
configFile: resolveConfigFile(options),
|
|
6148
|
+
inlineConfig: createInlineConfig(targets),
|
|
6149
|
+
cliPlatform: platform,
|
|
6150
|
+
projectConfigPath: options.projectConfig,
|
|
6151
|
+
emitDefaultAutoImportOutputs: false,
|
|
6152
|
+
preloadAppEntry: false
|
|
6153
|
+
});
|
|
6154
|
+
try {
|
|
6155
|
+
const target = await createUploadTarget(ctx.configService, options, action);
|
|
6156
|
+
logger_default.info(`[${action}:${target.platform}] 构建${target.context.version ? ` ${target.context.version}` : ""}`);
|
|
6157
|
+
await ctx.buildService.build({});
|
|
6158
|
+
return target;
|
|
6159
|
+
} finally {
|
|
6160
|
+
ctx.watcherService.closeAll();
|
|
6161
|
+
terminateStaleSassEmbeddedProcess();
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
async function runUploadCommand(root, options, action = "upload") {
|
|
6165
|
+
filterDuplicateOptions(options);
|
|
6166
|
+
const platforms = resolveUploadPlatforms(options.platform ?? options.p);
|
|
6167
|
+
setCommandNodeEnv("production");
|
|
6168
|
+
const cwd = path$1.resolve(root ?? process.cwd());
|
|
6169
|
+
options = await prepareAutoUploadMetadata(cwd, options, action);
|
|
6170
|
+
for (const platform of platforms) await executeUploadTarget(await buildUploadTarget(cwd, platform, options, action), options, action);
|
|
6171
|
+
}
|
|
6172
|
+
function registerUploadCommand(cli) {
|
|
6173
|
+
cli.command("upload [root]", "build and upload mini programs (does not submit for review or publish)").option("-p, --platform <platform>", "[string] weapp | alipay | tt | xhs | jd | swan; comma-separated targets or all").option("--project-config <path>", "[string] project config path").option("--uv <version>", "[string] upload version (default: weapp.upload.version or package.json version)").option("--desc <text>", "[string] upload description (default: weapp.upload.desc or project name and version)").option("--bump <release>", "[string] increment local package version once (patch | minor | major; dry-run does not write)").option("--git-desc", "[boolean] use the latest Git commit subject as upload description").option("--dry-run", "[boolean] build without validating upload credentials or uploading").example("wv upload -p weapp").example("wv upload -p xhs,tt --bump patch --git-desc --dry-run").example("wv upload --project ./dist --version 1.2.3 --desc \"release\" (deprecated IDE syntax; use wv ide upload to retain IDE behavior)").action(async (root, options) => {
|
|
6174
|
+
await runUploadCommand(root, {
|
|
6175
|
+
...options,
|
|
6176
|
+
...readUploadMetadata(cli)
|
|
6177
|
+
});
|
|
6178
|
+
scheduleCompletedProductionBuildExit({}, void 0);
|
|
6179
|
+
});
|
|
6180
|
+
}
|
|
6181
|
+
function registerPreviewCommand(cli) {
|
|
6182
|
+
cli.command("preview [root]", "build mini programs and generate preview QR codes or links").option("-p, --platform <platform>", "[string] weapp | alipay | tt | xhs | jd | swan; comma-separated targets or all").option("--project-config <path>", "[string] project config path").option("--desc <text>", "[string] preview description").option("--dry-run", "[boolean] build without validating credentials or generating previews").action(async (root, options) => {
|
|
6183
|
+
await runUploadCommand(root, {
|
|
6184
|
+
...options,
|
|
6185
|
+
...readUploadMetadata(cli)
|
|
6186
|
+
}, "preview");
|
|
6187
|
+
scheduleCompletedProductionBuildExit({}, void 0);
|
|
6188
|
+
});
|
|
6189
|
+
}
|
|
6190
|
+
//#endregion
|
|
6295
6191
|
//#region src/cli/error.ts
|
|
6296
6192
|
const watchLimitErrorCodes = /* @__PURE__ */ new Set(["EMFILE", "ENOSPC"]);
|
|
6297
6193
|
const watchLimitMessagePattern = /EMFILE|ENOSPC|unable to start FSEvent stream/i;
|
|
@@ -6333,6 +6229,8 @@ const WEAPP_VITE_NATIVE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
6333
6229
|
"dev",
|
|
6334
6230
|
"serve",
|
|
6335
6231
|
"build",
|
|
6232
|
+
"upload",
|
|
6233
|
+
"preview",
|
|
6336
6234
|
"close",
|
|
6337
6235
|
"analyze",
|
|
6338
6236
|
"alipay",
|
|
@@ -6345,8 +6243,66 @@ const WEAPP_VITE_NATIVE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
6345
6243
|
"g",
|
|
6346
6244
|
"mcp"
|
|
6347
6245
|
]);
|
|
6246
|
+
const LEGACY_UPLOAD_OPTIONS = {
|
|
6247
|
+
"--version": true,
|
|
6248
|
+
"-v": true,
|
|
6249
|
+
"--project": true,
|
|
6250
|
+
"--appid": true,
|
|
6251
|
+
"--ext-appid": true,
|
|
6252
|
+
"--info-output": true,
|
|
6253
|
+
"-i": true
|
|
6254
|
+
};
|
|
6255
|
+
const SDK_UPLOAD_OPTIONS = {
|
|
6256
|
+
"--platform": true,
|
|
6257
|
+
"--uv": true,
|
|
6258
|
+
"--bump": true,
|
|
6259
|
+
"--git-desc": true,
|
|
6260
|
+
"--dry-run": true
|
|
6261
|
+
};
|
|
6262
|
+
const UPLOAD_VALUE_OPTIONS = {
|
|
6263
|
+
...LEGACY_UPLOAD_OPTIONS,
|
|
6264
|
+
"-p": true,
|
|
6265
|
+
"--desc": true,
|
|
6266
|
+
"-d": true,
|
|
6267
|
+
"--platform": true,
|
|
6268
|
+
"--uv": true,
|
|
6269
|
+
"--bump": true,
|
|
6270
|
+
"--project-config": true,
|
|
6271
|
+
"--port": true,
|
|
6272
|
+
"--lang": true,
|
|
6273
|
+
"-c": true,
|
|
6274
|
+
"--config": true,
|
|
6275
|
+
"--base": true,
|
|
6276
|
+
"-l": true,
|
|
6277
|
+
"--logLevel": true,
|
|
6278
|
+
"-f": true,
|
|
6279
|
+
"--filter": true,
|
|
6280
|
+
"-m": true,
|
|
6281
|
+
"--mode": true
|
|
6282
|
+
};
|
|
6283
|
+
/**
|
|
6284
|
+
* @description 仅按显式参数区分上传方言,不根据 -p 的值或文件系统猜测后端。
|
|
6285
|
+
*/
|
|
6286
|
+
function isLegacyUploadInvocation(argv) {
|
|
6287
|
+
let legacyOption;
|
|
6288
|
+
let sdkOption;
|
|
6289
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
6290
|
+
const token = argv[index];
|
|
6291
|
+
if (token === "--") break;
|
|
6292
|
+
const equalsIndex = token.indexOf("=");
|
|
6293
|
+
const option = equalsIndex === -1 ? token : token.slice(0, equalsIndex);
|
|
6294
|
+
if (LEGACY_UPLOAD_OPTIONS[option] === true) legacyOption = option;
|
|
6295
|
+
if (SDK_UPLOAD_OPTIONS[option.startsWith("--no-") ? `--${option.slice(5)}` : option] === true) sdkOption = option;
|
|
6296
|
+
if (legacyOption && sdkOption) throw new Error(`不能混用微信 IDE 上传参数 ${legacyOption} 与 SDK 上传参数 ${sdkOption};请使用 wv ide upload 保留 IDE 行为,或使用 wv build --upload 迁移到 SDK。`);
|
|
6297
|
+
if (equalsIndex === -1 && UPLOAD_VALUE_OPTIONS[option] === true) index += 1;
|
|
6298
|
+
}
|
|
6299
|
+
return legacyOption !== void 0;
|
|
6300
|
+
}
|
|
6301
|
+
function warnLegacyUpload() {
|
|
6302
|
+
logger_default.warn("顶层微信 IDE 上传语法已弃用,将在未来版本移除。请迁移到 wv build --upload -p weapp --uv <version> --desc <description>(SDK 流程需要源码目录与上传凭据,并非 IDE 命令的等价替换);或使用 wv ide upload ... 保留现有 IDE 行为。");
|
|
6303
|
+
}
|
|
6348
6304
|
/**
|
|
6349
|
-
* @description
|
|
6305
|
+
* @description 保留旧版上传方言;其余未命中自身命令的调用回退到 weapp-ide-cli。
|
|
6350
6306
|
*/
|
|
6351
6307
|
async function tryRunIdeCommand(argv) {
|
|
6352
6308
|
const command = argv[0];
|
|
@@ -6359,11 +6315,14 @@ async function tryRunIdeCommand(argv) {
|
|
|
6359
6315
|
if (command.startsWith("-")) return false;
|
|
6360
6316
|
if (command === "help") {
|
|
6361
6317
|
const target = argv[1];
|
|
6362
|
-
if (!target || WEAPP_VITE_NATIVE_COMMANDS.has(target) || !isWeappIdeTopLevelCommand(target)) return false;
|
|
6318
|
+
if (!target || target !== "upload" && WEAPP_VITE_NATIVE_COMMANDS.has(target) || !isWeappIdeTopLevelCommand(target)) return false;
|
|
6319
|
+
if (target === "upload") warnLegacyUpload();
|
|
6363
6320
|
await executeWechatIdeCliCommand(argv);
|
|
6364
6321
|
return true;
|
|
6365
6322
|
}
|
|
6366
|
-
|
|
6323
|
+
const legacyUpload = command === "upload" && isLegacyUploadInvocation(argv);
|
|
6324
|
+
if (!legacyUpload && WEAPP_VITE_NATIVE_COMMANDS.has(command) || !isWeappIdeTopLevelCommand(command)) return false;
|
|
6325
|
+
if (legacyUpload) warnLegacyUpload();
|
|
6367
6326
|
if (!await dispatchWechatCliCommand(argv)) await executeWechatIdeCliCommand(argv);
|
|
6368
6327
|
return true;
|
|
6369
6328
|
}
|
|
@@ -6461,6 +6420,8 @@ cli.option("-c, --config <file>", `[string] use specified config file`).option("
|
|
|
6461
6420
|
registerIdeCommand(cli);
|
|
6462
6421
|
registerAlipayCommand(cli);
|
|
6463
6422
|
registerBuildCommand(cli);
|
|
6423
|
+
registerUploadCommand(cli);
|
|
6424
|
+
registerPreviewCommand(cli);
|
|
6464
6425
|
registerCloseCommand(cli);
|
|
6465
6426
|
registerAnalyzeCommand(cli);
|
|
6466
6427
|
registerInitCommand(cli);
|
|
@@ -6499,7 +6460,9 @@ function resolveManagedTsconfigBootstrapRoot(args) {
|
|
|
6499
6460
|
"dev",
|
|
6500
6461
|
"open",
|
|
6501
6462
|
"prepare",
|
|
6502
|
-
"
|
|
6463
|
+
"preview",
|
|
6464
|
+
"serve",
|
|
6465
|
+
"upload"
|
|
6503
6466
|
].includes(firstArg)) {
|
|
6504
6467
|
if (secondArg && !secondArg.startsWith("-")) return path.resolve(secondArg);
|
|
6505
6468
|
return process.cwd();
|
|
@@ -6512,7 +6475,7 @@ try {
|
|
|
6512
6475
|
const managedTsconfigBootstrapRoot = resolveManagedTsconfigBootstrapRoot(args);
|
|
6513
6476
|
if (managedTsconfigBootstrapRoot) await syncManagedTsconfigBootstrapFiles(managedTsconfigBootstrapRoot);
|
|
6514
6477
|
cli.parse(process.argv, { run: false });
|
|
6515
|
-
await maybeAutoStartMcpServer(args, cli.options);
|
|
6478
|
+
if (cli.matchedCommand?.name === "" && cli.matchedCommandName === void 0) await maybeAutoStartMcpServer(args, cli.options);
|
|
6516
6479
|
await cli.runMatchedCommand();
|
|
6517
6480
|
}
|
|
6518
6481
|
} catch (error) {
|