weapp-vite 6.11.9 → 6.12.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.
@@ -1,4 +1,4 @@
1
- import { i as getCompilerContext } from "./createContext-DZgOqIMU.mjs";
1
+ import { i as getCompilerContext, l as getRouteRuntimeGlobalKeys } from "./createContext-Cy_160Dm.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) {
@@ -28,7 +28,10 @@ function resolveMiniProgramGlobal() {
28
28
  const runtime = globalThis;
29
29
  const overrideRuntime = runtime[ROUTE_RUNTIME_OVERRIDE_KEY];
30
30
  if (overrideRuntime) return overrideRuntime;
31
- return runtime.wx ?? runtime.tt ?? runtime.my;
31
+ for (const key of getRouteRuntimeGlobalKeys()) {
32
+ const candidate = runtime[key];
33
+ if (candidate) return candidate;
34
+ }
32
35
  }
33
36
  function callRouteMethod(methodName, option) {
34
37
  const miniProgramGlobal = resolveMiniProgramGlobal();
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { _ as isPathInside, c as normalizeMiniPlatform, d as SHARED_CHUNK_VIRTUAL_PREFIX, f as resolveWeappConfigFile, g as createCjsConfigLoadError, h as parseCommentJson, l as resolveMiniPlatform, m as getProjectConfigFileName, n as syncProjectSupportFiles, o as formatBytes, p as checkRuntime, r as syncManagedTsconfigBootstrapFiles, s as DEFAULT_MP_PLATFORM, t as createCompilerContext, u as createSharedBuildConfig } from "./createContext-DZgOqIMU.mjs";
1
+ import { _ as normalizeMiniPlatform, b as isPathInside, c as SHARED_CHUNK_VIRTUAL_PREFIX, d as checkRuntime, f as getProjectConfigFileName, g as getDefaultIdeProjectRoot, h as DEFAULT_MP_PLATFORM, m as createCjsConfigLoadError, n as syncProjectSupportFiles, o as formatBytes, p as parseCommentJson, r as syncManagedTsconfigBootstrapFiles, s as createSharedBuildConfig, t as createCompilerContext, u as resolveWeappConfigFile, v as resolveMiniPlatform, y as shouldPassPlatformArgToIdeOpen } from "./createContext-Cy_160Dm.mjs";
2
2
  import { r as logger_default, t as colors } from "./logger-gutcwWKE.mjs";
3
- import { f as VERSION } from "./file-rRQhPOL8.mjs";
3
+ import { p as VERSION } from "./file-DKg072cZ.mjs";
4
4
  import { resolveWeappMcpConfig, startWeappViteMcpServer } from "./mcp.mjs";
5
5
  import { createRequire } from "node:module";
6
6
  import { defu } from "@weapp-core/shared";
@@ -22,6 +22,20 @@ import { initConfig } from "@weapp-core/init";
22
22
  //#region src/analyze/subpackages/classifier.ts
23
23
  const VIRTUAL_MODULE_INDICATOR = "\0";
24
24
  const VIRTUAL_PREFIX = `${SHARED_CHUNK_VIRTUAL_PREFIX}/`;
25
+ function classifyModuleSourceKind(options) {
26
+ if (options.isNodeModule) return "node_modules";
27
+ if (options.inSrc) return "src";
28
+ if (options.inPlugin) return "plugin";
29
+ return "workspace";
30
+ }
31
+ function resolvePluginAssetAbsolute(normalizedFileName, pluginRoot) {
32
+ if (!pluginRoot) return;
33
+ const pluginBase = posix.basename(pluginRoot);
34
+ if (normalizedFileName !== pluginBase && !normalizedFileName.startsWith(`${pluginBase}/`)) return;
35
+ const relative = normalizedFileName === pluginBase ? "" : normalizedFileName.slice(pluginBase.length + 1);
36
+ const absolute = posix.resolve(pluginRoot, relative);
37
+ return isPathInside(pluginRoot, absolute) ? absolute : void 0;
38
+ }
25
39
  function classifyPackage(fileName, origin, context) {
26
40
  if (fileName.startsWith(VIRTUAL_PREFIX)) {
27
41
  const combination = fileName.slice(VIRTUAL_PREFIX.length).split("/")[0] || "shared";
@@ -56,13 +70,11 @@ function resolveModuleSourceType(absoluteId, ctx) {
56
70
  const isNodeModule = absoluteId.includes("/node_modules/") || absoluteId.includes("\\node_modules\\");
57
71
  const pluginRoot = configService.absolutePluginRoot;
58
72
  const srcRoot = configService.absoluteSrcRoot;
59
- const inSrc = isPathInside(srcRoot, absoluteId);
60
- const inPlugin = pluginRoot ? isPathInside(pluginRoot, absoluteId) : false;
61
- let sourceType;
62
- if (isNodeModule) sourceType = "node_modules";
63
- else if (inSrc) sourceType = "src";
64
- else if (inPlugin) sourceType = "plugin";
65
- else sourceType = "workspace";
73
+ const sourceType = classifyModuleSourceKind({
74
+ isNodeModule,
75
+ inSrc: isPathInside(srcRoot, absoluteId),
76
+ inPlugin: pluginRoot ? isPathInside(pluginRoot, absoluteId) : false
77
+ });
66
78
  return {
67
79
  source: configService.relativeAbsoluteSrcRoot(absoluteId),
68
80
  sourceType
@@ -77,19 +89,12 @@ function resolveAssetSource(fileName, ctx) {
77
89
  source: configService.relativeAbsoluteSrcRoot(srcCandidate),
78
90
  sourceType: "src"
79
91
  };
80
- const pluginRoot = configService.absolutePluginRoot;
81
- if (pluginRoot) {
82
- const pluginBase = posix.basename(pluginRoot);
83
- if (normalized === pluginBase || normalized.startsWith(`${pluginBase}/`)) {
84
- const relative = normalized === pluginBase ? "" : normalized.slice(pluginBase.length + 1);
85
- const absolute = posix.resolve(pluginRoot, relative);
86
- if (isPathInside(pluginRoot, absolute)) return {
87
- absolute,
88
- source: configService.relativeAbsoluteSrcRoot(absolute),
89
- sourceType: "plugin"
90
- };
91
- }
92
- }
92
+ const pluginAbsolute = resolvePluginAssetAbsolute(normalized, configService.absolutePluginRoot);
93
+ if (pluginAbsolute) return {
94
+ absolute: pluginAbsolute,
95
+ source: configService.relativeAbsoluteSrcRoot(pluginAbsolute),
96
+ sourceType: "plugin"
97
+ };
93
98
  }
94
99
  //#endregion
95
100
  //#region src/analyze/subpackages/registry.ts
@@ -315,14 +320,32 @@ async function analyzeSubpackages(ctx) {
315
320
  //#endregion
316
321
  //#region src/cli/analyze/dashboard.ts
317
322
  const ANALYZE_GLOBAL_KEY = "__WEAPP_VITE_ANALYZE_RESULT__";
323
+ const DASHBOARD_EVENTS_GLOBAL_KEY = "__WEAPP_VITE_DASHBOARD_EVENTS__";
318
324
  const ANALYZE_DASHBOARD_PACKAGE_NAME = "@weapp-vite/dashboard";
319
325
  const ANALYZE_SSE_PATH = "/__weapp_vite_analyze";
326
+ const DASHBOARD_EVENT_NAME = "weapp-dashboard:event";
320
327
  const require = createRequire(import.meta.url);
321
328
  function createInstallCommand(agent) {
322
329
  const resolved = resolveCommand(agent ?? "npm", "install", [ANALYZE_DASHBOARD_PACKAGE_NAME]);
323
330
  if (!resolved) return `npm install ${ANALYZE_DASHBOARD_PACKAGE_NAME}`;
324
331
  return `${resolved.command} ${resolved.args.join(" ")}`;
325
332
  }
333
+ function formatEventTimestamp(date = /* @__PURE__ */ new Date()) {
334
+ return date.toLocaleTimeString("zh-CN", { hour12: false });
335
+ }
336
+ function createDashboardRuntimeEvent(input) {
337
+ return {
338
+ id: `dashboard:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`,
339
+ kind: input.kind,
340
+ level: input.level,
341
+ title: input.title,
342
+ detail: input.detail,
343
+ timestamp: formatEventTimestamp(),
344
+ source: input.source ?? "weapp-vite",
345
+ durationMs: input.durationMs,
346
+ tags: input.tags
347
+ };
348
+ }
326
349
  function readDashboardManifest(packageJsonPath) {
327
350
  try {
328
351
  return parseCommentJson(fs$2.readFileSync(packageJsonPath, "utf8"));
@@ -371,13 +394,25 @@ function resolveDashboardRoot(options) {
371
394
  logger_default.warn(`[weapp-vite ui] 未安装可选仪表盘包 ${colors.bold(colors.green(ANALYZE_DASHBOARD_PACKAGE_NAME))},已自动降级关闭 dashboard 能力。`);
372
395
  logger_default.info(`如需启用,请执行 ${colors.bold(colors.green(createInstallCommand(options?.packageManagerAgent)))}`);
373
396
  }
374
- function createAnalyzeHtmlPlugin(state, onServerInstance, onBroadcastReady) {
397
+ function createAnalyzeHtmlPlugin(state, runtimeEvents, onServerInstance, onBroadcastReady) {
375
398
  const sseClients = /* @__PURE__ */ new Set();
376
399
  const hotBridgeScript = `
377
400
  const applyAnalyzePayload = (payload) => {
378
401
  window.${ANALYZE_GLOBAL_KEY} = payload
379
402
  window.dispatchEvent(new CustomEvent('weapp-analyze:update', { detail: payload }))
380
403
  }
404
+ const applyDashboardEvents = (payload) => {
405
+ const events = Array.isArray(payload) ? payload : [payload]
406
+ const nextEvents = events.filter(Boolean)
407
+ if (nextEvents.length === 0) {
408
+ return
409
+ }
410
+ window.${DASHBOARD_EVENTS_GLOBAL_KEY} = [
411
+ ...(window.${DASHBOARD_EVENTS_GLOBAL_KEY} ?? []),
412
+ ...nextEvents,
413
+ ]
414
+ window.dispatchEvent(new CustomEvent('${DASHBOARD_EVENT_NAME}', { detail: nextEvents }))
415
+ }
381
416
  const source = new EventSource('${ANALYZE_SSE_PATH}')
382
417
  source.onmessage = (event) => {
383
418
  try {
@@ -389,6 +424,9 @@ function createAnalyzeHtmlPlugin(state, onServerInstance, onBroadcastReady) {
389
424
  import.meta.hot.on('weapp-analyze:update', (payload) => {
390
425
  applyAnalyzePayload(payload)
391
426
  })
427
+ import.meta.hot.on('${DASHBOARD_EVENT_NAME}', (payload) => {
428
+ applyDashboardEvents(payload)
429
+ })
392
430
  }
393
431
  `.trim();
394
432
  const broadcast = (payload) => {
@@ -407,6 +445,11 @@ function createAnalyzeHtmlPlugin(state, onServerInstance, onBroadcastReady) {
407
445
  children: `window.${ANALYZE_GLOBAL_KEY} = ${JSON.stringify(state.current)}`,
408
446
  injectTo: "head-prepend"
409
447
  },
448
+ {
449
+ tag: "script",
450
+ children: `window.${DASHBOARD_EVENTS_GLOBAL_KEY} = ${JSON.stringify(runtimeEvents.current)}`,
451
+ injectTo: "head-prepend"
452
+ },
410
453
  {
411
454
  tag: "script",
412
455
  attrs: {
@@ -475,9 +518,16 @@ async function startAnalyzeDashboard(result, options) {
475
518
  if (!resolved) return;
476
519
  const { root, configFile } = resolved;
477
520
  const state = { current: result };
521
+ const runtimeEvents = { current: [createDashboardRuntimeEvent({
522
+ kind: "command",
523
+ level: "success",
524
+ title: options?.watch ? "dashboard watch session started" : "dashboard static session started",
525
+ detail: options?.watch ? "weapp-vite UI 已进入实时分析模式,后续 analyze 结果会继续推送到 dashboard。" : "weapp-vite UI 已进入静态分析模式,当前页面展示的是一次性分析结果。",
526
+ tags: options?.watch ? ["watch", "analyze"] : ["static", "analyze"]
527
+ })] };
478
528
  let serverRef;
479
529
  let broadcastAnalyzeResult;
480
- const plugins = [createAnalyzeHtmlPlugin(state, (server) => {
530
+ const plugins = [createAnalyzeHtmlPlugin(state, runtimeEvents, (server) => {
481
531
  serverRef = server;
482
532
  }, (broadcast) => {
483
533
  broadcastAnalyzeResult = broadcast;
@@ -507,15 +557,39 @@ async function startAnalyzeDashboard(result, options) {
507
557
  return [...resolved.local ?? [], ...resolved.network ?? []];
508
558
  })();
509
559
  const waitPromise = waitForServerExit(server);
510
- if (serverRef?.ws) serverRef.ws.send({
511
- type: "custom",
512
- event: "weapp-analyze:update",
513
- data: state.current
514
- });
560
+ if (serverRef?.ws) {
561
+ serverRef.ws.send({
562
+ type: "custom",
563
+ event: "weapp-analyze:update",
564
+ data: state.current
565
+ });
566
+ serverRef.ws.send({
567
+ type: "custom",
568
+ event: DASHBOARD_EVENT_NAME,
569
+ data: runtimeEvents.current
570
+ });
571
+ }
515
572
  broadcastAnalyzeResult?.(state.current);
573
+ const emitRuntimeEvents = (events) => {
574
+ if (events.length === 0) return;
575
+ const nextEvents = events.map((event) => createDashboardRuntimeEvent(event));
576
+ runtimeEvents.current = [...nextEvents, ...runtimeEvents.current].slice(0, 24);
577
+ if (serverRef) serverRef.ws.send({
578
+ type: "custom",
579
+ event: DASHBOARD_EVENT_NAME,
580
+ data: nextEvents
581
+ });
582
+ };
516
583
  const handle = {
517
584
  async update(nextResult) {
518
585
  state.current = nextResult;
586
+ emitRuntimeEvents([{
587
+ kind: "build",
588
+ level: "info",
589
+ title: "analyze payload refreshed",
590
+ detail: `已推送新的 analyze 结果,当前包含 ${nextResult.packages.length} 个包与 ${nextResult.modules.length} 个模块。`,
591
+ tags: ["analyze", "refresh"]
592
+ }]);
519
593
  if (serverRef) serverRef.ws.send({
520
594
  type: "custom",
521
595
  event: "weapp-analyze:update",
@@ -523,6 +597,7 @@ async function startAnalyzeDashboard(result, options) {
523
597
  });
524
598
  broadcastAnalyzeResult?.(nextResult);
525
599
  },
600
+ emitRuntimeEvents,
526
601
  waitForExit: () => waitPromise,
527
602
  close: async () => {
528
603
  await server.close();
@@ -883,10 +958,69 @@ function logBuildPackageSizeReport(options) {
883
958
  //#endregion
884
959
  //#region src/cli/openIde.ts
885
960
  const execFileAsync = promisify(execFile);
961
+ /**
962
+ * @description 执行 IDE 打开流程,并在登录失效时允许按键重试。
963
+ */
964
+ async function runWechatIdeOpenWithRetry(argv) {
965
+ let retrying = true;
966
+ while (retrying) try {
967
+ await parse(argv);
968
+ return;
969
+ } catch (error) {
970
+ if (!isWechatIdeLoginRequiredError(error)) {
971
+ logger_default.error(error);
972
+ return;
973
+ }
974
+ logger_default.error("检测到微信开发者工具登录状态失效,请先登录后重试。");
975
+ logger_default.warn(formatWechatIdeLoginRequiredError(error));
976
+ logger_default.info(formatRetryHotkeyPrompt());
977
+ if (!await waitForRetryKeypress()) {
978
+ logger_default.warn("已取消重试。完成登录后请重新执行当前命令。");
979
+ retrying = false;
980
+ continue;
981
+ }
982
+ logger_default.info(colors.bold(colors.green("正在重试连接微信开发者工具...")));
983
+ }
984
+ }
985
+ async function closeIdeByAppleScript() {
986
+ if (process.platform !== "darwin") return false;
987
+ const appName = process.env.WEAPP_DEVTOOLS_APP_NAME || "wechatwebdevtools";
988
+ try {
989
+ await execFileAsync("osascript", ["-e", `tell application "${appName}" to quit`]);
990
+ return true;
991
+ } catch {
992
+ return false;
993
+ }
994
+ }
995
+ async function closeIdeByProcessKill(cliPath) {
996
+ if (!cliPath) return false;
997
+ const appContentsRoot = cliPath.includes(".app/") ? cliPath.slice(0, cliPath.indexOf(".app/") + 4) : path.dirname(path.dirname(cliPath));
998
+ try {
999
+ await execFileAsync("pkill", ["-f", appContentsRoot]);
1000
+ return true;
1001
+ } catch {
1002
+ return false;
1003
+ }
1004
+ }
1005
+ /**
1006
+ * @description 根据 mpDistRoot 推导 IDE 项目目录(目录内应包含 project/mini 配置)
1007
+ */
1008
+ function resolveIdeProjectPath(mpDistRoot) {
1009
+ if (!mpDistRoot || !mpDistRoot.trim()) return;
1010
+ const parent = path.dirname(mpDistRoot);
1011
+ if (!parent || parent === "." || parent === "/") return;
1012
+ return parent;
1013
+ }
1014
+ /**
1015
+ * @description 结合 mpDistRoot 与配置根目录解析最终 IDE 项目目录。
1016
+ */
1017
+ function resolveIdeProjectRoot(mpDistRoot, cwd) {
1018
+ return resolveIdeProjectPath(mpDistRoot) ?? cwd;
1019
+ }
886
1020
  async function openIde(platform, projectPath) {
887
1021
  const argv = ["open", "-p"];
888
1022
  if (projectPath) argv.push(projectPath);
889
- if (platform === "alipay") argv.push("--platform", platform);
1023
+ if (shouldPassPlatformArgToIdeOpen(platform)) argv.push("--platform", platform);
890
1024
  await runWechatIdeOpenWithRetry(argv);
891
1025
  }
892
1026
  async function closeIde() {
@@ -941,73 +1075,31 @@ async function resolveIdeCommandContext(options) {
941
1075
  mpDistRoot: ctx.configService.mpDistRoot
942
1076
  };
943
1077
  } catch {}
944
- if (!projectPath && platform === "alipay") projectPath = resolveIdeProjectRoot("dist/alipay/dist", cwd);
1078
+ if (!projectPath) {
1079
+ const defaultProjectRoot = getDefaultIdeProjectRoot(platform);
1080
+ if (defaultProjectRoot) projectPath = resolveIdeProjectRoot(defaultProjectRoot, cwd);
1081
+ }
945
1082
  return {
946
1083
  platform,
947
1084
  projectPath
948
1085
  };
949
1086
  }
950
- /**
951
- * @description 执行 IDE 打开流程,并在登录失效时允许按键重试。
952
- */
953
- async function runWechatIdeOpenWithRetry(argv) {
954
- let retrying = true;
955
- while (retrying) try {
956
- await parse(argv);
957
- return;
958
- } catch (error) {
959
- if (!isWechatIdeLoginRequiredError(error)) {
960
- logger_default.error(error);
961
- return;
962
- }
963
- logger_default.error("检测到微信开发者工具登录状态失效,请先登录后重试。");
964
- logger_default.warn(formatWechatIdeLoginRequiredError(error));
965
- logger_default.info(formatRetryHotkeyPrompt());
966
- if (!await waitForRetryKeypress()) {
967
- logger_default.warn("已取消重试。完成登录后请重新执行当前命令。");
968
- retrying = false;
969
- continue;
970
- }
971
- logger_default.info(colors.bold(colors.green("正在重试连接微信开发者工具...")));
972
- }
973
- }
974
- async function closeIdeByAppleScript() {
975
- if (process.platform !== "darwin") return false;
976
- const appName = process.env.WEAPP_DEVTOOLS_APP_NAME || "wechatwebdevtools";
977
- try {
978
- await execFileAsync("osascript", ["-e", `tell application "${appName}" to quit`]);
979
- return true;
980
- } catch {
981
- return false;
982
- }
983
- }
984
- async function closeIdeByProcessKill(cliPath) {
985
- if (!cliPath) return false;
986
- const appContentsRoot = cliPath.includes(".app/") ? cliPath.slice(0, cliPath.indexOf(".app/") + 4) : path.dirname(path.dirname(cliPath));
987
- try {
988
- await execFileAsync("pkill", ["-f", appContentsRoot]);
989
- return true;
990
- } catch {
991
- return false;
992
- }
1087
+ //#endregion
1088
+ //#region src/cli/commands/build.ts
1089
+ function isSassEmbeddedChild(handle) {
1090
+ return Boolean(handle && typeof handle === "object" && "kill" in handle && "spawnfile" in handle && typeof handle.spawnfile === "string" && handle.spawnfile?.includes("sass-embedded"));
993
1091
  }
994
- /**
995
- * @description 根据 mpDistRoot 推导 IDE 项目目录(目录内应包含 project/mini 配置)
996
- */
997
- function resolveIdeProjectPath(mpDistRoot) {
998
- if (!mpDistRoot || !mpDistRoot.trim()) return;
999
- const parent = path.dirname(mpDistRoot);
1000
- if (!parent || parent === "." || parent === "/") return;
1001
- return parent;
1092
+ function terminateStaleSassEmbeddedProcess() {
1093
+ const getHandles = process._getActiveHandles;
1094
+ const handles = typeof getHandles === "function" ? getHandles() : void 0;
1095
+ if (!Array.isArray(handles)) return;
1096
+ for (const handle of handles) if (isSassEmbeddedChild(handle)) try {
1097
+ handle.kill();
1098
+ } catch {}
1002
1099
  }
1003
- /**
1004
- * @description 结合 mpDistRoot 与配置根目录解析最终 IDE 项目目录。
1005
- */
1006
- function resolveIdeProjectRoot(mpDistRoot, cwd) {
1007
- return resolveIdeProjectPath(mpDistRoot) ?? cwd;
1100
+ function emitDashboardEvents$1(handle, events) {
1101
+ handle?.emitRuntimeEvents(events);
1008
1102
  }
1009
- //#endregion
1010
- //#region src/cli/commands/build.ts
1011
1103
  function registerBuildCommand(cli) {
1012
1104
  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 | h5 | 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("--ui", `[boolean] 启动调试 UI(当前提供分析视图)`, { default: false }).option("--analyze", `[boolean] 输出分包分析仪表盘`, { default: false }).action(async (root, options) => {
1013
1105
  filterDuplicateOptions(options);
@@ -1027,44 +1119,73 @@ function registerBuildCommand(cli) {
1027
1119
  const enableAnalyze = Boolean(isUiEnabled(options) && targets.runMini);
1028
1120
  let analyzeHandle;
1029
1121
  if (targets.runMini) {
1122
+ const miniBuildStartedAt = Date.now();
1030
1123
  const output = await buildService.build(options);
1031
1124
  if (!Array.isArray(output) && "output" in output) logBuildPackageSizeReport({
1032
1125
  output,
1033
1126
  subPackageMap: ctx.scanService?.subPackageMap,
1034
1127
  warningBytes: configService.weappViteConfig.packageSizeWarningBytes
1035
1128
  });
1036
- if (enableAnalyze) analyzeHandle = await startAnalyzeDashboard(await analyzeSubpackages(ctx), {
1037
- watch: true,
1038
- cwd: configService.cwd,
1039
- packageManagerAgent: configService.packageManager.agent
1040
- }) ?? void 0;
1129
+ if (enableAnalyze) {
1130
+ const analyzeResult = await analyzeSubpackages(ctx);
1131
+ analyzeHandle = await startAnalyzeDashboard(analyzeResult, {
1132
+ watch: true,
1133
+ cwd: configService.cwd,
1134
+ packageManagerAgent: configService.packageManager.agent
1135
+ }) ?? void 0;
1136
+ emitDashboardEvents$1(analyzeHandle, [{
1137
+ kind: "build",
1138
+ level: "success",
1139
+ title: "mini build completed",
1140
+ detail: `生产构建已完成,当前 analyze 结果包含 ${analyzeResult.packages.length} 个包。`,
1141
+ durationMs: Date.now() - miniBuildStartedAt,
1142
+ tags: ["build", "mini"]
1143
+ }]);
1144
+ }
1041
1145
  }
1042
1146
  const webConfig = configService.weappWebConfig;
1043
- if (targets.runWeb && webConfig?.enabled) try {
1044
- await webService?.build();
1045
- logger_default.success(`Web 构建完成,输出目录:${colors.green(configService.relativeCwd(webConfig.outDir))}`);
1046
- } catch (error) {
1047
- logger_default.error(error);
1048
- throw error;
1147
+ if (targets.runWeb && webConfig?.enabled) {
1148
+ const webBuildStartedAt = Date.now();
1149
+ try {
1150
+ await webService?.build();
1151
+ logger_default.success(`Web 构建完成,输出目录:${colors.green(configService.relativeCwd(webConfig.outDir))}`);
1152
+ emitDashboardEvents$1(analyzeHandle, [{
1153
+ kind: "build",
1154
+ level: "success",
1155
+ title: "web build completed",
1156
+ detail: `Web 构建已完成,输出目录 ${configService.relativeCwd(webConfig.outDir)}。`,
1157
+ durationMs: Date.now() - webBuildStartedAt,
1158
+ tags: ["build", "web"]
1159
+ }]);
1160
+ } catch (error) {
1161
+ emitDashboardEvents$1(analyzeHandle, [{
1162
+ kind: "diagnostic",
1163
+ level: "error",
1164
+ title: "web build failed",
1165
+ detail: error instanceof Error ? error.message : String(error),
1166
+ durationMs: Date.now() - webBuildStartedAt,
1167
+ tags: ["build", "web"]
1168
+ }]);
1169
+ logger_default.error(error);
1170
+ throw error;
1171
+ }
1049
1172
  }
1050
1173
  if (targets.runMini) logBuildAppFinish(configService, void 0, { skipWeb: !targets.runWeb });
1051
- if (options.open && targets.runMini) await openIde(configService.platform, resolveIdeProjectPath(configService.mpDistRoot));
1174
+ if (options.open && targets.runMini) {
1175
+ emitDashboardEvents$1(analyzeHandle, [{
1176
+ kind: "command",
1177
+ level: "info",
1178
+ title: "opening ide",
1179
+ detail: "构建完成后准备打开 IDE 项目。",
1180
+ tags: ["ide", "open"]
1181
+ }]);
1182
+ await openIde(configService.platform, resolveIdeProjectPath(configService.mpDistRoot));
1183
+ }
1052
1184
  if (analyzeHandle) await analyzeHandle.waitForExit();
1053
1185
  ctx.watcherService?.closeAll();
1054
1186
  terminateStaleSassEmbeddedProcess();
1055
1187
  });
1056
1188
  }
1057
- function terminateStaleSassEmbeddedProcess() {
1058
- const getHandles = process._getActiveHandles;
1059
- const handles = typeof getHandles === "function" ? getHandles() : void 0;
1060
- if (!Array.isArray(handles)) return;
1061
- for (const handle of handles) if (isSassEmbeddedChild(handle)) try {
1062
- handle.kill();
1063
- } catch {}
1064
- }
1065
- function isSassEmbeddedChild(handle) {
1066
- return Boolean(handle && typeof handle === "object" && "kill" in handle && "spawnfile" in handle && typeof handle.spawnfile === "string" && handle.spawnfile?.includes("sass-embedded"));
1067
- }
1068
1189
  //#endregion
1069
1190
  //#region src/cli/commands/close.ts
1070
1191
  function registerCloseCommand(cli) {
@@ -1537,6 +1658,9 @@ function registerPrepareCommand(cli) {
1537
1658
  }
1538
1659
  //#endregion
1539
1660
  //#region src/cli/commands/serve.ts
1661
+ function emitDashboardEvents(handle, events) {
1662
+ handle?.emitRuntimeEvents(events);
1663
+ }
1540
1664
  function resolveWebHost(host) {
1541
1665
  if (host === void 0) return;
1542
1666
  if (typeof host === "boolean") return host;
@@ -1680,6 +1804,7 @@ function registerServeCommand(cli) {
1680
1804
  let analyzeHandle;
1681
1805
  let analyzeRunId = 0;
1682
1806
  const runAnalyze = async () => {
1807
+ const startedAt = Date.now();
1683
1808
  try {
1684
1809
  const result = await analyzeSubpackages(await createCompilerContext({
1685
1810
  key: `serve-ui-analyze:${process.pid}:${++analyzeRunId}`,
@@ -1692,48 +1817,126 @@ function registerServeCommand(cli) {
1692
1817
  projectConfigPath: options.projectConfig,
1693
1818
  syncSupportFiles: false
1694
1819
  }));
1695
- if (hasAnalyzeData(result)) return result;
1820
+ if (hasAnalyzeData(result)) return {
1821
+ result,
1822
+ durationMs: Date.now() - startedAt,
1823
+ mode: "full"
1824
+ };
1696
1825
  } catch (error) {
1697
1826
  const message = error instanceof Error ? error.message : String(error);
1698
1827
  logger_default.warn(`[ui] 完整分析失败,已回退到 dist 文件扫描:${message}`);
1828
+ return {
1829
+ result: await analyzeUiFallback(ctx),
1830
+ durationMs: Date.now() - startedAt,
1831
+ mode: "fallback",
1832
+ fallbackReason: message
1833
+ };
1699
1834
  }
1700
- return analyzeUiFallback(ctx);
1835
+ return {
1836
+ result: await analyzeUiFallback(ctx),
1837
+ durationMs: Date.now() - startedAt,
1838
+ mode: "fallback",
1839
+ fallbackReason: "完整分析结果为空,已回退到 dist 文件扫描。"
1840
+ };
1701
1841
  };
1702
- const triggerAnalyzeUpdate = async () => {
1842
+ const triggerAnalyzeUpdate = async (reason = "watch") => {
1703
1843
  if (!analyzeHandle) return;
1844
+ emitDashboardEvents(analyzeHandle, [{
1845
+ kind: reason === "watch" ? "hmr" : "build",
1846
+ level: "info",
1847
+ title: reason === "watch" ? "analyze refresh started" : "initial analyze started",
1848
+ detail: reason === "watch" ? "检测到新的构建结束事件,开始刷新 analyze 面板。" : "开发态 UI 已启动,开始生成第一份 analyze 结果。",
1849
+ tags: reason === "watch" ? ["watch", "analyze"] : ["initial", "analyze"]
1850
+ }]);
1704
1851
  const next = await runAnalyze();
1705
- await analyzeHandle.update(next);
1852
+ if (next.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
1853
+ kind: "diagnostic",
1854
+ level: "warning",
1855
+ title: "analyze fallback enabled",
1856
+ detail: next.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
1857
+ durationMs: next.durationMs,
1858
+ tags: ["analyze", "fallback"]
1859
+ }]);
1860
+ await analyzeHandle.update(next.result);
1861
+ emitDashboardEvents(analyzeHandle, [{
1862
+ kind: next.mode === "fallback" ? "diagnostic" : "build",
1863
+ level: next.mode === "fallback" ? "warning" : "success",
1864
+ title: reason === "watch" ? "analyze refresh completed" : "initial analyze completed",
1865
+ detail: next.mode === "fallback" ? `analyze 已回退到 dist 扫描,当前包含 ${next.result.packages.length} 个包。` : `analyze 已刷新完成,当前包含 ${next.result.packages.length} 个包与 ${next.result.modules.length} 个模块。`,
1866
+ durationMs: next.durationMs,
1867
+ tags: next.mode === "fallback" ? ["analyze", "fallback"] : ["analyze", reason === "watch" ? "refresh" : "initial"]
1868
+ }]);
1706
1869
  };
1707
1870
  if (targets.runMini) {
1708
1871
  const buildResult = await buildService.build(options);
1709
1872
  if (enableAnalyze) {
1710
- analyzeHandle = await startAnalyzeDashboard(await runAnalyze(), {
1873
+ const initialAnalyze = await runAnalyze();
1874
+ analyzeHandle = await startAnalyzeDashboard(initialAnalyze.result, {
1711
1875
  watch: true,
1712
1876
  cwd: configService.cwd,
1713
1877
  packageManagerAgent: configService.packageManager.agent,
1714
1878
  silentStartupLog: true
1715
1879
  }) ?? void 0;
1880
+ emitDashboardEvents(analyzeHandle, [{
1881
+ kind: "command",
1882
+ level: "success",
1883
+ title: "dev ui session ready",
1884
+ detail: `开发态分析面板已启动,当前包含 ${initialAnalyze.result.packages.length} 个包。`,
1885
+ durationMs: initialAnalyze.durationMs,
1886
+ tags: ["dev", "ui"]
1887
+ }]);
1888
+ if (initialAnalyze.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
1889
+ kind: "diagnostic",
1890
+ level: "warning",
1891
+ title: "initial analyze fallback enabled",
1892
+ detail: initialAnalyze.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
1893
+ durationMs: initialAnalyze.durationMs,
1894
+ tags: [
1895
+ "analyze",
1896
+ "fallback",
1897
+ "initial"
1898
+ ]
1899
+ }]);
1716
1900
  let updating = false;
1717
1901
  if (analyzeHandle && buildResult && typeof buildResult.on === "function") buildResult.on("event", (event) => {
1718
1902
  if (event.code !== "END" || updating) return;
1719
1903
  updating = true;
1720
- triggerAnalyzeUpdate().finally(() => {
1904
+ triggerAnalyzeUpdate("watch").finally(() => {
1721
1905
  updating = false;
1722
1906
  });
1723
1907
  });
1724
1908
  if (analyzeHandle) {
1725
1909
  updating = true;
1726
- await triggerAnalyzeUpdate();
1910
+ await triggerAnalyzeUpdate("initial");
1727
1911
  updating = false;
1728
1912
  }
1729
1913
  }
1730
1914
  }
1731
1915
  let webServer;
1732
- if (targets.runWeb) try {
1733
- webServer = await webService?.startDevServer();
1734
- } catch (error) {
1735
- logger_default.error(error);
1736
- throw error;
1916
+ if (targets.runWeb) {
1917
+ const webServerStartedAt = Date.now();
1918
+ try {
1919
+ webServer = await webService?.startDevServer();
1920
+ emitDashboardEvents(analyzeHandle, [{
1921
+ kind: "system",
1922
+ level: "success",
1923
+ title: "web dev server started",
1924
+ detail: "Web 开发服务器已启动,可与小程序调试 UI 并行工作。",
1925
+ durationMs: Date.now() - webServerStartedAt,
1926
+ tags: ["dev", "web"]
1927
+ }]);
1928
+ } catch (error) {
1929
+ emitDashboardEvents(analyzeHandle, [{
1930
+ kind: "diagnostic",
1931
+ level: "error",
1932
+ title: "web dev server failed",
1933
+ detail: error instanceof Error ? error.message : String(error),
1934
+ durationMs: Date.now() - webServerStartedAt,
1935
+ tags: ["dev", "web"]
1936
+ }]);
1937
+ logger_default.error(error);
1938
+ throw error;
1939
+ }
1737
1940
  }
1738
1941
  if (targets.runMini) logBuildAppFinish(configService, webServer, {
1739
1942
  skipWeb: !targets.runWeb,
@@ -1741,6 +1944,13 @@ function registerServeCommand(cli) {
1741
1944
  });
1742
1945
  else if (targets.runWeb) logBuildAppFinish(configService, webServer, { skipMini: true });
1743
1946
  if (options.open && targets.runMini) {
1947
+ emitDashboardEvents(analyzeHandle, [{
1948
+ kind: "command",
1949
+ level: "info",
1950
+ title: "opening ide",
1951
+ detail: "开发服务已就绪,准备打开 IDE 项目。",
1952
+ tags: ["ide", "open"]
1953
+ }]);
1744
1954
  if (!await maybeStartForwardConsole({
1745
1955
  platform: configService.platform,
1746
1956
  mpDistRoot: configService.mpDistRoot,