weapp-vite 6.15.0 → 6.15.3

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.
@@ -1,4 +1,4 @@
1
- import { i as getCompilerContext, u as getRouteRuntimeGlobalKeys } from "./createContext-DZzBkyLu.mjs";
1
+ import { i as getCompilerContext, u as getRouteRuntimeGlobalKeys } from "./createContext-BuwQryVc.mjs";
2
2
  //#region src/auto-routes.ts
3
3
  const ROUTE_RUNTIME_OVERRIDE_KEY = Symbol.for("weapp-vite.route-runtime");
4
4
  function createGetter(resolver) {
package/dist/cli.mjs CHANGED
@@ -1,10 +1,10 @@
1
- import { S as isPathInside, _ as DEFAULT_MP_PLATFORM, b as resolveMiniPlatform, c as createSharedBuildConfig, d as resolveWeappConfigFile, f as checkRuntime, g as createCjsConfigLoadError, h as parseCommentJson, l as SHARED_CHUNK_VIRTUAL_PREFIX, m as loadViteConfigFile, n as syncProjectSupportFiles, p as getProjectConfigFileName, r as syncManagedTsconfigBootstrapFiles, s as formatBytes, t as createCompilerContext, v as getDefaultIdeProjectRoot, x as shouldPassPlatformArgToIdeOpen, y as normalizeMiniPlatform } from "./createContext-DZzBkyLu.mjs";
1
+ import { S as isPathInside, _ as DEFAULT_MP_PLATFORM, b as resolveMiniPlatform, c as createSharedBuildConfig, d as resolveWeappConfigFile, f as checkRuntime, g as createCjsConfigLoadError, h as parseCommentJson, l as SHARED_CHUNK_VIRTUAL_PREFIX, m as loadViteConfigFile, n as syncProjectSupportFiles, p as getProjectConfigFileName, r as syncManagedTsconfigBootstrapFiles, s as formatBytes, t as createCompilerContext, v as getDefaultIdeProjectRoot, x as shouldPassPlatformArgToIdeOpen, y as normalizeMiniPlatform } from "./createContext-BuwQryVc.mjs";
2
2
  import { r as logger_default, t as colors } from "./logger-CgxdNjvb.mjs";
3
- import { m as VERSION } from "./file-DZdM8x0_.mjs";
3
+ import { h as VERSION } from "./file-x_pM3sdN.mjs";
4
4
  import { a as resolveWeappMcpConfig, o as startWeappViteMcpServer } from "./mcp-DRlj32v4.mjs";
5
5
  import { createRequire } from "node:module";
6
- import { defu, fs } from "@weapp-core/shared";
7
6
  import path, { posix } from "pathe";
7
+ import { defu, fs } from "@weapp-core/shared";
8
8
  import process from "node:process";
9
9
  import fs$1 from "node:fs/promises";
10
10
  import { build, createServer } from "vite";
@@ -1626,7 +1626,7 @@ function registerInitCommand(cli) {
1626
1626
  });
1627
1627
  }
1628
1628
  //#endregion
1629
- //#region src/cli/mcpClient.ts
1629
+ //#region src/cli/mcpClient/shared.ts
1630
1630
  const CODEX_BLOCK_PREFIX = "# >>> weapp-vite mcp ";
1631
1631
  const CODEX_BLOCK_SUFFIX = " >>>";
1632
1632
  const CODEX_BLOCK_END_PREFIX = "# <<< weapp-vite mcp ";
@@ -1710,6 +1710,9 @@ function resolveHttpEntry(client, url) {
1710
1710
  };
1711
1711
  return { url };
1712
1712
  }
1713
+ function resolveConfigEntry(client, transport, workspaceRoot, url) {
1714
+ return transport === "http" ? resolveHttpEntry(client, url ?? "") : resolveCommandEntry(client, workspaceRoot);
1715
+ }
1713
1716
  function resolveCodexBlockPattern(serverName) {
1714
1717
  const startMarker = `${CODEX_BLOCK_PREFIX}${serverName}${CODEX_BLOCK_SUFFIX}`;
1715
1718
  const endMarker = `${CODEX_BLOCK_END_PREFIX}${serverName}${CODEX_BLOCK_END_SUFFIX}`;
@@ -1750,40 +1753,8 @@ function resolveTarget(client, workspaceRoot) {
1750
1753
  serverName
1751
1754
  };
1752
1755
  }
1753
- function resolveSupportedMcpClient(input) {
1754
- if (input === "codex" || input === "claude-code" || input === "cursor") return input;
1755
- throw new Error(`不支持的 MCP 客户端:${input}。当前仅支持 codex、claude-code、cursor。`);
1756
- }
1757
- function buildMcpClientConfigPlan(options) {
1758
- const workspaceRoot = path.resolve(options.workspaceRoot);
1759
- const target = resolveTarget(options.client, workspaceRoot);
1760
- const entry = options.transport === "http" ? resolveHttpEntry(options.client, options.url ?? "") : resolveCommandEntry(options.client, workspaceRoot);
1761
- if (options.transport === "http" && !entry.url) throw new Error("HTTP 模式缺少 MCP 服务地址,请使用 --url 指定或在当前项目中检测到可用地址。");
1762
- return {
1763
- entry,
1764
- preview: target.client === "codex" ? renderCodexBlock(target.serverName, entry) : renderJsonPreview(target.serverName, entry),
1765
- target,
1766
- transport: options.transport
1767
- };
1768
- }
1769
- async function writeMcpClientConfig(plan) {
1770
- await fs.ensureDir(path.dirname(plan.target.configPath));
1771
- const existing = await fs.readFile(plan.target.configPath, "utf8").catch(() => "");
1772
- if (plan.target.client === "codex") {
1773
- const nextContent = upsertCodexManagedBlock(existing, plan.target.serverName, plan.preview);
1774
- await fs.writeFile(plan.target.configPath, nextContent, "utf8");
1775
- return;
1776
- }
1777
- const parsed = parseJsonConfig(existing, plan.target.configPath);
1778
- const nextConfig = {
1779
- ...parsed,
1780
- mcpServers: {
1781
- ...parsed.mcpServers ?? {},
1782
- [plan.target.serverName]: plan.entry
1783
- }
1784
- };
1785
- await fs.writeFile(plan.target.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
1786
- }
1756
+ //#endregion
1757
+ //#region src/cli/mcpClient/inspect.ts
1787
1758
  async function probeHttpEndpoint(url) {
1788
1759
  const controller = new AbortController();
1789
1760
  const timer = setTimeout$1(() => {
@@ -1888,6 +1859,24 @@ async function inspectMcpClientConfig(options) {
1888
1859
  if (target.client === "codex") return await inspectCodexConfig(target);
1889
1860
  return await inspectJsonConfig(target);
1890
1861
  }
1862
+ //#endregion
1863
+ //#region src/cli/mcpClient/plan.ts
1864
+ function resolveSupportedMcpClient(input) {
1865
+ if (input === "codex" || input === "claude-code" || input === "cursor") return input;
1866
+ throw new Error(`不支持的 MCP 客户端:${input}。当前仅支持 codex、claude-code、cursor。`);
1867
+ }
1868
+ function buildMcpClientConfigPlan(options) {
1869
+ const workspaceRoot = path.resolve(options.workspaceRoot);
1870
+ const target = resolveTarget(options.client, workspaceRoot);
1871
+ const entry = resolveConfigEntry(options.client, options.transport, workspaceRoot, options.url);
1872
+ if (options.transport === "http" && !entry.url) throw new Error("HTTP 模式缺少 MCP 服务地址,请使用 --url 指定或在当前项目中检测到可用地址。");
1873
+ return {
1874
+ entry,
1875
+ preview: target.client === "codex" ? renderCodexBlock(target.serverName, entry) : renderJsonPreview(target.serverName, entry),
1876
+ target,
1877
+ transport: options.transport
1878
+ };
1879
+ }
1891
1880
  function formatMcpQuickStart(options) {
1892
1881
  const suffix = options.transport === "http" && options.httpUrl ? ` --transport http --url ${options.httpUrl}` : "";
1893
1882
  return [
@@ -1898,6 +1887,26 @@ function formatMcpQuickStart(options) {
1898
1887
  ];
1899
1888
  }
1900
1889
  //#endregion
1890
+ //#region src/cli/mcpClient/index.ts
1891
+ async function writeMcpClientConfig(plan) {
1892
+ await fs.ensureDir(path.dirname(plan.target.configPath));
1893
+ const existing = await fs.readFile(plan.target.configPath, "utf8").catch(() => "");
1894
+ if (plan.target.client === "codex") {
1895
+ const nextContent = upsertCodexManagedBlock(existing, plan.target.serverName, plan.preview);
1896
+ await fs.writeFile(plan.target.configPath, nextContent, "utf8");
1897
+ return;
1898
+ }
1899
+ const parsed = parseJsonConfig(existing, plan.target.configPath);
1900
+ const nextConfig = {
1901
+ ...parsed,
1902
+ mcpServers: {
1903
+ ...parsed.mcpServers ?? {},
1904
+ [plan.target.serverName]: plan.entry
1905
+ }
1906
+ };
1907
+ await fs.writeFile(plan.target.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
1908
+ }
1909
+ //#endregion
1901
1910
  //#region src/cli/commands/mcp.ts
1902
1911
  function resolvePort(port) {
1903
1912
  if (typeof port === "number" && Number.isInteger(port)) return port;
@@ -2068,25 +2077,13 @@ function registerPrepareCommand(cli) {
2068
2077
  }
2069
2078
  //#endregion
2070
2079
  //#region package.json
2071
- var version = "6.15.0";
2080
+ var version = "6.15.3";
2072
2081
  //#endregion
2073
- //#region src/cli/devHotkeys.ts
2074
- const DEV_SCREENSHOT_DIR = ".weapp-vite/dev-screenshots";
2075
- const DEFAULT_SCREENSHOT_TIMEOUT = 3e4;
2076
- const REG_PENDING_PREFIX = /^正在/;
2082
+ //#region src/cli/devHotkeys/format.ts
2077
2083
  const FULLWIDTH_ASCII_START = 65281;
2078
2084
  const FULLWIDTH_ASCII_END = 65374;
2079
2085
  const FULLWIDTH_ASCII_OFFSET = 65248;
2080
- const HOTKEY_DEDUP_WINDOW_MS = 32;
2081
- function formatMcpUrl(host, port, endpoint) {
2082
- return `http://${host}:${port}${endpoint}`;
2083
- }
2084
- function forwardSigint() {
2085
- process.kill(process.pid, "SIGINT");
2086
- }
2087
- function forwardSigtstp() {
2088
- process.kill(process.pid, "SIGTSTP");
2089
- }
2086
+ const REG_PENDING_PREFIX = /^正在/;
2090
2087
  function formatProjectLabel(cwd) {
2091
2088
  return path.basename(cwd) || cwd;
2092
2089
  }
@@ -2157,6 +2154,59 @@ function formatDevHotkeyHintWithState(state) {
2157
2154
  if (state.currentAction) return `${formatFooterLine(state)},按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
2158
2155
  return `开发快捷键已就绪,按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
2159
2156
  }
2157
+ function normalizeInputChar(input) {
2158
+ if (input.length !== 1) return input;
2159
+ const codePoint = input.codePointAt(0);
2160
+ if (!codePoint) return input;
2161
+ if (codePoint >= FULLWIDTH_ASCII_START && codePoint <= FULLWIDTH_ASCII_END) return String.fromCodePoint(codePoint - FULLWIDTH_ASCII_OFFSET);
2162
+ return input;
2163
+ }
2164
+ function resolveProjectLabel(cwd) {
2165
+ return formatProjectLabel(cwd);
2166
+ }
2167
+ //#endregion
2168
+ //#region src/cli/devHotkeys/mcp.ts
2169
+ function formatMcpUrl(host, port, endpoint) {
2170
+ return `http://${host}:${port}${endpoint}`;
2171
+ }
2172
+ function createToggleMcpAction(options) {
2173
+ const { cwd, getHandle, resolvedMcp, setHandle } = options;
2174
+ return async function toggleMcp() {
2175
+ if (!resolvedMcp.enabled) {
2176
+ logger_default.warn("[dev action] MCP 已在配置中禁用,跳过切换。");
2177
+ return "MCP 已禁用";
2178
+ }
2179
+ const existingHandle = getHandle();
2180
+ if (existingHandle?.close) {
2181
+ const url = formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
2182
+ logger_default.info(`[dev action] 正在关闭 MCP 服务:${colors.cyan(url)}`);
2183
+ await existingHandle.close();
2184
+ setHandle(void 0);
2185
+ logger_default.success(`[dev action] MCP 服务已关闭:${colors.cyan(url)}`);
2186
+ return `MCP 已关闭 (${url})`;
2187
+ }
2188
+ const url = formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
2189
+ logger_default.info(`[dev action] 正在启动 MCP 服务:${colors.cyan(url)}`);
2190
+ setHandle(await startWeappViteMcpServer({
2191
+ endpoint: resolvedMcp.endpoint,
2192
+ host: resolvedMcp.host,
2193
+ port: resolvedMcp.port,
2194
+ transport: "streamable-http",
2195
+ unref: false,
2196
+ workspaceRoot: cwd
2197
+ }));
2198
+ logger_default.success(`[dev action] MCP 服务已启动:${colors.cyan(url)}`);
2199
+ for (const line of formatMcpQuickStart({
2200
+ httpUrl: url,
2201
+ transport: "http"
2202
+ })) logger_default.info(line);
2203
+ return `MCP 已启动 (${url})`;
2204
+ };
2205
+ }
2206
+ //#endregion
2207
+ //#region src/cli/devHotkeys/screenshot.ts
2208
+ const DEV_SCREENSHOT_DIR = ".weapp-vite/dev-screenshots";
2209
+ const DEFAULT_SCREENSHOT_TIMEOUT = 3e4;
2160
2210
  /**
2161
2211
  * @description 生成开发态截图输出路径。
2162
2212
  */
@@ -2172,13 +2222,6 @@ function formatLogPath(cwd, targetPath) {
2172
2222
  function formatResolvedScreenshotPath(cwd, fallbackPath, result) {
2173
2223
  return formatLogPath(cwd, result.path ?? fallbackPath);
2174
2224
  }
2175
- function normalizeInputChar(input) {
2176
- if (input.length !== 1) return input;
2177
- const codePoint = input.codePointAt(0);
2178
- if (!codePoint) return input;
2179
- if (codePoint >= FULLWIDTH_ASCII_START && codePoint <= FULLWIDTH_ASCII_END) return String.fromCodePoint(codePoint - FULLWIDTH_ASCII_OFFSET);
2180
- return input;
2181
- }
2182
2225
  /**
2183
2226
  * @description 执行当前页面截图并输出结果日志。
2184
2227
  */
@@ -2197,6 +2240,14 @@ async function runScreenshotAction(options) {
2197
2240
  logger_default.success(`[dev action] 当前页面截图完成:${colors.cyan(resolvedPath)}`);
2198
2241
  return resolvedPath;
2199
2242
  }
2243
+ //#endregion
2244
+ //#region src/cli/devHotkeys/index.ts
2245
+ function forwardSigint() {
2246
+ process.kill(process.pid, "SIGINT");
2247
+ }
2248
+ function forwardSigtstp() {
2249
+ process.kill(process.pid, "SIGTSTP");
2250
+ }
2200
2251
  /**
2201
2252
  * @description 在开发态启动终端快捷键,并将动作输出到本地日志。
2202
2253
  */
@@ -2222,7 +2273,7 @@ function startDevHotkeys(options) {
2222
2273
  lastAction,
2223
2274
  mcpEnabled: resolvedMcp.enabled,
2224
2275
  mcpRunning: Boolean(mcpHandle?.close),
2225
- projectLabel: formatProjectLabel(options.cwd)
2276
+ projectLabel: resolveProjectLabel(options.cwd)
2226
2277
  });
2227
2278
  const detachTerminal = () => {
2228
2279
  if (hasSetRawMode) process.stdin.setRawMode(false);
@@ -2279,36 +2330,14 @@ function startDevHotkeys(options) {
2279
2330
  detachTerminal();
2280
2331
  forwardSigtstp();
2281
2332
  };
2282
- const toggleMcp = async () => {
2283
- if (!resolvedMcp.enabled) {
2284
- logger_default.warn("[dev action] MCP 已在配置中禁用,跳过切换。");
2285
- return "MCP 已禁用";
2333
+ const toggleMcp = createToggleMcpAction({
2334
+ cwd: options.cwd,
2335
+ getHandle: () => mcpHandle,
2336
+ resolvedMcp,
2337
+ setHandle: (handle) => {
2338
+ mcpHandle = handle;
2286
2339
  }
2287
- if (mcpHandle?.close) {
2288
- const url = formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
2289
- logger_default.info(`[dev action] 正在关闭 MCP 服务:${colors.cyan(url)}`);
2290
- await mcpHandle.close();
2291
- mcpHandle = void 0;
2292
- logger_default.success(`[dev action] MCP 服务已关闭:${colors.cyan(url)}`);
2293
- return `MCP 已关闭 (${url})`;
2294
- }
2295
- const url = formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
2296
- logger_default.info(`[dev action] 正在启动 MCP 服务:${colors.cyan(url)}`);
2297
- mcpHandle = await startWeappViteMcpServer({
2298
- endpoint: resolvedMcp.endpoint,
2299
- host: resolvedMcp.host,
2300
- port: resolvedMcp.port,
2301
- transport: "streamable-http",
2302
- unref: false,
2303
- workspaceRoot: options.cwd
2304
- });
2305
- logger_default.success(`[dev action] MCP 服务已启动:${colors.cyan(url)}`);
2306
- for (const line of formatMcpQuickStart({
2307
- httpUrl: url,
2308
- transport: "http"
2309
- })) logger_default.info(line);
2310
- return `MCP 已启动 (${url})`;
2311
- };
2340
+ });
2312
2341
  const runAction = (label, pendingLabel, action) => {
2313
2342
  if (running) {
2314
2343
  const current = currentAction ?? "已有命令";
@@ -2363,12 +2392,12 @@ function startDevHotkeys(options) {
2363
2392
  const handleInputOnce = (input, source) => {
2364
2393
  const normalizedInput = normalizeInputChar(input);
2365
2394
  const now = Date.now();
2366
- for (const [token, timestamp] of recentInputs) if (now - Number(timestamp.split(":")[1] ?? 0) > HOTKEY_DEDUP_WINDOW_MS) recentInputs.delete(token);
2395
+ for (const [token, timestamp] of recentInputs) if (now - Number(timestamp.split(":")[1] ?? 0) > 32) recentInputs.delete(token);
2367
2396
  const dedupKey = normalizedInput;
2368
2397
  const recentEntry = recentInputs.get(dedupKey);
2369
2398
  if (recentEntry !== void 0) {
2370
2399
  const [recentSource, recentTimestamp] = recentEntry.split(":");
2371
- if (recentSource !== source && now - Number(recentTimestamp) <= HOTKEY_DEDUP_WINDOW_MS) return;
2400
+ if (recentSource !== source && now - Number(recentTimestamp) <= 32) return;
2372
2401
  }
2373
2402
  recentInputs.set(dedupKey, `${source}:${now}`);
2374
2403
  handleInput(normalizedInput);
@@ -2404,32 +2433,15 @@ function startDevHotkeys(options) {
2404
2433
  };
2405
2434
  }
2406
2435
  //#endregion
2407
- //#region src/cli/commands/serve.ts
2436
+ //#region src/cli/commands/serve/analyze.ts
2437
+ const REG_DIST_PAGE_ENTRY = /pages\/.+\/index\.js$/;
2438
+ const REG_DIST_POSIX_SEP = /\\/g;
2408
2439
  function emitDashboardEvents(handle, events) {
2409
2440
  handle?.emitRuntimeEvents(events);
2410
2441
  }
2411
- function resolveWebHost(host) {
2412
- if (host === void 0) return;
2413
- if (typeof host === "boolean") return host;
2414
- if (typeof host === "string") return host;
2415
- return String(host);
2416
- }
2417
- const REG_DIST_PAGE_ENTRY = /pages\/.+\/index\.js$/;
2418
- const REG_DIST_POSIX_SEP = /\\/g;
2419
2442
  function hasAnalyzeData(result) {
2420
2443
  return result.packages.length > 0 || result.modules.length > 0;
2421
2444
  }
2422
- function waitForServeShutdownSignal() {
2423
- return new Promise((resolve) => {
2424
- const onSignal = () => {
2425
- process.off("SIGINT", onSignal);
2426
- process.off("SIGTERM", onSignal);
2427
- resolve();
2428
- };
2429
- process.on("SIGINT", onSignal);
2430
- process.on("SIGTERM", onSignal);
2431
- });
2432
- }
2433
2445
  async function collectOutputFiles(root) {
2434
2446
  const entries = await fs$1.readdir(root, { withFileTypes: true });
2435
2447
  const files = [];
@@ -2503,6 +2515,153 @@ async function analyzeUiFallback(ctx) {
2503
2515
  })).filter((item) => item.root).sort((a, b) => a.root.localeCompare(b.root))
2504
2516
  };
2505
2517
  }
2518
+ function createAnalyzeController(options) {
2519
+ const { configFile, ctx, options: cliOptions, targets } = options;
2520
+ const { configService } = ctx;
2521
+ let analyzeRunId = 0;
2522
+ let analyzeHandle;
2523
+ const runAnalyze = async () => {
2524
+ const startedAt = Date.now();
2525
+ try {
2526
+ const result = await analyzeSubpackages(await createCompilerContext({
2527
+ key: `serve-ui-analyze:${process.pid}:${++analyzeRunId}`,
2528
+ cwd: configService.cwd,
2529
+ mode: configService.mode,
2530
+ isDev: false,
2531
+ configFile,
2532
+ inlineConfig: createInlineConfig(targets.mpPlatform),
2533
+ cliPlatform: targets.rawPlatform,
2534
+ projectConfigPath: cliOptions.projectConfig,
2535
+ syncSupportFiles: false
2536
+ }));
2537
+ if (hasAnalyzeData(result)) return {
2538
+ result,
2539
+ durationMs: Date.now() - startedAt,
2540
+ mode: "full"
2541
+ };
2542
+ } catch (error) {
2543
+ const message = error instanceof Error ? error.message : String(error);
2544
+ logger_default.warn(`[ui] 完整分析失败,已回退到 dist 文件扫描:${message}`);
2545
+ return {
2546
+ result: await analyzeUiFallback(ctx),
2547
+ durationMs: Date.now() - startedAt,
2548
+ mode: "fallback",
2549
+ fallbackReason: message
2550
+ };
2551
+ }
2552
+ return {
2553
+ result: await analyzeUiFallback(ctx),
2554
+ durationMs: Date.now() - startedAt,
2555
+ mode: "fallback",
2556
+ fallbackReason: "完整分析结果为空,已回退到 dist 文件扫描。"
2557
+ };
2558
+ };
2559
+ const triggerAnalyzeUpdate = async (reason = "watch") => {
2560
+ if (!analyzeHandle) return;
2561
+ emitDashboardEvents(analyzeHandle, [{
2562
+ kind: reason === "watch" ? "hmr" : "build",
2563
+ level: "info",
2564
+ title: reason === "watch" ? "analyze refresh started" : "initial analyze started",
2565
+ detail: reason === "watch" ? "检测到新的构建结束事件,开始刷新 analyze 面板。" : "开发态 UI 已启动,开始生成第一份 analyze 结果。",
2566
+ tags: reason === "watch" ? ["watch", "analyze"] : ["initial", "analyze"]
2567
+ }]);
2568
+ const next = await runAnalyze();
2569
+ if (next.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
2570
+ kind: "diagnostic",
2571
+ level: "warning",
2572
+ title: "analyze fallback enabled",
2573
+ detail: next.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
2574
+ durationMs: next.durationMs,
2575
+ tags: ["analyze", "fallback"]
2576
+ }]);
2577
+ await analyzeHandle.update(next.result);
2578
+ emitDashboardEvents(analyzeHandle, [{
2579
+ kind: next.mode === "fallback" ? "diagnostic" : "build",
2580
+ level: next.mode === "fallback" ? "warning" : "success",
2581
+ title: reason === "watch" ? "analyze refresh completed" : "initial analyze completed",
2582
+ detail: next.mode === "fallback" ? `analyze 已回退到 dist 扫描,当前包含 ${next.result.packages.length} 个包。` : `analyze 已刷新完成,当前包含 ${next.result.packages.length} 个包与 ${next.result.modules.length} 个模块。`,
2583
+ durationMs: next.durationMs,
2584
+ tags: next.mode === "fallback" ? ["analyze", "fallback"] : ["analyze", reason === "watch" ? "refresh" : "initial"]
2585
+ }]);
2586
+ };
2587
+ const bindWatcher = (buildResult) => {
2588
+ let updating = false;
2589
+ if (analyzeHandle && buildResult && typeof buildResult.on === "function") buildResult.on("event", (event) => {
2590
+ if (event.code !== "END" || updating) return;
2591
+ updating = true;
2592
+ triggerAnalyzeUpdate("watch").finally(() => {
2593
+ updating = false;
2594
+ });
2595
+ });
2596
+ return { async runInitialUpdate() {
2597
+ if (analyzeHandle) {
2598
+ updating = true;
2599
+ await triggerAnalyzeUpdate("initial");
2600
+ updating = false;
2601
+ }
2602
+ } };
2603
+ };
2604
+ return {
2605
+ getHandle: () => analyzeHandle,
2606
+ async startDashboard(startDashboard) {
2607
+ const initialAnalyze = await runAnalyze();
2608
+ analyzeHandle = await startDashboard(initialAnalyze.result, {
2609
+ watch: true,
2610
+ cwd: configService.cwd,
2611
+ packageManagerAgent: configService.packageManager.agent,
2612
+ silentStartupLog: true
2613
+ }) ?? void 0;
2614
+ emitDashboardEvents(analyzeHandle, [{
2615
+ kind: "command",
2616
+ level: "success",
2617
+ title: "dev ui session ready",
2618
+ detail: `开发态分析面板已启动,当前包含 ${initialAnalyze.result.packages.length} 个包。`,
2619
+ durationMs: initialAnalyze.durationMs,
2620
+ tags: ["dev", "ui"]
2621
+ }]);
2622
+ if (initialAnalyze.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
2623
+ kind: "diagnostic",
2624
+ level: "warning",
2625
+ title: "initial analyze fallback enabled",
2626
+ detail: initialAnalyze.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
2627
+ durationMs: initialAnalyze.durationMs,
2628
+ tags: [
2629
+ "analyze",
2630
+ "fallback",
2631
+ "initial"
2632
+ ]
2633
+ }]);
2634
+ },
2635
+ bindWatcher,
2636
+ emitRuntimeEvents(events) {
2637
+ emitDashboardEvents(analyzeHandle, events);
2638
+ },
2639
+ waitForExit() {
2640
+ return analyzeHandle?.waitForExit();
2641
+ }
2642
+ };
2643
+ }
2644
+ //#endregion
2645
+ //#region src/cli/commands/serve/shared.ts
2646
+ function resolveWebHost(host) {
2647
+ if (host === void 0) return;
2648
+ if (typeof host === "boolean") return host;
2649
+ if (typeof host === "string") return host;
2650
+ return String(host);
2651
+ }
2652
+ function waitForServeShutdownSignal() {
2653
+ return new Promise((resolve) => {
2654
+ const onSignal = () => {
2655
+ process.off("SIGINT", onSignal);
2656
+ process.off("SIGTERM", onSignal);
2657
+ resolve();
2658
+ };
2659
+ process.on("SIGINT", onSignal);
2660
+ process.on("SIGTERM", onSignal);
2661
+ });
2662
+ }
2663
+ //#endregion
2664
+ //#region src/cli/commands/serve/index.ts
2506
2665
  function registerServeCommand(cli) {
2507
2666
  cli.command("[root]", "start dev server").alias("serve").alias("dev").option("--skipNpm", `[boolean] if skip npm build`).option("-o, --open", `[boolean] open ide`).option("-p, --platform <platform>", `[string] target platform (weapp | h5 | all)`).option("--project-config <path>", `[string] project config path (miniprogram only)`).option("--trust-project", "[boolean] auto trust Wechat DevTools project on open", { default: true }).option("--host [host]", `[string] web dev server host`).option("--ui", `[boolean] 启动调试 UI(当前提供分析视图)`, { default: false }).option("--analyze", `[boolean] 启动分包分析仪表盘 (实验特性)`, { default: false }).action(async (root, options) => {
2508
2667
  filterDuplicateOptions(options);
@@ -2551,7 +2710,6 @@ function registerServeCommand(cli) {
2551
2710
  logRuntimeTarget(targets, { resolvedConfigPlatform: configService.platform });
2552
2711
  const enableAnalyze = Boolean(isUiEnabled(options) && targets.runMini);
2553
2712
  let analyzeHandle;
2554
- let analyzeRunId = 0;
2555
2713
  const devHotkeysSession = targets.runMini ? startDevHotkeys({
2556
2714
  cwd: configService.cwd,
2557
2715
  mcpConfig: configService.weappViteConfig?.mcp,
@@ -2560,115 +2718,20 @@ function registerServeCommand(cli) {
2560
2718
  silentStartupHint: true
2561
2719
  }) : void 0;
2562
2720
  try {
2563
- const runAnalyze = async () => {
2564
- const startedAt = Date.now();
2565
- try {
2566
- const result = await analyzeSubpackages(await createCompilerContext({
2567
- key: `serve-ui-analyze:${process.pid}:${++analyzeRunId}`,
2568
- cwd: configService.cwd,
2569
- mode: configService.mode,
2570
- isDev: false,
2571
- configFile,
2572
- inlineConfig: createInlineConfig(targets.mpPlatform),
2573
- cliPlatform: targets.rawPlatform,
2574
- projectConfigPath: options.projectConfig,
2575
- syncSupportFiles: false
2576
- }));
2577
- if (hasAnalyzeData(result)) return {
2578
- result,
2579
- durationMs: Date.now() - startedAt,
2580
- mode: "full"
2581
- };
2582
- } catch (error) {
2583
- const message = error instanceof Error ? error.message : String(error);
2584
- logger_default.warn(`[ui] 完整分析失败,已回退到 dist 文件扫描:${message}`);
2585
- return {
2586
- result: await analyzeUiFallback(ctx),
2587
- durationMs: Date.now() - startedAt,
2588
- mode: "fallback",
2589
- fallbackReason: message
2590
- };
2591
- }
2592
- return {
2593
- result: await analyzeUiFallback(ctx),
2594
- durationMs: Date.now() - startedAt,
2595
- mode: "fallback",
2596
- fallbackReason: "完整分析结果为空,已回退到 dist 文件扫描。"
2597
- };
2598
- };
2599
- const triggerAnalyzeUpdate = async (reason = "watch") => {
2600
- if (!analyzeHandle) return;
2601
- emitDashboardEvents(analyzeHandle, [{
2602
- kind: reason === "watch" ? "hmr" : "build",
2603
- level: "info",
2604
- title: reason === "watch" ? "analyze refresh started" : "initial analyze started",
2605
- detail: reason === "watch" ? "检测到新的构建结束事件,开始刷新 analyze 面板。" : "开发态 UI 已启动,开始生成第一份 analyze 结果。",
2606
- tags: reason === "watch" ? ["watch", "analyze"] : ["initial", "analyze"]
2607
- }]);
2608
- const next = await runAnalyze();
2609
- if (next.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
2610
- kind: "diagnostic",
2611
- level: "warning",
2612
- title: "analyze fallback enabled",
2613
- detail: next.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
2614
- durationMs: next.durationMs,
2615
- tags: ["analyze", "fallback"]
2616
- }]);
2617
- await analyzeHandle.update(next.result);
2618
- emitDashboardEvents(analyzeHandle, [{
2619
- kind: next.mode === "fallback" ? "diagnostic" : "build",
2620
- level: next.mode === "fallback" ? "warning" : "success",
2621
- title: reason === "watch" ? "analyze refresh completed" : "initial analyze completed",
2622
- detail: next.mode === "fallback" ? `analyze 已回退到 dist 扫描,当前包含 ${next.result.packages.length} 个包。` : `analyze 已刷新完成,当前包含 ${next.result.packages.length} 个包与 ${next.result.modules.length} 个模块。`,
2623
- durationMs: next.durationMs,
2624
- tags: next.mode === "fallback" ? ["analyze", "fallback"] : ["analyze", reason === "watch" ? "refresh" : "initial"]
2625
- }]);
2626
- };
2721
+ const analyzeController = createAnalyzeController({
2722
+ configFile,
2723
+ ctx,
2724
+ options,
2725
+ targets
2726
+ });
2627
2727
  if (targets.runMini) {
2628
2728
  const miniBuildStartedAt = Date.now();
2629
2729
  const buildResult = await buildService.build(options);
2630
2730
  logger_default.success(`小程序初次构建完成,耗时:${formatDuration(Date.now() - miniBuildStartedAt)}`);
2631
2731
  if (enableAnalyze) {
2632
- const initialAnalyze = await runAnalyze();
2633
- analyzeHandle = await startAnalyzeDashboard(initialAnalyze.result, {
2634
- watch: true,
2635
- cwd: configService.cwd,
2636
- packageManagerAgent: configService.packageManager.agent,
2637
- silentStartupLog: true
2638
- }) ?? void 0;
2639
- emitDashboardEvents(analyzeHandle, [{
2640
- kind: "command",
2641
- level: "success",
2642
- title: "dev ui session ready",
2643
- detail: `开发态分析面板已启动,当前包含 ${initialAnalyze.result.packages.length} 个包。`,
2644
- durationMs: initialAnalyze.durationMs,
2645
- tags: ["dev", "ui"]
2646
- }]);
2647
- if (initialAnalyze.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
2648
- kind: "diagnostic",
2649
- level: "warning",
2650
- title: "initial analyze fallback enabled",
2651
- detail: initialAnalyze.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
2652
- durationMs: initialAnalyze.durationMs,
2653
- tags: [
2654
- "analyze",
2655
- "fallback",
2656
- "initial"
2657
- ]
2658
- }]);
2659
- let updating = false;
2660
- if (analyzeHandle && buildResult && typeof buildResult.on === "function") buildResult.on("event", (event) => {
2661
- if (event.code !== "END" || updating) return;
2662
- updating = true;
2663
- triggerAnalyzeUpdate("watch").finally(() => {
2664
- updating = false;
2665
- });
2666
- });
2667
- if (analyzeHandle) {
2668
- updating = true;
2669
- await triggerAnalyzeUpdate("initial");
2670
- updating = false;
2671
- }
2732
+ await analyzeController.startDashboard(startAnalyzeDashboard);
2733
+ analyzeHandle = analyzeController.getHandle();
2734
+ await analyzeController.bindWatcher(buildResult).runInitialUpdate();
2672
2735
  }
2673
2736
  }
2674
2737
  let webServer;
@@ -2677,7 +2740,7 @@ function registerServeCommand(cli) {
2677
2740
  try {
2678
2741
  webServer = await webService?.startDevServer();
2679
2742
  logger_default.success(`Web 开发服务启动完成,耗时:${formatDuration(Date.now() - webServerStartedAt)}`);
2680
- emitDashboardEvents(analyzeHandle, [{
2743
+ analyzeController.emitRuntimeEvents([{
2681
2744
  kind: "system",
2682
2745
  level: "success",
2683
2746
  title: "web dev server started",
@@ -2686,7 +2749,7 @@ function registerServeCommand(cli) {
2686
2749
  tags: ["dev", "web"]
2687
2750
  }]);
2688
2751
  } catch (error) {
2689
- emitDashboardEvents(analyzeHandle, [{
2752
+ analyzeController.emitRuntimeEvents([{
2690
2753
  kind: "diagnostic",
2691
2754
  level: "error",
2692
2755
  title: "web dev server failed",
@@ -2706,7 +2769,7 @@ function registerServeCommand(cli) {
2706
2769
  devHotkeysSession?.restore();
2707
2770
  } else if (targets.runWeb) logBuildAppFinish(configService, webServer, { skipMini: true });
2708
2771
  if (options.open && targets.runMini) {
2709
- emitDashboardEvents(analyzeHandle, [{
2772
+ analyzeController.emitRuntimeEvents([{
2710
2773
  kind: "command",
2711
2774
  level: "info",
2712
2775
  title: "opening ide",
@@ -2721,7 +2784,7 @@ function registerServeCommand(cli) {
2721
2784
  })) await openIde(configService.platform, resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd), { trustProject: options.trustProject });
2722
2785
  devHotkeysSession?.restore();
2723
2786
  }
2724
- if (analyzeHandle) await analyzeHandle.waitForExit();
2787
+ if (analyzeHandle) await analyzeController.waitForExit();
2725
2788
  else if (targets.runMini || targets.runWeb) await waitForServeShutdownSignal();
2726
2789
  } finally {
2727
2790
  devHotkeysSession?.close();