UNPKG

weapp-vite

Version:

weapp-vite 一个现代化的小程序打包工具

6,158 lines 252 kB
import { A as getDefaultIdeProjectRoot, C as collectPreloadPages, D as loadViteConfigFile, E as suggestPreloadRules, M as isPathInside, O as createCjsConfigLoadError, S as getProjectConfigFileName, T as resolvePreloadPackageIdentifier, _ as SHARED_CHUNK_VIRTUAL_PREFIX, a as createGlassEaselAnalyzeResult, b as resolveWeappConfigFile, f as getBackendForCapability, g as createSharedBuildConfig, h as syncManagedTsconfigBootstrapFiles, j as shouldPassPlatformArgToIdeOpen, k as parseCommentJson, m as syncProjectSupportFiles, o as formatBytes, p as resolveBackendExecution, t as createCompilerContext, v as resolveHmrProfileJsonPath, w as findPreloadRuleKey, x as checkRuntime } from "./createContext-DDhnklA6.mjs";
import { f as VERSION, i as findJsEntry, o as findTemplateEntry, s as findVueEntry } from "./file-B34zY0ET.mjs";
import { r as logger_default, t as colors } from "./logger-mt4mSTqV.mjs";
import { c as startWeappViteMcpServer, l as detectAiDevelopmentEnvironment, s as resolveWeappMcpConfig } from "./mcp-BG6TliEg.mjs";
import { createRequire } from "node:module";
import fs from "node:fs";
import path, { posix } from "pathe";
import path$1 from "node:path";
import { defu } from "@weapp-core/shared";
import { fs as fs$1 } from "@weapp-core/shared/fs";
import process from "node:process";
import { parse } from "vue/compiler-sfc";
import fs$2, { mkdir, writeFile } from "node:fs/promises";
import { build, createServer } from "vite";
import os from "node:os";
import { execFile, spawn } from "node:child_process";
import { Buffer } from "node:buffer";
import { cac } from "cac";
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";
import { promisify } from "node:util";
import { brotliCompressSync, gzipSync } from "node:zlib";
import { resolveCommand } from "package-manager-detector/commands";
import { generateJs, generateJson, generateWxml, generateWxss } from "@weapp-core/schematics";
import { determineAgent } from "@vercel/detect-agent";
import { initConfig } from "@weapp-core/init";
import { createInterface } from "node:readline/promises";
import { clearTimeout as clearTimeout$1, setTimeout as setTimeout$1 } from "node:timers";
import net from "node:net";
//#region src/cli/runtime.ts
function logRuntimeTarget(targets, options = {}) {
	if (options.silent) return;
	if (targets.label === "config") {
		const resolvedPlatform = targets.platform ?? options.resolvedConfigPlatform;
		if (resolvedPlatform) {
			logger_default.info(`目标平台:${colors.green(resolvedPlatform)}`);
			return;
		}
		logger_default.info(`目标平台:使用配置文件中的 ${colors.bold(colors.green("weapp.platform"))}`);
		return;
	}
	logger_default.info(`目标平台:${colors.green(targets.label)}`);
}
function resolveRuntimeTargets(options) {
	const rawPlatform = typeof options.platform === "string" ? options.platform : typeof options.p === "string" ? options.p : void 0;
	const execution = resolveBackendExecution(rawPlatform, { warn: (message) => logger_default.warn(message) });
	const miniBackend = execution.get("miniprogram");
	return {
		...execution,
		platform: miniBackend?.platform,
		rawPlatform
	};
}
function createInlineConfig(execution, options = {}) {
	const configs = execution.entries.map((entry) => entry.driver.createInlineConfig({
		execution,
		platform: entry.platform,
		scope: options.scope,
		host: options.host
	})).filter((config) => Boolean(config));
	if (configs.length === 0) return options.inlineConfig;
	const merged = configs.slice(1).reduce((merged, config) => defu(merged, config), configs[0]);
	return options.inlineConfig ? defu(options.inlineConfig, merged) : merged;
}
//#endregion
//#region src/cli/openIde/execute.ts
function readArgOption(argv, ...names) {
	for (let index = 0; index < argv.length; index += 1) {
		const current = argv[index];
		if (!names.includes(current)) continue;
		const next = argv[index + 1];
		if (typeof next === "string" && !next.startsWith("-")) return next;
	}
}
async function tryExecuteWechatIdeCliCommandByAutomator(argv, projectPath, options = {}) {
	if (!projectPath) return false;
	const command = argv[0];
	if (!command) return false;
	if (command === "compile") {
		await compileWechatIdeByAutomator({
			preserveProjectRoot: options.preserveProjectRoot,
			projectPath
		});
		return true;
	}
	if (command === "cache") {
		const cleanType = readArgOption(argv, "--clean", "-c");
		if (cleanType !== "compile" && cleanType !== "all") return false;
		await clearWechatIdeCacheByAutomator({
			clean: cleanType,
			preserveProjectRoot: options.preserveProjectRoot,
			projectPath
		});
		return true;
	}
	return false;
}
async function tryExecuteWechatIdeCliCommandByHttp(argv, projectPath, engineBuildFallbackToCli = false) {
	const command = argv[0];
	if (!command) return false;
	if (command === "reset-fileutils") {
		if (!projectPath) return false;
		await resetWechatIdeFileUtilsByHttp(projectPath);
		return true;
	}
	if (command === "engine" && argv[1] === "build") {
		const engineProjectPath = argv[2] || projectPath;
		if (!engineProjectPath) return false;
		await runWechatIdeEngineBuild(engineProjectPath, {
			fallbackToCli: engineBuildFallbackToCli,
			logPath: readArgOption(argv, "--logPath", "-l")
		});
		return true;
	}
	return false;
}
async function tryExecuteWechatIdeCliCommandByHelper(argv) {
	const command = argv[0];
	if (!command) return false;
	if (command === "close") {
		await closeWechatIdeProject();
		return true;
	}
	if (command === "quit") {
		await quitWechatIde();
		return true;
	}
	if (command === "cache") {
		const cleanType = readArgOption(argv, "--clean", "-c");
		if (!cleanType) return false;
		await clearWechatIdeCache({ clean: cleanType });
		return true;
	}
	return false;
}
/**
* @description 统一执行 weapp-ide-cli 命令,并在登录失效时复用同一套重试交互。
*/
async function executeWechatIdeCliCommand(argv, options = {}) {
	const { automatorMode = "prefer", cancelLevel = "warn", engineBuildFallbackToCli = false, httpMode = "prefer", promptOpenIdeLogin = true, onNonLoginError, onRetry, projectPath, preserveProjectRoot } = options;
	await runWithSuspendedSharedInput(async () => {
		if (httpMode !== "skip") try {
			if (await tryExecuteWechatIdeCliCommandByHttp(argv, projectPath, engineBuildFallbackToCli)) return;
		} catch (error) {
			if (httpMode === "require" || isWechatIdeEngineBuildEndpointMissingError(error)) throw error;
		}
		if (automatorMode !== "skip") try {
			if (await tryExecuteWechatIdeCliCommandByAutomator(argv, projectPath, { preserveProjectRoot })) return;
		} catch (error) {
			if (automatorMode === "require") throw error;
		}
		try {
			if (await tryExecuteWechatIdeCliCommandByHelper(argv)) return;
		} catch (error) {
			if (isWechatIdeLoginRequiredExitError(error)) throw error;
			if (onNonLoginError) {
				onNonLoginError(error);
				return;
			}
			throw error;
		}
		await runRetryableCommand({
			createCancelError: (error) => createWechatIdeLoginRequiredExitError(error, "cancelled"),
			execute: async () => {
				try {
					await parse$1(argv);
					return null;
				} catch (error) {
					if (isWechatIdeLoginRequiredExitError(error)) throw error;
					if (!isWechatIdeLoginRequiredError(error)) {
						if (onNonLoginError) {
							onNonLoginError(error);
							return null;
						}
						throw error;
					}
					return error;
				}
			},
			isRetryableResult: (result) => result !== null,
			onCancel: () => {},
			onRetry: () => {
				onRetry?.();
			},
			promptRetry: async (error) => await promptWechatIdeLoginRetry({
				cancelLevel,
				error,
				logger: logger_default,
				promptOpenIdeLogin
			}),
			shouldRetry: (action) => action === "retry"
		});
	});
}
//#endregion
//#region src/cli/openIde/close.ts
const execFileAsync = promisify(execFile);
async function closeIdeByAppleScript() {
	if (process.platform !== "darwin") return false;
	const appName = process.env.WEAPP_DEVTOOLS_APP_NAME || "wechatwebdevtools";
	try {
		await execFileAsync("osascript", ["-e", `tell application "${appName}" to quit`]);
		return true;
	} catch {
		return false;
	}
}
async function closeIdeByProcessKill(cliPath) {
	if (!cliPath) return false;
	const appContentsRoot = cliPath.includes(".app/") ? cliPath.slice(0, cliPath.indexOf(".app/") + 4) : path.dirname(path.dirname(cliPath));
	try {
		await execFileAsync("pkill", ["-f", appContentsRoot]);
		return true;
	} catch {
		return false;
	}
}
/**
* @description 关闭微信开发者工具,并在 CLI 不可用时回退到系统级关闭。
*/
async function closeIde$1() {
	const config = await getConfig();
	const cliPath = config.cliPath?.trim() ? config.cliPath : null;
	try {
		await closeWechatIdeProject();
		return true;
	} catch (error) {
		if (isWechatIdeLoginRequiredError(error)) try {
			await executeWechatIdeCliCommand(["close"], {
				cancelLevel: "warn",
				onNonLoginError: (retryError) => logger_default.error(retryError),
				onRetry: () => logger_default.info("正在重试连接微信开发者工具...")
			});
			return true;
		} catch (retryError) {
			logger_default.error(retryError);
		}
		else {
			logger_default.warn("微信开发者工具 CLI close 执行失败,尝试回退为系统级关闭。");
			logger_default.error(error);
		}
		if (await closeIdeByAppleScript()) {
			logger_default.info("已回退为系统级关闭微信开发者工具。");
			return true;
		}
		if (await closeIdeByProcessKill(cliPath)) {
			logger_default.info("已回退为进程级关闭微信开发者工具。");
			return true;
		}
		return false;
	}
}
//#endregion
//#region src/cli/openIde/diagnostics.ts
/**
* @description 将 automator 启动错误归类为用户可执行的恢复提示。
*/
function formatWechatIdeAutomatorFailure(error) {
	const message = error instanceof Error ? error.message : String(error);
	if (/需要重新登录|need\s+re-?login|code\s*[:=]\s*10/i.test(message)) return "登录状态失效,请先登录微信开发者工具。";
	if (/service port|http port|ECONNREFUSED|listen EPERM|EACCES/i.test(message)) return "服务端口不可用,请在设置 -> 安全设置中开启服务端口。";
	if (/protocol method|protocol timeout|automator.*timeout|Wait timed out|websocket|ws:\/\//i.test(message)) return "自动化 websocket 或协议尚未就绪,已保留普通 CLI 打开的项目。";
	if (/cliPath|not found|spawn/i.test(message)) return "微信开发者工具 CLI 路径不可用,请执行 `wv ide doctor` 检查配置。";
	return "当前 DevTools 自动化能力不可用,已保留普通 CLI 打开的项目。";
}
/**
* @description 输出微信开发者工具打开后的自助恢复步骤。
*/
function logWechatIdeRecoveryHint(options) {
	const lines = [
		`微信开发者工具打开后状态可能不稳定:${options.reason}`,
		"可按下面顺序恢复:",
		"1. 在微信开发者工具中确认:设置 -> 安全设置 -> 服务端口已开启。",
		"2. 仅当项目索引刷新失败时,默认会自动关闭并重开一次当前目标项目;如需跳过,传入 `--no-open-recovery` 或设置 `WEAPP_VITE_DISABLE_IDE_OPEN_RECOVERY=1`。",
		"3. 如果仍然回到项目选择页,手动导入 project.config.json 所在目录,并关闭多余的微信开发者工具窗口后重试。",
		"4. 需要查看底层错误时,设置 `WEAPP_VITE_DEBUG_AUTOMATOR_OPEN=1` 后重试。"
	];
	if (options.projectPath) lines.push(`当前目标项目目录:${options.projectPath}`);
	logger_default.warn(lines.join("\n"));
}
/**
* @description 输出服务端口关闭时的定向恢复提示。
*/
function logWechatIdeServicePortDisabledHint(projectPath) {
	const lines = [
		"检测到微信开发者工具服务端口当前处于关闭状态,已保留用户设置并回退到普通 open 流程。",
		"自动刷新、截图、MCP 和 IDE 联动能力需要服务端口。",
		"请在微信开发者工具中打开:设置 -> 安全设置 -> 服务端口,然后重新执行当前 dev/open 命令。"
	];
	if (projectPath) lines.push(`当前目标项目目录:${projectPath}`);
	logger_default.warn(lines.join("\n"));
}
//#endregion
//#region src/cli/openIde/reuse.ts
const OPENED_PROJECT_HEALTH_CHECK_TIMEOUT = 3e3;
const OPEN_AUTOMATOR_TIMEOUT = 12e4;
function formatReuseOpenedWechatIdePrompt() {
	return `目标项目已在微信开发者工具中打开,已跳过重复打开。按 ${RETRY_CONFIRM_KEYS.map((key) => colors.bold(colors.green(key))).join(" / ")} 关闭当前窗口后重新打开。`;
}
function disconnectMiniProgram(miniProgram) {
	miniProgram.disconnect();
}
function withTimeout(task, timeoutMs) {
	let timer;
	const timeout = new Promise((_, reject) => {
		timer = setTimeout(() => {
			reject(/* @__PURE__ */ new Error(`opened automator health check timed out after ${timeoutMs}ms`));
		}, timeoutMs);
	});
	return Promise.race([task, timeout]).finally(() => {
		if (timer) clearTimeout(timer);
	});
}
async function verifyOpenedProjectHealth(miniProgram) {
	if (typeof miniProgram.screenshot !== "function") return;
	await withTimeout(miniProgram.screenshot({ timeout: OPENED_PROJECT_HEALTH_CHECK_TIMEOUT }), OPENED_PROJECT_HEALTH_CHECK_TIMEOUT);
}
async function openWechatIdeByAutomator(projectPath, options = {}) {
	disconnectMiniProgram(await launchAutomator({
		persistAsDefaultSession: true,
		preserveProjectRoot: options.preserveProjectRoot ?? true,
		projectPath,
		port: resolveProjectAutomatorPort(projectPath),
		timeout: OPEN_AUTOMATOR_TIMEOUT,
		trustProject: options.trustProject !== false
	}));
}
async function connectOpenedProject(projectPath) {
	let miniProgram;
	try {
		miniProgram = await connectOpenedAutomator({
			projectPath,
			port: resolveProjectAutomatorPort(projectPath),
			timeout: 3e3
		});
	} catch {
		return { status: "not-connected" };
	}
	try {
		await verifyOpenedProjectHealth(miniProgram);
		return {
			status: "connected",
			miniProgram
		};
	} catch {
		disconnectMiniProgram(miniProgram);
		return { status: "unhealthy" };
	}
}
/**
* @description 若当前项目已在微信开发者工具中打开且自动化可连通,则直接复用现有会话,避免重复拉起 IDE。
*/
async function tryReuseOpenedWechatIde(projectPath, closeIde, options = {}) {
	const connection = await connectOpenedProject(projectPath);
	if (connection.status === "unhealthy") return {
		reopened: false,
		reused: false,
		unhealthy: true
	};
	if (connection.status === "not-connected") return null;
	const { miniProgram } = connection;
	disconnectMiniProgram(miniProgram);
	if (options.promptReopen === false) {
		logger_default.info("目标项目已在微信开发者工具中打开,已跳过重复打开。");
		return {
			reopened: false,
			reused: true
		};
	}
	logger_default.info(formatReuseOpenedWechatIdePrompt());
	if (await promptRetryKeypress({ logger: logger_default }) !== "retry") return {
		reopened: false,
		reused: true
	};
	logger_default.info(colors.bold(colors.green("正在关闭当前已打开项目,并重新拉起微信开发者工具...")));
	if (!await closeIde()) logger_default.warn("关闭当前微信开发者工具失败,仍继续尝试重新打开目标项目。");
	await openWechatIdeByAutomator(projectPath, {
		preserveProjectRoot: options.preserveProjectRoot,
		trustProject: options.trustProject
	});
	return {
		reopened: true,
		reused: false
	};
}
/**
* @description 对已打开的目标项目执行强制重开,以刷新最新构建产物。
*/
async function reopenOpenedWechatIde(projectPath, closeIde, options = {}) {
	const connection = await connectOpenedProject(projectPath);
	if (connection.status === "connected") {
		disconnectMiniProgram(connection.miniProgram);
		logger_default.info("目标项目已在微信开发者工具中打开,当前命令将主动重开以刷新最新构建产物。");
	} else if (connection.status === "unhealthy") logger_default.info("目标项目的 automator 会话未通过健康检查,当前命令将关闭窗口并重新拉起。");
	else logger_default.info("未检测到可复用的 automator 会话,当前命令将关闭现有窗口并重新拉起目标项目。");
	if (!await (connection.status === "connected" ? closeIde : options.restartIde ?? closeIde)()) logger_default.warn("关闭当前微信开发者工具失败,仍继续尝试重新打开目标项目。");
	await openWechatIdeByAutomator(projectPath, options);
	return true;
}
//#endregion
//#region src/cli/openIde/index.ts
function shouldLogAutomatorFallbackError() {
	const flag = process.env.WEAPP_VITE_DEBUG_AUTOMATOR_OPEN;
	return flag === "1" || flag === "true";
}
const PREPARE_AUTOMATOR_SESSION_TIMEOUT = 8e3;
const AUTOMATOR_RECOVERY_QUIT_SETTLE_MS = 5e3;
const RESET_FILEUTILS_ENV = "WEAPP_VITE_RESET_IDE_FILEUTILS";
function sleep$1(ms) {
	return new Promise((resolve) => setTimeout(resolve, ms));
}
function readProjectConfigObject(projectPath) {
	try {
		const raw = fs.readFileSync(path.join(projectPath, "project.config.json"), "utf8");
		const value = JSON.parse(raw);
		return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
	} catch {
		return;
	}
}
function shouldUseAutomatorProjectWrapper(projectPath) {
	const projectConfig = readProjectConfigObject(projectPath);
	return projectConfig?.compileType === "plugin" && typeof projectConfig.miniprogramRoot === "string" && projectConfig.miniprogramRoot.trim().length > 0;
}
function isWechatIdeOpenRecoveryDisabled(options) {
	if (options.openRecovery === false) return true;
	const flag = process.env.WEAPP_VITE_DISABLE_IDE_OPEN_RECOVERY;
	return flag === "1" || flag === "true";
}
function shouldResetWechatIdeFileUtils() {
	const flag = process.env[RESET_FILEUTILS_ENV];
	return flag === "1" || flag === "true";
}
/**
* @description 执行 IDE 打开流程,并在登录失效时允许按键重试。
*/
async function runWechatIdeOpenWithRetry(argv) {
	await executeWechatIdeCliCommand(argv, {
		cancelLevel: "warn",
		onNonLoginError: (error) => logger_default.error(error),
		onRetry: () => {
			logger_default.info(colors.bold(colors.green("正在重试连接微信开发者工具...")));
		},
		promptOpenIdeLogin: true
	});
}
/**
* @description 根据 mpDistRoot 推导 IDE 项目目录(目录内应包含 project/mini 配置)
*/
function resolveIdeProjectPath(mpDistRoot) {
	if (!mpDistRoot || !mpDistRoot.trim()) return;
	const parent = path.dirname(mpDistRoot);
	if (!parent || parent === "." || parent === "/") return;
	return parent;
}
/**
* @description 结合 mpDistRoot 与配置根目录解析最终 IDE 项目目录。
*/
function resolveIdeProjectRoot(mpDistRoot, cwd) {
	return resolveIdeProjectPath(mpDistRoot) ?? cwd;
}
async function closeIde() {
	return await closeIde$1();
}
async function restartWechatIdeForAutomatorRecovery() {
	try {
		await quitWechatIde();
		await sleep$1(AUTOMATOR_RECOVERY_QUIT_SETTLE_MS);
		return true;
	} catch {
		return await closeIde();
	}
}
async function tryOpenWechatIdeByAutomator(projectPath, options) {
	const preserveProjectRoot = !shouldUseAutomatorProjectWrapper(projectPath);
	if (options.reuseOpenedProject === false) {
		if (await reopenOpenedWechatIde(projectPath, closeIde, {
			preserveProjectRoot,
			restartIde: restartWechatIdeForAutomatorRecovery,
			trustProject: options.trustProject
		})) return "reopened";
	}
	const reuseResult = await tryReuseOpenedWechatIde(projectPath, closeIde, {
		preserveProjectRoot,
		promptReopen: options.reuseOpenedProject !== true,
		trustProject: options.trustProject
	});
	if (reuseResult?.reused) return "reused";
	if (reuseResult?.reopened) return "reopened";
	if (reuseResult?.unhealthy) return "unhealthy";
	await openWechatIdeByAutomator(projectPath, {
		preserveProjectRoot,
		trustProject: options.trustProject
	});
	return "opened";
}
/**
* @description 打开后主动刷新微信开发者工具的项目索引,避免模拟器沿用过期 app 配置。
*/
function appendLoginRetryArgv(argv, options) {
	if (options.nonInteractive) argv.push("--non-interactive");
	if (options.loginRetry) argv.push("--login-retry", options.loginRetry);
	if (options.loginRetryTimeout) argv.push("--login-retry-timeout", options.loginRetryTimeout);
	return argv;
}
function createIdeOpenArgv(platform, projectPath, options = {}) {
	const argv = ["open", "-p"];
	if (projectPath) argv.push(projectPath);
	if (platform === "weapp" && options.trustProject !== false) argv.push("--trust-project");
	if (platform && shouldPassPlatformArgToIdeOpen(platform)) argv.push("--platform", platform);
	if (options.nonInteractive) argv.push("--non-interactive");
	if (options.loginRetry) argv.push("--login-retry", options.loginRetry);
	if (options.loginRetryTimeout) argv.push("--login-retry-timeout", options.loginRetryTimeout);
	return argv;
}
async function prepareOpenedWechatIdeAutomatorSession(projectPath, options) {
	const preserveProjectRoot = !shouldUseAutomatorProjectWrapper(projectPath);
	try {
		(await launchAutomator({
			persistAsDefaultSession: true,
			preserveProjectRoot,
			projectPath,
			port: resolveProjectAutomatorPort(projectPath),
			timeout: PREPARE_AUTOMATOR_SESSION_TIMEOUT,
			trustProject: options.trustProject !== false
		})).disconnect?.();
		return { ok: true };
	} catch (error) {
		logger_default.warn("准备当前项目的微信开发者工具自动化会话失败,截图、MCP 或 IDE 联动命令首次运行时将重新连接。");
		logWechatIdeRecoveryHint({
			projectPath,
			reason: "无法建立当前项目的自动化会话,常见原因是 DevTools 服务端口未就绪、窗口停留在项目选择页,或存在残留 DevTools 会话。"
		});
		if (shouldLogAutomatorFallbackError()) logger_default.error(error);
		return {
			ok: false,
			reason: "automator-session-failed",
			error
		};
	}
}
async function connectOpenedWechatIdeAutomatorSession(projectPath) {
	try {
		(await connectOpenedAutomator({
			projectPath,
			port: resolveProjectAutomatorPort(projectPath),
			timeout: PREPARE_AUTOMATOR_SESSION_TIMEOUT
		})).disconnect?.();
		return { ok: true };
	} catch (error) {
		logger_default.warn("连接当前项目的微信开发者工具自动化会话失败,截图、MCP 或 IDE 联动命令首次运行时将重新连接。");
		logWechatIdeRecoveryHint({
			projectPath,
			reason: "当前项目已完成打开流程,但尚未连接到可复用的自动化会话。"
		});
		if (shouldLogAutomatorFallbackError()) logger_default.error(error);
		return {
			ok: false,
			reason: "automator-session-failed",
			error
		};
	}
}
async function prepareWechatIdeAutomatorSession(projectPath, options) {
	if (options.prepareAutomatorSession === "connect-opened") return await connectOpenedWechatIdeAutomatorSession(projectPath);
	return await prepareOpenedWechatIdeAutomatorSession(projectPath, options);
}
async function stabilizeOpenedWechatIdeProject(projectPath, servicePortEnabled, options = {}) {
	if (servicePortEnabled === false) return {
		ok: false,
		reason: "service-port-disabled"
	};
	try {
		if (shouldResetWechatIdeFileUtils()) await executeWechatIdeCliCommand(appendLoginRetryArgv([
			"reset-fileutils",
			"-p",
			projectPath
		], options), {
			automatorMode: options.useAutomatorOpen === false ? "skip" : "prefer",
			httpMode: "require",
			onNonLoginError: (error) => logger_default.error(error),
			preserveProjectRoot: options.useAutomatorOpen === false,
			projectPath
		});
		try {
			await executeWechatIdeCliCommand(appendLoginRetryArgv([
				"engine",
				"build",
				projectPath
			], options), {
				automatorMode: options.useAutomatorOpen === false ? "skip" : "prefer",
				engineBuildFallbackToCli: true,
				httpMode: "prefer",
				onNonLoginError: (error) => logger_default.error(error),
				preserveProjectRoot: options.useAutomatorOpen === false,
				projectPath
			});
		} catch (error) {
			if (!isWechatIdeEngineBuildEndpointMissingError(error)) throw error;
			logger_default.warn("当前微信开发者工具不支持自动 engine build 刷新,已跳过该步骤;如模拟器显示旧状态,可在开发者工具内手动编译。");
		}
		if (options.useAutomatorOpen !== false && options.skipAutomatorCompile !== true) try {
			await executeWechatIdeCliCommand(appendLoginRetryArgv(["compile"], options), {
				automatorMode: "require",
				httpMode: "skip",
				preserveProjectRoot: !shouldUseAutomatorProjectWrapper(projectPath),
				projectPath
			});
		} catch (error) {
			if (shouldLogAutomatorFallbackError()) logger_default.error(error);
		}
		return { ok: true };
	} catch (error) {
		if (isWechatIdeLoginRequiredExitError(error)) throw error;
		logger_default.warn("刷新微信开发者工具项目索引失败,已保留当前打开状态;如模拟器仍显示旧状态,可手动刷新一次。");
		logWechatIdeRecoveryHint({
			projectPath,
			reason: "打开项目后的文件索引刷新失败,DevTools 可能仍在使用旧项目状态或内部服务未就绪。"
		});
		if (shouldLogAutomatorFallbackError()) logger_default.error(error);
		return {
			ok: false,
			reason: "index-refresh-failed",
			error
		};
	}
}
async function verifyOpenedWechatIdeProject(projectPath, servicePortEnabled, options) {
	const stabilizeResult = await stabilizeOpenedWechatIdeProject(projectPath, servicePortEnabled, options);
	if (!stabilizeResult.ok) return stabilizeResult;
	if (options.useAutomatorOpen === false && servicePortEnabled !== false) return await connectOpenedWechatIdeAutomatorSession(projectPath);
	return stabilizeResult;
}
function formatWechatIdeOpenHealthReason(result) {
	if (result.reason === "index-refresh-failed") return "项目索引刷新失败";
	return "服务端口未开启";
}
async function recoverOpenedWechatIdeProject(platform, projectPath, servicePortEnabled, options, failedResult) {
	if (failedResult.reason === "service-port-disabled") return failedResult;
	if (failedResult.reason === "automator-session-failed") {
		logger_default.warn("已跳过微信开发者工具自动恢复;自动化会话预热失败不影响当前项目打开,截图、MCP 或 IDE 联动命令首次运行时会重新连接。");
		return failedResult;
	}
	if (isWechatIdeOpenRecoveryDisabled(options)) {
		logger_default.warn("已跳过微信开发者工具自动恢复;请按上方提示手动关闭并重新打开目标项目。");
		return failedResult;
	}
	logger_default.info(`检测到微信开发者工具打开后状态不稳定(${formatWechatIdeOpenHealthReason(failedResult)}),正在自动关闭并重新打开目标项目...`);
	if (!await closeIde()) logger_default.warn("自动恢复时关闭当前微信开发者工具失败,仍继续尝试重新打开目标项目。");
	await runWechatIdeOpenWithRetry(createIdeOpenArgv(platform, projectPath, options));
	const recoveredResult = await verifyOpenedWechatIdeProject(projectPath, servicePortEnabled, options);
	if (recoveredResult.ok) logger_default.info("微信开发者工具已完成自动恢复。");
	else logger_default.warn("微信开发者工具自动恢复未完成;可设置 `WEAPP_VITE_DISABLE_IDE_OPEN_RECOVERY=1` 或传入 `--no-open-recovery` 跳过自动关闭重开,并按提示手动处理。");
	return recoveredResult;
}
async function verifyAndRecoverOpenedWechatIdeProject(platform, projectPath, servicePortEnabled, options) {
	const healthResult = await verifyOpenedWechatIdeProject(projectPath, servicePortEnabled, options);
	if (!healthResult.ok) await recoverOpenedWechatIdeProject(platform, projectPath, servicePortEnabled, options, healthResult);
}
async function openIde(platform, projectPath, options = {}) {
	let bootstrapResult;
	const openStrategy = options.openStrategy ?? (options.useAutomatorOpen === true ? "automator" : "cli");
	const useAutomatorOpen = openStrategy === "automator";
	const normalizedOptions = {
		...options,
		openStrategy,
		useAutomatorOpen
	};
	if (platform === "weapp" && projectPath) try {
		bootstrapResult = await bootstrapWechatDevtoolsSettings({
			projectPath,
			trustProject: normalizedOptions.trustProject
		});
	} catch (error) {
		logger_default.warn("检测微信开发者工具服务端口或写入项目信任状态失败,继续执行 open 流程。");
		logger_default.error(error);
	}
	if (platform === "weapp" && projectPath && bootstrapResult?.servicePortEnabled === false) logWechatIdeServicePortDisabledHint(projectPath);
	if (platform === "weapp" && projectPath && bootstrapResult?.servicePortEnabled !== false && useAutomatorOpen) try {
		const openResult = await tryOpenWechatIdeByAutomator(projectPath, normalizedOptions);
		if (openResult === "reused") return;
		if (openResult === "unhealthy") {
			logger_default.warn("已连接到目标微信开发者工具项目,但协议健康检查暂未就绪;已保留当前窗口并跳过重复启动。");
			return;
		} else if (openResult) {
			if (!normalizedOptions.skipPostOpenHealthCheck) await verifyAndRecoverOpenedWechatIdeProject(platform, projectPath, bootstrapResult?.servicePortEnabled, normalizedOptions);
			return;
		}
	} catch (error) {
		if (isAutomatorLoginError(error)) {
			logger_default.error("检测到微信开发者工具登录状态失效,请先登录后重试。");
			logger_default.warn(formatAutomatorLoginError(error));
		}
		logger_default.warn(`通过 automator 启动微信开发者工具失败,回退到普通 open 流程:${formatWechatIdeAutomatorFailure(error)}`);
		if (shouldLogAutomatorFallbackError()) logger_default.error(error);
	}
	else if (platform === "weapp" && projectPath && normalizedOptions.reuseOpenedProject === false) {
		if (!await closeIde()) logger_default.warn("关闭当前微信开发者工具失败,仍继续尝试打开目标项目。");
	}
	await runWechatIdeOpenWithRetry(createIdeOpenArgv(platform, projectPath, normalizedOptions));
	if (platform === "weapp" && projectPath && normalizedOptions.prepareAutomatorSession) await prepareWechatIdeAutomatorSession(projectPath, normalizedOptions);
	if (platform === "weapp" && projectPath && !normalizedOptions.skipPostOpenHealthCheck) await verifyAndRecoverOpenedWechatIdeProject(platform, projectPath, bootstrapResult?.servicePortEnabled, normalizedOptions);
}
/**
* @description 解析 IDE 相关命令所需的平台、项目目录与配置上下文。
*/
async function resolveIdeCommandContext(options) {
	const cwd = options.cwd ?? process.cwd();
	let platform = options.platform;
	let projectPath = options.projectPath;
	if (!platform || !projectPath) try {
		const targets = resolveRuntimeTargets({ platform });
		const ctx = await createCompilerContext({
			cwd,
			mode: options.mode ?? "development",
			configFile: options.configFile,
			inlineConfig: createInlineConfig(targets),
			cliPlatform: options.cliPlatform
		});
		platform ??= ctx.configService.platform;
		if (!projectPath) projectPath = resolveIdeProjectRoot(ctx.configService.mpDistRoot, ctx.configService.cwd);
		return {
			cwd: ctx.configService.cwd,
			platform,
			projectPath,
			weappViteConfig: ctx.configService.weappViteConfig,
			mpDistRoot: ctx.configService.mpDistRoot
		};
	} catch {}
	if (!projectPath) {
		const defaultProjectRoot = getDefaultIdeProjectRoot(platform);
		if (defaultProjectRoot) projectPath = resolveIdeProjectRoot(defaultProjectRoot, cwd);
	}
	return {
		cwd,
		platform,
		projectPath
	};
}
//#endregion
//#region src/cli/options.ts
function filterDuplicateOptions(options) {
	for (const [key, value] of Object.entries(options)) if (Array.isArray(value)) options[key] = value[value.length - 1];
}
function resolveConfigFile(options) {
	if (typeof options.config === "string") return options.config;
	if (typeof options.c === "string") return options.c;
}
function convertBase(value) {
	if (value === 0) return "";
	return value;
}
function coerceBooleanOption(value) {
	if (value === void 0) return;
	if (typeof value === "boolean") return value;
	if (typeof value === "string") {
		const normalized = value.trim().toLowerCase();
		if (normalized === "") return true;
		if (normalized === "false" || normalized === "0" || normalized === "off" || normalized === "no") return false;
		if (normalized === "true" || normalized === "1" || normalized === "on" || normalized === "yes") return true;
		return true;
	}
	if (typeof value === "number") return value !== 0;
	return Boolean(value);
}
function isUiEnabled(options) {
	return Boolean(options.ui || options.analyze);
}
//#endregion
//#region src/cli/commands/alipayExecute.ts
function createSpawnOptions() {
	return {
		shell: process.platform === "win32",
		stdio: "inherit"
	};
}
/**
* @description 执行本机 minidev 命令。
*/
async function runSpawnMinidev(command, argv, runner) {
	await new Promise((resolve, reject) => {
		const child = runner(command, argv, createSpawnOptions());
		child.on("error", (error) => {
			reject(error);
		});
		child.on("exit", (code, signal) => {
			if (code === 0) {
				resolve();
				return;
			}
			reject(/* @__PURE__ */ new Error(signal ? `minidev ${argv[0] ?? ""} exited with signal ${signal}` : `minidev ${argv[0] ?? ""} exited with code ${code ?? "unknown"}`));
		});
	});
}
/**
* @description 使用系统进程执行本机 minidev 命令。
*/
async function spawnMinidev(command, argv) {
	return await runSpawnMinidev(command, argv, spawn);
}
//#endregion
//#region src/cli/commands/alipay.ts
function normalizePassthroughArgs(args) {
	return Array.isArray(args) ? args.filter((arg) => typeof arg === "string") : [];
}
function appendOption(argv, name, value) {
	if (typeof value === "string" && value.trim()) argv.push(name, value);
}
function hasOption(argv, ...names) {
	return argv.some((arg) => names.includes(arg) || names.some((name) => arg.startsWith(`${name}=`)));
}
function normalizeAlipayAction(action) {
	if (action === "open") return "ide";
	if (action === "ide" || action === "login" || action === "preview" || action === "upload") return action;
	throw new Error(`未知 alipay 子命令: ${action ?? "(empty)"}`);
}
function resolveProjectPath(root, resolvedProjectPath, options) {
	if (typeof options.project === "string" && options.project.trim()) return options.project;
	return root ?? resolvedProjectPath;
}
function resolveMinidevCommand(options) {
	return typeof options.minidev === "string" && options.minidev.trim() ? options.minidev : "minidev";
}
function createMinidevArgv(action, root, resolvedProjectPath, options) {
	const passthroughArgs = normalizePassthroughArgs(options["--"]);
	const argv = [action];
	const projectPath = resolveProjectPath(root, resolvedProjectPath, options);
	if ((action === "ide" || action === "preview" || action === "upload") && projectPath && !hasOption(passthroughArgs, "--project", "-p")) argv.push("--project", path.normalize(projectPath));
	if ((action === "preview" || action === "upload") && !hasOption(passthroughArgs, "--app-id", "-a")) appendOption(argv, "--app-id", options.appId);
	if ((action === "login" || action === "preview" || action === "upload") && !hasOption(passthroughArgs, "--client-type", "-c")) appendOption(argv, "--client-type", options.clientType);
	if (action === "upload" && !hasOption(passthroughArgs, "--version", "-v")) appendOption(argv, "--version", options.version);
	argv.push(...passthroughArgs);
	return argv;
}
async function runAlipayCommand(action, root, options) {
	const normalizedAction = normalizeAlipayAction(action);
	filterDuplicateOptions(options);
	const argv = createMinidevArgv(normalizedAction, root, (await resolveIdeCommandContext({
		configFile: resolveConfigFile(options),
		mode: options.mode ?? (normalizedAction === "upload" ? "production" : "development"),
		platform: "alipay",
		projectPath: root ?? options.project,
		cliPlatform: "alipay"
	})).projectPath, options);
	const command = resolveMinidevCommand(options);
	logger_default.info(`执行支付宝小程序 CLI:${command} ${argv.join(" ")}`);
	await spawnMinidev(command, argv);
}
function registerAlipayCommand(cli) {
	cli.command("alipay [action] [root]", "run Alipay minidev ide, login, preview, or upload").option("-a, --app-id <appId>", "[string] Alipay mini program appId").option("-c, --client-type <clientType>", "[string] minidev client type").option("--minidev <command>", "[string] minidev executable path or command name").option("--project <path>", "[string] Alipay mini program project path").option("--version <version>", "[string] upload version").allowUnknownOptions().action(async (action, root, options) => {
		await runAlipayCommand(action, root, options);
	});
}
//#endregion
//#region src/analyze/hmr.ts
function createMetricSummary(values) {
	if (!values.length) return { count: 0 };
	const total = values.reduce((sum, value) => sum + value, 0);
	return {
		count: values.length,
		averageMs: total / values.length,
		maxMs: Math.max(...values)
	};
}
function createOperationSummary(values) {
	if (!values.length) return { count: 0 };
	const total = values.reduce((sum, value) => sum + value, 0);
	return {
		count: values.length,
		average: total / values.length,
		max: Math.max(...values)
	};
}
function sortCountEntries(map) {
	return [...map.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).map(([name, count]) => ({
		name,
		count
	}));
}
function collectCounts(target, values) {
	for (const value of values ?? []) {
		if (!value) continue;
		target.set(value, (target.get(value) ?? 0) + 1);
	}
}
function isFiniteNumber$1(value) {
	return typeof value === "number" && Number.isFinite(value);
}
/**
* @description 聚合 HMR JSONL profile,为命令行与后续仪表盘复用。
*/
async function analyzeHmrProfile(options) {
	const lines = (await fs$1.readFile(options.profilePath, "utf8")).split(/\r?\n/);
	const samples = [];
	let skippedLineCount = 0;
	for (const line of lines) {
		const trimmed = line.trim();
		if (!trimmed) continue;
		try {
			const parsed = JSON.parse(trimmed);
			if (!isFiniteNumber$1(parsed.totalMs)) {
				skippedLineCount += 1;
				continue;
			}
			samples.push(parsed);
		} catch {
			skippedLineCount += 1;
		}
	}
	const eventCounts = /* @__PURE__ */ new Map();
	const dirtyReasonCounts = /* @__PURE__ */ new Map();
	const pendingReasonCounts = /* @__PURE__ */ new Map();
	const totalValues = [];
	const buildCoreValues = [];
	const buildStartValues = [];
	const pluginResolveValues = [];
	const transformValues = [];
	const coreTransformValues = [];
	const wevuTransformValues = [];
	const vueTransformValues = [];
	const vueReadSourceValues = [];
	const vueCompileValues = [];
	const vueFinalizeCompiledValues = [];
	const vueFinalizeCodeValues = [];
	const coreLoadValues = [];
	const entryLoadValues = [];
	const entryCodeReadValues = [];
	const entrySidecarResolveValues = [];
	const entryJsonReadValues = [];
	const entryVueConfigValues = [];
	const entryTemplateScanValues = [];
	const entryScriptSetupValues = [];
	const entryVueSignatureValues = [];
	const entryAutoImportValues = [];
	const entryPrepareValues = [];
	const entryEmitOutputValues = [];
	const entryStyleScanValues = [];
	const entryStyleReadValues = [];
	const entryResolveValues = [];
	const entryChunkEmitValues = [];
	const entryChunkLoadValues = [];
	const entryChunkEmitFileValues = [];
	const entryLayoutValues = [];
	const requestGlobalsValues = [];
	const weapiResolveValues = [];
	const renderStartValues = [];
	const generateBundleValues = [];
	const generateSharedValues = [];
	const generateRewriteValues = [];
	const generateModuleGraphValues = [];
	const snapshotResolveValues = [];
	const snapshotBuildValues = [];
	const writeValues = [];
	const watchToDirtyValues = [];
	const emitValues = [];
	const sharedChunkValues = [];
	const chunkEmitCountValues = [];
	const loadCountValues = [];
	const resolveCountValues = [];
	const skippedLoadedCountValues = [];
	for (const sample of samples) {
		totalValues.push(sample.totalMs);
		if (sample.event) eventCounts.set(sample.event, (eventCounts.get(sample.event) ?? 0) + 1);
		if (isFiniteNumber$1(sample.buildCoreMs)) buildCoreValues.push(sample.buildCoreMs);
		if (isFiniteNumber$1(sample.buildStartMs)) buildStartValues.push(sample.buildStartMs);
		if (isFiniteNumber$1(sample.pluginResolveMs)) pluginResolveValues.push(sample.pluginResolveMs);
		if (isFiniteNumber$1(sample.transformMs)) transformValues.push(sample.transformMs);
		if (isFiniteNumber$1(sample.coreTransformMs)) coreTransformValues.push(sample.coreTransformMs);
		if (isFiniteNumber$1(sample.wevuTransformMs)) wevuTransformValues.push(sample.wevuTransformMs);
		if (isFiniteNumber$1(sample.vueTransformMs)) vueTransformValues.push(sample.vueTransformMs);
		if (isFiniteNumber$1(sample.vueReadSourceMs)) vueReadSourceValues.push(sample.vueReadSourceMs);
		if (isFiniteNumber$1(sample.vueCompileMs)) vueCompileValues.push(sample.vueCompileMs);
		if (isFiniteNumber$1(sample.vueFinalizeCompiledMs)) vueFinalizeCompiledValues.push(sample.vueFinalizeCompiledMs);
		if (isFiniteNumber$1(sample.vueFinalizeCodeMs)) vueFinalizeCodeValues.push(sample.vueFinalizeCodeMs);
		if (isFiniteNumber$1(sample.coreLoadMs)) coreLoadValues.push(sample.coreLoadMs);
		if (isFiniteNumber$1(sample.entryLoadMs)) entryLoadValues.push(sample.entryLoadMs);
		if (isFiniteNumber$1(sample.entryCodeReadMs)) entryCodeReadValues.push(sample.entryCodeReadMs);
		if (isFiniteNumber$1(sample.entrySidecarResolveMs)) entrySidecarResolveValues.push(sample.entrySidecarResolveMs);
		if (isFiniteNumber$1(sample.entryJsonReadMs)) entryJsonReadValues.push(sample.entryJsonReadMs);
		if (isFiniteNumber$1(sample.entryVueConfigMs)) entryVueConfigValues.push(sample.entryVueConfigMs);
		if (isFiniteNumber$1(sample.entryTemplateScanMs)) entryTemplateScanValues.push(sample.entryTemplateScanMs);
		if (isFiniteNumber$1(sample.entryScriptSetupMs)) entryScriptSetupValues.push(sample.entryScriptSetupMs);
		if (isFiniteNumber$1(sample.entryVueSignatureMs)) entryVueSignatureValues.push(sample.entryVueSignatureMs);
		if (isFiniteNumber$1(sample.entryAutoImportMs)) entryAutoImportValues.push(sample.entryAutoImportMs);
		if (isFiniteNumber$1(sample.entryPrepareMs)) entryPrepareValues.push(sample.entryPrepareMs);
		if (isFiniteNumber$1(sample.entryEmitOutputMs)) entryEmitOutputValues.push(sample.entryEmitOutputMs);
		if (isFiniteNumber$1(sample.entryStyleScanMs)) entryStyleScanValues.push(sample.entryStyleScanMs);
		if (isFiniteNumber$1(sample.entryStyleReadMs)) entryStyleReadValues.push(sample.entryStyleReadMs);
		if (isFiniteNumber$1(sample.entryResolveMs)) entryResolveValues.push(sample.entryResolveMs);
		if (isFiniteNumber$1(sample.entryChunkEmitMs)) entryChunkEmitValues.push(sample.entryChunkEmitMs);
		if (isFiniteNumber$1(sample.entryChunkLoadMs)) entryChunkLoadValues.push(sample.entryChunkLoadMs);
		if (isFiniteNumber$1(sample.entryChunkEmitFileMs)) entryChunkEmitFileValues.push(sample.entryChunkEmitFileMs);
		if (isFiniteNumber$1(sample.entryLayoutMs)) entryLayoutValues.push(sample.entryLayoutMs);
		if (isFiniteNumber$1(sample.requestGlobalsMs)) requestGlobalsValues.push(sample.requestGlobalsMs);
		if (isFiniteNumber$1(sample.weapiResolveMs)) weapiResolveValues.push(sample.weapiResolveMs);
		if (isFiniteNumber$1(sample.renderStartMs)) renderStartValues.push(sample.renderStartMs);
		if (isFiniteNumber$1(sample.generateBundleMs)) generateBundleValues.push(sample.generateBundleMs);
		if (isFiniteNumber$1(sample.generateSharedMs)) generateSharedValues.push(sample.generateSharedMs);
		if (isFiniteNumber$1(sample.generateRewriteMs)) generateRewriteValues.push(sample.generateRewriteMs);
		if (isFiniteNumber$1(sample.generateModuleGraphMs)) generateModuleGraphValues.push(sample.generateModuleGraphMs);
		if (isFiniteNumber$1(sample.snapshotResolveMs)) snapshotResolveValues.push(sample.snapshotResolveMs);
		if (isFiniteNumber$1(sample.snapshotBuildMs)) snapshotBuildValues.push(sample.snapshotBuildMs);
		if (isFiniteNumber$1(sample.writeMs)) writeValues.push(sample.writeMs);
		if (isFiniteNumber$1(sample.watchToDirtyMs)) watchToDirtyValues.push(sample.watchToDirtyMs);
		if (isFiniteNumber$1(sample.emitMs)) emitValues.push(sample.emitMs);
		if (isFiniteNumber$1(sample.sharedChunkResolveMs)) sharedChunkValues.push(sample.sharedChunkResolveMs);
		if (isFiniteNumber$1(sample.chunkEmitCount)) chunkEmitCountValues.push(sample.chunkEmitCount);
		if (isFiniteNumber$1(sample.loadCount)) loadCountValues.push(sample.loadCount);
		if (isFiniteNumber$1(sample.resolveCount)) resolveCountValues.push(sample.resolveCount);
		if (isFiniteNumber$1(sample.skippedLoadedCount)) skippedLoadedCountValues.push(sample.skippedLoadedCount);
		collectCounts(dirtyReasonCounts, sample.dirtyReasonSummary);
		collectCounts(pendingReasonCounts, sample.pendingReasonSummary);
	}
	const orderedByTime = [...samples].sort((left, right) => {
		const leftTime = typeof left.timestamp === "string" ? Date.parse(left.timestamp) : NaN;
		const rightTime = typeof right.timestamp === "string" ? Date.parse(right.timestamp) : NaN;
		if (Number.isFinite(leftTime) && Number.isFinite(rightTime)) return leftTime - rightTime;
		return 0;
	});
	const slowestSamples = [...samples].sort((left, right) => (right.totalMs ?? 0) - (left.totalMs ?? 0)).slice(0, options.topSlowest ?? 5);
	return {
		runtime: "mini",
		kind: "hmr-profile",
		generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
		profilePath: options.profilePath,
		sampleCount: samples.length,
		skippedLineCount,
		firstTimestamp: orderedByTime[0]?.timestamp,
		lastTimestamp: orderedByTime[orderedByTime.length - 1]?.timestamp,
		metrics: {
			totalMs: createMetricSummary(totalValues),
			buildCoreMs: createMetricSummary(buildCoreValues),
			buildStartMs: createMetricSummary(buildStartValues),
			pluginResolveMs: createMetricSummary(pluginResolveValues),
			transformMs: createMetricSummary(transformValues),
			coreTransformMs: createMetricSummary(coreTransformValues),
			wevuTransformMs: createMetricSummary(wevuTransformValues),
			vueTransformMs: createMetricSummary(vueTransformValues),
			vueReadSourceMs: createMetricSummary(vueReadSourceValues),
			vueCompileMs: createMetricSummary(vueCompileValues),
			vueFinalizeCompiledMs: createMetricSummary(vueFinalizeCompiledValues),
			vueFinalizeCodeMs: createMetricSummary(vueFinalizeCodeValues),
			coreLoadMs: createMetricSummary(coreLoadValues),
			entryLoadMs: createMetricSummary(entryLoadValues),
			entryCodeReadMs: createMetricSummary(entryCodeReadValues),
			entrySidecarResolveMs: createMetricSummary(entrySidecarResolveValues),
			entryJsonReadMs: createMetricSummary(entryJsonReadValues),
			entryVueConfigMs: createMetricSummary(entryVueConfigValues),
			entryTemplateScanMs: createMetricSummary(entryTemplateScanValues),
			entryScriptSetupMs: createMetricSummary(entryScriptSetupValues),
			entryVueSignatureMs: createMetricSummary(entryVueSignatureValues),
			entryAutoImportMs: createMetricSummary(entryAutoImportValues),
			entryPrepareMs: createMetricSummary(entryPrepareValues),
			entryEmitOutputMs: createMetricSummary(entryEmitOutputValues),
			entryStyleScanMs: createMetricSummary(entryStyleScanValues),
			entryStyleReadMs: createMetricSummary(entryStyleReadValues),
			entryResolveMs: createMetricSummary(entryResolveValues),
			entryChunkEmitMs: createMetricSummary(entryChunkEmitValues),
			entryChunkLoadMs: createMetricSummary(entryChunkLoadValues),
			entryChunkEmitFileMs: createMetricSummary(entryChunkEmitFileValues),
			entryLayoutMs: createMetricSummary(entryLayoutValues),
			requestGlobalsMs: createMetricSummary(requestGlobalsValues),
			weapiResolveMs: createMetricSummary(weapiResolveValues),
			renderStartMs: createMetricSummary(renderStartValues),
			generateBundleMs: createMetricSummary(generateBundleValues),
			generateSharedMs: createMetricSummary(generateSharedValues),
			generateRewriteMs: createMetricSummary(generateRewriteValues),
			generateModuleGraphMs: createMetricSummary(generateModuleGraphValues),
			snapshotResolveMs: createMetricSummary(snapshotResolveValues),
			snapshotBuildMs: createMetricSummary(snapshotBuildValues),
			writeMs: createMetricSummary(writeValues),
			watchToDirtyMs: createMetricSummary(watchToDirtyValues),
			emitMs: createMetricSummary(emitValues),
			sharedChunkResolveMs: createMetricSummary(sharedChunkValues)
		},
		operations: {
			chunkEmitCount: createOperationSummary(chunkEmitCountValues),
			loadCount: createOperationSummary(loadCountValues),
			resolveCount: createOperationSummary(resolveCountValues),
			skippedLoadedCount: createOperationSummary(skippedLoadedCountValues)
		},
		events: sortCountEntries(eventCounts),
		dirtyReasons: sortCountEntries(dirtyReasonCounts),
		pendingReasons: sortCountEntries(pendingReasonCounts),
		slowestSamples
	};
}
//#endregion
//#region src/analyze/preload/budget.ts
const PRELOAD_LIMIT_BYTES = 2097152;
function createPackageSizeMap$1(result) {
	return new Map((result?.packages ?? []).map((pkg) => [pkg.id, pkg.files.reduce((total, file) => total + (file.size ?? 0), 0)]));
}
function getConfiguredPackages(rule, appJson) {
	if (!rule || typeof rule !== "object" || !("packages" in rule)) return [];
	const packages = rule.packages;
	if (!Array.isArray(packages)) return [];
	return packages.flatMap((item) => typeof item === "string" ? [resolvePreloadPackageIdentifier(item, appJson)] : []).filter((item) => Boolean(item));
}
function createPreloadBudgets(appJson, result, configuredRules, packageAnalysis) {
	const packageSizes = createPackageSizeMap$1(packageAnalysis);
	const bySourcePackage = /* @__PURE__ */ new Map();
	const ensureSourcePackage = (page) => {
		const sourcePackage = page.packageRoot ?? "__main__";
		let entry = bySourcePackage.get(sourcePackage);
		if (!entry) {
			entry = {
				sourceType: page.packageRoot ? page.independent ? "independent" : "subpackage" : "main",
				targets: /* @__PURE__ */ new Map()
			};
			bySourcePackage.set(sourcePackage, entry);
		}
		return entry;
	};
	for (const page of result.pages) {
		const configuredKey = findPreloadRuleKey(page.route, configuredRules);
		if (configuredKey === void 0) continue;
		const entry = ensureSourcePackage(page);
		for (const packageRoot of getConfiguredPackages(configuredRules[configuredKey], appJson)) {
			const target = entry.targets.get(packageRoot) ?? {
				configured: false,
				suggested: false
			};
			target.configured = true;
			entry.targets.set(packageRoot, target);
		}
	}
	const pageByRoute = new Map(result.pages.map((page) => [page.route, page]));
	for (const suggestion of result.suggestions) {
		const page = pageByRoute.get(suggestion.page);
		if (!page) continue;
		const entry = ensureSourcePackage(page);
		const target = entry.targets.get(suggestion.packageRoot) ?? {
			configured: false,
			suggested: false
		};
		target.suggested = true;
		entry.targets.set(suggestion.packageRoot, target);
	}
	return [...bySourcePackage.entries()].map(([sourcePackage, entry]) => {
		const targets = [...entry.targets.entries()].map(([packageRoot, state]) => {
			const packageId = packageRoot === "__APP__" ? "__main__" : packageRoot;
			return {
				packageRoot,
				bytes: packageSizes.get(packageId),
				...state
			};
		}).sort((left, right) => left.packageRoot.localeCompare(right.packageRoot));
		const unknownPackages = targets.filter((target) => target.bytes === void 0).map((target) => target.packageRoot);
		const estimatedBytes = targets.reduce((total, target) => total + (target.bytes ?? 0), 0);
		const status = estimatedBytes > PRELOAD_LIMIT_BYTES ? "exceeded" : unknownPackages.length > 0 ? "unknown" : "ok";
		return {
			sourcePackage,
			sourceType: entry.sourceType,
			limitBytes: PRELOAD_LIMIT_BYTES,
			estimatedBytes,
			remainingBytes: unknownPackages.length === 0 ? Math.max(0, PRELOAD_LIMIT_BYTES - estimatedBytes) : void 0,
			status,
			targets,
			unknownPackages
		};
	}).sort((left, right) => left.sourcePackage.localeCompare(right.sourcePackage));
}
//#endregion
//#region src/analyze/preload/index.ts
async function readFileIfExists(filePath) {
	if (!filePath) return;
	try {
		return await fs$1.readFile(filePath, "utf8");
	} catch {
		return;
	}
}
function splitVueSource(source, filename) {
	try {
		const { descriptor } = parse(source, { filename });
		return {
			template: descriptor.template?.content,
			script: [descriptor.script?.content, descriptor.scriptSetup?.content].filter((content) => Boolean(content)).join("\n")
		};
	} catch {
		return {
			template: source,
			script: source
		};
	}
}
async function collectPageSources(ctx, route) {
	const base = path.resolve(ctx.configService.absoluteSrcRoot, route);
	const [templateEntry, scriptEntry, vueEntry] = await Promise.all([
		findTemplateEntry(base, ctx.configService.platform),
		findJsEntry(base),
		findVueEntry(base)
	]);
	if (vueEntry) {
		const source = await readFileIfExists(vueEntry);
		return source ? splitVueSource(source, vueEntry) : {};
	}
	const [template, script] = await Promise.all([readFileIfExists(templateEntry.path), readFileIfExists(scriptEntry.path)]);
	return {
		template,
		script
	};
}
function aggregateSuggestions(result, configuredRules, appJson) {
	const byPage = /* @__PURE__ */ new Map();
	for (const suggestion of result.suggestions) {
		const current = byPage.get(suggestion.page) ?? {
			page: suggestion.page,
			packages: [],
			evidence: [],
			alreadyConfigured: []
		};
		if (!current.packages.includes(suggestion.packageRoot)) current.packages.push(suggestion.packageRoot);
		current.evidence.push({
			target: suggestion.target,
			packageRoot: suggestion.packageRoot,
			source: suggestion.source
		});
		const configuredKey = findPreloadRuleKey(suggestion.page, configuredRules);
		const existingRule = configuredKey === void 0 ? void 0 : configuredRules[configuredKey];
		const existingPackages = existingRule && typeof existingRule === "object" && "packages" in existingRule ? existingRule.packages : void 0;
		if ((Array.isArray(existingPackages) ? existingPackages.flatMap((item) => typeof item === "string" ? [resolvePreloadPackageIdentifier(item, appJson)] : []) : []).includes(suggestion.packageRoot)) current.alreadyConfigured.push(suggestion.packageRoot);
		byPage.set(suggestion.page, current);
	}
	return [...byPage.values()].map((suggestion) => ({
		...suggestion,
		packages: suggestion.packages.sort(),
		alreadyConfigured: [...new Set(suggestion.alreadyConfigured)].sort(),
		evidence: suggestion.evidence.sort((left, right) => left.target.localeCompare(right.target))
	}));
}
async function analyzePreloadRules(ctx, options = {}) {
	const appJson = (await ctx.scanService.loadAppEntry()).json;
	const pages = collectPreloadPages(appJson);
	const pageSources = /* @__PURE__ */ new Map();
	const sourceEntries = await Promise.all(pages.map(async (page) => [page.route, await collectPageSources(ctx, page.route)]));
	for (const [route, source] of sourceEntries) pageSources.set(route, source);
	const configuredRules = appJson.preloadRule && typeof appJson.preloadRule === "object" ? appJson.preloadRule : {};
	const suggestions = suggestPreloadRules(appJson, pageSources);
	return {
		runtime: "mini",
		kind: "preload",
		generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
		platform: ctx.configService.platform,
		pages: pages.map((page) => page.route),
		configuredRules,
		suggestions: aggregateSuggestions(suggestions, configuredRules, appJson),
		budgets: createPreloadBudgets(appJson, suggestions, configuredRules, options.packageAnalysis),
		uncoveredPages: suggestions.uncovered,
		limitations: [
			"仅分析静态路由字面量;动态路由、后端配置和运行时守卫不会被推断。",
			"建议使用分包 root 作为 packages 值;预算按触发页所属包聚合,未知体积目标仍需人工复核。",
			"静态跳转只能证明可达性,不能代表真实访问频率,正式启用前仍需结合业务数据确认。",
			"该命令只提供可审计建议,不会自动修改源码或覆盖手写的 app.json.preloadRule。"
		]
	};
}
//#endregion
//#region src/analyze/components/suggestions.ts
function createComponentSuggestion(usage) {
	if (usage.componentPackage !== "__main__" || usage.crossPackageUsageCount === 0) return;
	if (usage.crossPackageUsageCount === usage.placeholderCoveredCrossPackageUsageCount) return;
	const pagePackages = Array.from(new Set(Array.from(usage.pages.values()).map((page) => page.packageId))).sort((left, right) => {
		if (left === "__main__") return -1;
		if (right === "__main__") return 1;
		return left.localeCompare(right);
	});
	const subPackageIds = pagePackages.filter((packageId) => packageId !== "__main__");
	const usedByMain = pagePackages.includes("__main__");
	if (subPackageIds.length === 1 && !usedByMain) {
		const targetPackage = subPackageIds[0];
		return {
			kind: "move-to-subpackage",
			component: usage.component,
			componentPackage: usage.componentPackage,
			targetPackage,
			pagePackages,
			message: `主包组件 ${usage.component} 仅被分包 ${targetPackage} 使用,建议评估移动到该分包。`
		};
	}
	if (subPackageIds.length > 1) return {
		kind: "shared-subpackage-or-placeholder",
		component: usage.component,
		componentPackage: usage.componentPackage,
		pagePackages,
		message: `主包组件 ${usage.component} 被多个分包使用,建议评估分包归属、共享策略或 componentPlaceholder。`
	};
	if (usedByMain && subPackageIds.length > 0) return {
		kind: "split-or-async",
		component: usage.component,
		componentPackage: usage.componentPackage,
		pagePackages,
		message: `主包组件 ${usage.component} 同时被主包和分包使用,建议评估组件拆分、归属或异步化策略。`
	};
}
//#endregion
//#region src/analyze/components/index.ts
function normalizeRoute(value) {
	return posix.normalize(value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.json$/, ""));
}
function isRecord(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
function toStringArray(value) {
	return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim() !== "") : [];
}
function readUsingComponents(config) {
	if (!isRecord(config) || !isRecord(config.usingComponents)) return [];
	return Object.entries(config.usingComponents).filter((entry) => typeof entry[1] === "string" && entry[1].trim() !== "");
}
function readComponentGenericDefaults(config) {
	if (!isRecord(config) || !isRecord(config.componentGenerics)) return [];
	return Object.entries(config.componentGenerics).map(([name, value]) => isRecord(value) && typeof value.default === "string" && value.default.trim() !== "" ? [name, value.default] : void 0).filter((entry) => Boolean(entry));
}
function readComponentPlaceholder(config) {
	if (!isRecord(config) || !isRecord(config.componentPlaceholder)) return /* @__PURE__ */ new Set();
	return new Set(Object.keys(config.componentPlaceholder));
}
function resolveComponentRoute(owner, request) {
	const normalizedRequest = request.replace(/\\/g, "/").trim();
	if (!normalizedRequest || normalizedRequest.startsWith("plugin://")) return;
	if (normalizedRequest.startsWith(".")) return normalizeRoute(posix.join(posix.dirname(owner), normalizedRequest));
	if (normalizedRequest.startsWith("/")) return normalizeRoute(normalizedRequest);
	return normalizeRoute(normalizedRequest);
}
function resolvePackageId(route, subPackages) {
	return subPackages.map((item) => item.root).filter(Boolean).sort((left, right) => right.length - left.length).find((root) => route === root || route.startsWith(`${root}/`)) ?? "__main__";
}
function collectAppPages(configs) {
	const appJson = configs.get("app");
	if (!isRecord(appJson)) return /* @__PURE__ */ new Set();
	const pages = /* @__PURE__ */ new Set();
	for (const page of toStringArray(appJson.pages)) pages.add(normalizeRoute(page));
	const subPackages = Array.isArray(appJson.subPackages) ? appJson.subPackages : Array.isArray(appJson.subpackages) ? appJson.subpackages : [];
	for (const item of subPackages) {
		if (!isRecord(item) || typeof item.root !== "string") continue;
		for (const page of toStringArray(item.pages)) pages.add(normalizeRoute(posix.join(item.root, page)));
	}
	return pages;
}
function registerUsage(usageMap, edge, page, pagePackage, componentPackage) {
	const usage = usageMap.get(edge.component) ?? {
		component: edge.component,
		componentPackage,
		totalUsageCount: 0,
		pages: /* @__PURE__ */ new Map(),
		crossPackageUsageCount: 0,
		placeholderCoveredCrossPackageUsageCount: 0
	};
	usage.totalUsageCount += 1;
	const pageUsage = usage.pages.get(page) ?? {
		page,
		packageId: pagePackage,
		usageCount: 0
	};
	pageUsage.usageCount += 1;
	usage.pages.set(page, pageUsage);
	if (pagePackage !== componentPackage) {
		usage.crossPackageUsageCount += 1;
		if (edge.placeholderCovered) usage.placeholderCoveredCrossPackageUsageCount += 1;
	}
	usageMap.set(edge.component, usage);
}
function collectAnalyzeComponentJsonConfigs(output) {
	if (!output) return [];
	const configs = [];
	for (const item of output.output ?? []) {
		if (item.type !== "asset" || !item.fileName.endsWith(".json")) continue;
		const asset = item;
		if (typeof asset.source !== "string") continue;
		try {
			configs.push({
				file: normalizeRoute(asset.fileName),
				config: JSON.parse(asset.source)
			});
		} catch {}
	}
	return configs;
}
function analyzeComponentUsage(options) {
	const configs = /* @__PURE__ */ new Map();
	for (const item of options.jsonConfigs) configs.set(normalizeRoute(item.file), item.config);
	const pages = collectAppPages(configs);
	const graph = /* @__PURE__ */ new Map();
	const packageMap = /* @__PURE__ */ new Map();
	for (const route of configs.keys()) packageMap.set(route, resolvePackageId(route, options.subPackages));
	for (const [owner, config] of configs) {
		const placeholders = readComponentPlaceholder(config);
		const edges = [...readUsingComponents(config), ...readComponentGenericDefaults(config)].map(([name, request]) => {
			const component = resolveComponentRoute(owner, request);
			return component && configs.has(component) ? {
				owner,
				component,
				placeholderCovered: placeholders.has(name)
			} : void 0;
		}).filter((edge) => Boolean(edge)).filter((edge, index, all) => all.findIndex((item) => item.component === edge.component) === index);
		if (edges.length > 0) graph.set(owner, edges);
	}
	const usageMap = /* @__PURE__ */ new Map();
	const visit = (owner, page, stack) => {
		for (const edge of graph.get(owner) ?? []) {
			const componentPackage = packageMap.get(edge.component) ?? "__main__";
			const pagePackage = packageMap.get(page) ?? "__main__";
			registerUsage(usageMap, edge, page, pagePackage, componentPackage);
			if (stack.has(edge.component)) continue;
			const nextStack = new Set(stack);
			nextStack.add(edge.component);
			visit(edge.component, page, nextStack);
		}
	};
	for (const page of pages) visit(page, page, /* @__PURE__ */ new Set([page]));
	return Array.from(usageMap.values()).map((usage) => {
		const pages = Array.from(usage.pages.values()).sort((left, right) => left.page.localeCompare(right.page));
		const suggestions = [createComponentSuggestion(usage)].filter((item) => Boolean(item));
		return {
			component: usage.component,
			componentPackage: usage.componentPackage,
			totalUsageCount: usage.totalUsageCount,
			pageUsageCount: pages.length,
			pages,
			suggestions
		};
	}).sort((left, right) => {
		const usageDelta = right.totalUsageCount - left.totalUsageCount;
		return usageDelta !== 0 ? usageDelta : left.component.localeCompare(right.component);
	});
}
//#endregion
//#region src/analyze/subpackages/metadata.ts
const defaultTotalBudgetBytes = 20971520;
const defaultPackageBudgetBytes = 2097152;
const defaultWarningRatio = .85;
const defaultHistoryDir = ".weapp-vite/analyze-history";
const defaultHistoryLimit = 20;
function resolveBudgetValue(value, fallback) {
	return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
}
function resolveHistoryLimit(value) {
	return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : defaultHistoryLimit;
}
function resolveAnalyzeBudgets(configService) {
	const budgets = configService.weappViteConfig.analyze?.budgets;
	const legacyPackageBudget = configService.weappViteConfig.packageSizeWarningBytes;
	const packageFallback = resolveBudgetValue(legacyPackageBudget, defaultPackageBudgetBytes);
	return {
		totalBytes: resolveBudgetValue(budgets?.totalBytes, defaultTotalBudgetBytes),
		mainBytes: resolveBudgetValue(budgets?.mainBytes, packageFallback),
		subPackageBytes: resolveBudgetValue(budgets?.subPackageBytes, packageFallback),
		independentBytes: resolveBudgetValue(budgets?.independentBytes, packageFallback),
		warningRatio: resolveBudgetValue(budgets?.warningRatio, defaultWarningRatio),
		source: budgets ? "config" : "default"
	};
}
function resolveAnalyzeHistoryMetadata(configService) {
	const history = configService.weappViteConfig.analyze?.history;
	if (history === false) return {
		enabled: false,
		dir: path.resolve(configService.cwd, defaultHistoryDir),
		limit: defaultHistoryLimit
	};
	const historyConfig = typeof history === "object" && history ? history : {};
	const rawDir = typeof historyConfig.dir === "string" && historyConfig.dir.trim() ? historyConfig.dir.trim() : defaultHistoryDir;
	return {
		enabled: historyConfig.enabled !== false,
		dir: path.isAbsolute(rawDir) ? rawDir : path.resolve(configService.cwd, rawDir),
		limit: resolveHistoryLimit(historyConfig.limit)
	};
}
function createAnalyzeMetadata(configService, now = /* @__PURE__ */ new Date()) {
	const history = resolveAnalyzeHistoryMetadata(configService);
	return {
		generatedAt: now.toISOString(),
		budgets: resolveAnalyzeBudgets(configService),
		history: {
			...history,
			dir: configService.relativeCwd(history.dir)
		}
	};
}
//#endregion
//#region src/analyze/subpackages/classifier.ts
const VIRTUAL_MODULE_INDICATOR = "\0";
const VIRTUAL_PREFIX = `${SHARED_CHUNK_VIRTUAL_PREFIX}/`;
function classifyModuleSourceKind(options) {
	if (options.isNodeModule) return "node_modules";
	if (options.inSrc) return "src";
	if (options.inPlugin) return "plugin";
	return "workspace";
}
function resolvePluginAssetAbsolute(normalizedFileName, pluginRoot) {
	if (!pluginRoot) return;
	const pluginBase = posix.basename(pluginRoot);
	if (normalizedFileName !== pluginBase && !normalizedFileName.startsWith(`${pluginBase}/`)) return;
	const relative = normalizedFileName === pluginBase ? "" : normalizedFileName.slice(pluginBase.length + 1);
	const absolute = posix.resolve(pluginRoot, relative);
	return isPathInside(pluginRoot, absolute) ? absolute : void 0;
}
function resolveSubPackageRoot$1(fileName, context) {
	const normalized = posix.normalize(fileName);
	return Array.from(context.subPackageRoots).filter(Boolean).sort((left, right) => right.length - left.length).find((root) => normalized === root || normalized.startsWith(`${root}/`));
}
function classifyPackage(fileName, origin, context) {
	if (fileName.startsWith(VIRTUAL_PREFIX)) {
		const combination = fileName.slice(VIRTUAL_PREFIX.length).split("/")[0] || "shared";
		return {
			id: `virtual:${combination}`,
			label: `共享虚拟包 ${combination}`,
			type: "virtual"
		};
	}
	const rootCandidate = resolveSubPackageRoot$1(fileName, context);
	if (rootCandidate) {
		const isIndependent = context.independentRoots.has(rootCandidate);
		return {
			id: rootCandidate,
			label: `${isIndependent ? "独立分包" : "分包"} ${rootCandidate}`,
			type: isIndependent || origin === "independent" ? "independent" : "subPackage"
		};
	}
	return {
		id: "__main__",
		label: "主包",
		type: "main"
	};
}
function normalizeModuleId(id) {
	if (!id || id.includes(VIRTUAL_MODULE_INDICATOR)) return;
	if (!posix.isAbsolute(id)) return;
	return posix.normalize(id);
}
function resolveModuleSourceType(absoluteId, ctx) {
	const { configService } = ctx;
	const isNodeModule = absoluteId.includes("/node_modules/") || absoluteId.includes("\\node_modules\\");
	const pluginRoot = configService.absolutePluginRoot;
	const srcRoot = configService.absoluteSrcRoot;
	const sourceType = classifyModuleSourceKind({
		isNodeModule,
		inSrc: isPathInside(srcRoot, absoluteId),
		inPlugin: pluginRoot ? isPathInside(pluginRoot, absoluteId) : false
	});
	return {
		source: configService.relativeAbsoluteSrcRoot(absoluteId),
		sourceType
	};
}
function resolveAssetSource(fileName, ctx) {
	const { configService } = ctx;
	const normalized = posix.normalize(fileName);
	const srcCandidate = posix.resolve(configService.absoluteSrcRoot, normalized);
	if (isPathInside(configService.absoluteSrcRoot, srcCandidate)) return {
		absolute: srcCandidate,
		source: configService.relativeAbsoluteSrcRoot(srcCandidate),
		sourceType: "src"
	};
	const pluginAbsolute = resolvePluginAssetAbsolute(normalized, configService.absolutePluginRoot);
	if (pluginAbsolute) return {
		absolute: pluginAbsolute,
		source: configService.relativeAbsoluteSrcRoot(pluginAbsolute),
		sourceType: "plugin"
	};
}
//#endregion
//#region src/analyze/subpackages/registry.ts
function ensurePackage(packages, classification) {
	const existing = packages.get(classification.id);
	if (existing) return existing;
	const created = {
		...classification,
		files: /* @__PURE__ */ new Map()
	};
	packages.set(classification.id, created);
	return created;
}
function ensureModule(modules, id, source, sourceType) {
	const existing = modules.get(id);
	if (existing) return existing;
	const created = {
		id,
		source,
		sourceType,
		packages: /* @__PURE__ */ new Map()
	};
	modules.set(id, created);
	return created;
}
function registerModuleInPackage(modules, moduleId, source, sourceType, packageId, fileName) {
	const moduleEntry = ensureModule(modules, moduleId, source, sourceType);
	const files = moduleEntry.packages.get(packageId) ?? /* @__PURE__ */ new Set();
	files.add(fileName);
	moduleEntry.packages.set(packageId, files);
}
//#endregion
//#region src/analyze/subpackages/output.ts
function getAssetBuffer(asset) {
	if (typeof asset.source === "string") return Buffer.from(asset.source, "utf8");
	if (asset.source instanceof Uint8Array) return Buffer.from(asset.source);
}
function getCompressedSizes$1(content) {
	if (content === void 0) return {};
	const buffer = typeof content === "string" ? Buffer.from(content, "utf8") : Buffer.from(content);
	return {
		gzipSize: gzipSync(buffer).byteLength,
		brotliSize: brotliCompressSync(buffer).byteLength
	};
}
function processChunk(chunk, origin, ctx, classifierContext, packages, modules) {
	const classification = classifyPackage(chunk.fileName, origin, classifierContext);
	const packageEntry = ensurePackage(packages, classification);
	const chunkEntry = {
		file: chunk.fileName,
		type: "chunk",
		from: origin,
		size: typeof chunk.code === "string" ? Buffer.byteLength(chunk.code, "utf8") : void 0,
		...getCompressedSizes$1(chunk.code),
		isEntry: chunk.isEntry,
		modules: []
	};
	const moduleEntries = Object.entries(chunk.modules ?? {});
	for (const [rawModuleId, info] of moduleEntries) {
		const absoluteId = normalizeModuleId(rawModuleId);
		if (!absoluteId) continue;
		const { source, sourceType } = resolveModuleSourceType(absoluteId, ctx);
		const moduleEntry = {
			id: absoluteId,
			source,
			sourceType,
			bytes: info?.renderedLength
		};
		if (typeof info?.code === "string") moduleEntry.originalBytes = Buffer.byteLength(info.code, "utf8");
		chunkEntry.modules.push(moduleEntry);
		registerModuleInPackage(modules, absoluteId, source, sourceType, classification.id, chunk.fileName);
	}
	if (chunkEntry.modules) chunkEntry.modules.sort((a, b) => a.source.localeCompare(b.source));
	packageEntry.files.set(chunk.fileName, chunkEntry);
}
function processAsset(asset, origin, ctx, classifierContext, packages, modules) {
	const classification = classifyPackage(asset.fileName, origin, classifierContext);
	const packageEntry = ensurePackage(packages, classification);
	const assetBuffer = getAssetBuffer(asset);
	const entry = {
		file: asset.fileName,
		type: "asset",
		from: origin,
		size: assetBuffer?.byteLength,
		...getCompressedSizes$1(assetBuffer)
	};
	const assetSource = resolveAssetSource(asset.fileName, ctx);
	if (assetSource) {
		entry.source = assetSource.source;
		registerModuleInPackage(modules, assetSource.absolute, assetSource.source, assetSource.sourceType, classification.id, asset.fileName);
	}
	packageEntry.files.set(asset.fileName, entry);
}
function processOutput(output, origin, ctx, classifierContext, packages, modules) {
	if (!output) return;
	for (const item of output.output ?? []) if (item.type === "chunk") processChunk(item, origin, ctx, classifierContext, packages, modules);
	else if (item.type === "asset") processAsset(item, origin, ctx, classifierContext, packages, modules);
}
//#endregion
//#region src/analyze/subpackages/summary.ts
function toArray(value) {
	return Array.from(value);
}
function summarizePackages(packages) {
	const order = {
		main: 0,
		subPackage: 1,
		independent: 2,
		virtual: 3
	};
	const reports = toArray(packages.values()).map((pkg) => {
		const files = toArray(pkg.files.values());
		files.sort((a, b) => a.file.localeCompare(b.file));
		return {
			id: pkg.id,
			label: pkg.label,
			type: pkg.type,
			files
		};
	});
	reports.sort((a, b) => {
		const delta = order[a.type] - order[b.type];
		if (delta !== 0) return delta;
		if (a.id === "__main__") return -1;
		if (b.id === "__main__") return 1;
		return a.id.localeCompare(b.id);
	});
	return reports;
}
function summarizeModules(modules) {
	const usage = toArray(modules.values()).map((module) => {
		const packages = toArray(module.packages.entries()).map(([packageId, files]) => {
			return {
				packageId,
				files: toArray(files).sort((a, b) => a.localeCompare(b))
			};
		}).sort((a, b) => {
			if (a.packageId === b.packageId) return 0;
			if (a.packageId === "__main__") return -1;
			if (b.packageId === "__main__") return 1;
			return a.packageId.localeCompare(b.packageId);
		});
		return {
			id: module.id,
			source: module.source,
			sourceType: module.sourceType,
			packages
		};
	});
	usage.sort((a, b) => a.source.localeCompare(b.source));
	return usage;
}
function expandVirtualModulePlacements(modules, packages, context) {
	for (const moduleEntry of modules.values()) {
		const virtualEntries = Array.from(moduleEntry.packages.entries()).filter(([packageId]) => packageId.startsWith("virtual:"));
		if (!virtualEntries.length) continue;
		const virtualFileBases = /* @__PURE__ */ new Map();
		for (const [virtualPackageId, files] of virtualEntries) {
			const combination = virtualPackageId.slice(8);
			if (!combination) continue;
			const segments = combination.split(/[_+]/).map((segment) => segment.trim()).filter(Boolean);
			if (!segments.length) continue;
			let matchingBases = virtualFileBases.get(virtualPackageId);
			if (!matchingBases) {
				matchingBases = Array.from(files).map((file) => posix.basename(file));
				virtualFileBases.set(virtualPackageId, matchingBases);
			}
			for (const root of segments) {
				if (!context.subPackageRoots.has(root)) continue;
				const targetPackage = packages.get(root);
				if (!targetPackage) continue;
				const moduleFiles = moduleEntry.packages.get(root) ?? /* @__PURE__ */ new Set();
				const targetFiles = Array.from(targetPackage.files.values()).filter((fileEntry) => {
					if (!matchingBases?.length) return true;
					const base = posix.basename(fileEntry.file);
					return matchingBases.includes(base);
				}).map((fileEntry) => fileEntry.file);
				if (targetFiles.length === 0) {
					const fallback = targetPackage.files.values().next().value;
					if (fallback) moduleFiles.add(fallback.file);
				} else for (const fileName of targetFiles) moduleFiles.add(fileName);
				if (moduleFiles.size > 0) moduleEntry.packages.set(root, moduleFiles);
			}
		}
	}
}
function summarizeSubPackages(metas) {
	const descriptors = metas.map((meta) => {
		return {
			root: meta.subPackage.root ?? "",
			independent: Boolean(meta.subPackage.independent),
			name: meta.subPackage.name
		};
	}).filter((descriptor) => descriptor.root);
	descriptors.sort((a, b) => a.root.localeCompare(b.root));
	return descriptors;
}
//#endregion
//#region src/analyze/subpackages/index.ts
async function analyzeSubpackages(ctx) {
	const { configService, scanService, buildService } = ctx;
	if (!configService || !scanService || !buildService) throw new Error("analyzeSubpackages 需要先初始化 configService、scanService 和 buildService。");
	await scanService.loadAppEntry();
	const subPackageMetas = scanService.loadSubPackages();
	const subPackageRoots = /* @__PURE__ */ new Set();
	const independentRoots = /* @__PURE__ */ new Set();
	for (const meta of subPackageMetas) {
		const root = meta.subPackage.root;
		if (root) {
			subPackageRoots.add(root);
			if (meta.subPackage.independent) independentRoots.add(root);
		}
	}
	const classifierContext = {
		subPackageRoots,
		independentRoots
	};
	const analysisConfig = configService.merge(void 0, createSharedBuildConfig(configService, scanService), { build: {
		write: false,
		watch: null
	} });
	const mainResult = await build(analysisConfig);
	const mainOutputs = Array.isArray(mainResult) ? mainResult : [mainResult];
	const packages = /* @__PURE__ */ new Map();
	const modules = /* @__PURE__ */ new Map();
	const componentJsonConfigs = [];
	for (const output of mainOutputs) {
		processOutput(output, "main", ctx, classifierContext, packages, modules);
		componentJsonConfigs.push(...collectAnalyzeComponentJsonConfigs(output));
	}
	for (const root of independentRoots) {
		const output = buildService.getIndependentOutput(root);
		processOutput(output, "independent", ctx, classifierContext, packages, modules);
		componentJsonConfigs.push(...collectAnalyzeComponentJsonConfigs(output));
	}
	expandVirtualModulePlacements(modules, packages, classifierContext);
	const subPackages = summarizeSubPackages(subPackageMetas);
	return {
		metadata: createAnalyzeMetadata(ctx.configService),
		packages: summarizePackages(packages),
		modules: summarizeModules(modules),
		subPackages,
		components: analyzeComponentUsage({
			jsonConfigs: componentJsonConfigs,
			subPackages
		}),
		glassEasel: createGlassEaselAnalyzeResult(ctx)
	};
}
//#endregion
//#region src/analyze/subpackages/history.ts
const jsonExtension = ".json";
function createSnapshotFileName(date = /* @__PURE__ */ new Date()) {
	return `${date.toISOString().replace(/[:.]/g, "-")}${jsonExtension}`;
}
function isAnalyzeResult(value) {
	return Boolean(value && typeof value === "object" && Array.isArray(value.packages) && Array.isArray(value.modules) && Array.isArray(value.subPackages));
}
async function listSnapshotFiles(dir) {
	try {
		return (await fs$2.readdir(dir, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(jsonExtension)).map((entry) => path.join(dir, entry.name)).sort((a, b) => b.localeCompare(a));
	} catch {
		return [];
	}
}
async function trimSnapshotFiles(dir, limit) {
	const staleFiles = (await listSnapshotFiles(dir)).slice(limit);
	await Promise.all(staleFiles.map(async (file) => {
		try {
			await fs$2.unlink(file);
		} catch {}
	}));
}
async function readLatestAnalyzeHistorySnapshot(configService) {
	const history = resolveAnalyzeHistoryMetadata(configService);
	if (!history.enabled) return null;
	const files = await listSnapshotFiles(history.dir);
	for (const file of files) try {
		const parsed = JSON.parse(await fs$2.readFile(file, "utf8"));
		if (isAnalyzeResult(parsed)) return parsed;
	} catch {}
	return null;
}
async function writeAnalyzeHistorySnapshot(result, configService, now = /* @__PURE__ */ new Date()) {
	const history = resolveAnalyzeHistoryMetadata(configService);
	if (!history.enabled) return;
	await fs$2.mkdir(history.dir, { recursive: true });
	const snapshotPath = path.join(history.dir, createSnapshotFileName(now));
	if (result.metadata) result.metadata = {
		...result.metadata,
		history: {
			...history,
			dir: configService.relativeCwd(history.dir),
			latestSnapshot: configService.relativeCwd(snapshotPath)
		}
	};
	await fs$2.writeFile(snapshotPath, `${JSON.stringify(result, null, 2)}\n`, "utf8");
	await trimSnapshotFiles(history.dir, history.limit);
	return snapshotPath;
}
//#endregion
//#region src/analyze/subpackages/report.ts
function formatAnalyzeBytes(bytes) {
	if (!bytes || Number.isNaN(bytes)) return "0 B";
	const units = [
		"B",
		"KB",
		"MB",
		"GB"
	];
	let value = bytes;
	let unitIndex = 0;
	while (value >= 1024 && unitIndex < units.length - 1) {
		value /= 1024;
		unitIndex++;
	}
	return `${value.toFixed(value >= 100 || unitIndex === 0 ? 0 : 2)} ${units[unitIndex]}`;
}
function formatDelta(bytes) {
	if (typeof bytes !== "number" || Number.isNaN(bytes) || bytes === 0) return "无变化";
	return `${bytes > 0 ? "+" : "-"}${formatAnalyzeBytes(Math.abs(bytes))}`;
}
function getFileSize(file) {
	return file.size ?? 0;
}
function getCompressedSize(file) {
	return file.brotliSize ?? file.gzipSize ?? 0;
}
function getBudgetLimit(type, budgets) {
	if (!budgets) return;
	if (type === "main") return budgets.mainBytes;
	if (type === "subPackage") return budgets.subPackageBytes;
	if (type === "independent") return budgets.independentBytes;
}
function createPackageSizeMap(result) {
	return new Map((result?.packages ?? []).map((pkg) => {
		const totalBytes = pkg.files.reduce((sum, file) => sum + getFileSize(file), 0);
		return [pkg.id, totalBytes];
	}));
}
function createFileKey(packageId, fileName) {
	return `${packageId}\u0000${fileName}`;
}
function createFileSizeMap(result) {
	const map = /* @__PURE__ */ new Map();
	for (const pkg of result?.packages ?? []) for (const file of pkg.files) map.set(createFileKey(pkg.id, file.file), getFileSize(file));
	return map;
}
function createPackageTypeMap(result) {
	return new Map(result.packages.map((pkg) => [pkg.id, pkg.type]));
}
function createModuleByteMap(result) {
	const map = /* @__PURE__ */ new Map();
	for (const pkg of result.packages) for (const file of pkg.files) for (const mod of file.modules ?? []) {
		const bytes = mod.bytes ?? mod.originalBytes ?? 0;
		map.set(mod.id, Math.max(map.get(mod.id) ?? 0, bytes));
	}
	return map;
}
function classifyIncrementCategory(source, sourceType) {
	if (source.includes("wevu") || source.includes("@weapp-vite/dashboard")) return "WeVu / runtime";
	if (sourceType === "node_modules" || source.includes("node_modules")) return "第三方依赖";
	if (sourceType === "workspace") return "工作区包";
	if (sourceType === "plugin") return "插件生成";
	if (source.endsWith(".wxss") || source.endsWith(".css") || source.endsWith(".scss")) return "样式资源";
	if (source.endsWith(".wxml") || source.endsWith(".json")) return "页面结构";
	return "业务源码";
}
function createIncrementAdvice(item) {
	if (item.category === "第三方依赖") return "检查依赖是否只在必要分包引用,必要时收敛到公共入口或替换轻量实现。";
	if (item.category === "WeVu / runtime") return "确认运行时能力是否被新页面引入,优先排查组件和 API 引用边界。";
	if (item.category === "样式资源") return "检查样式重复、原子类生成范围和组件样式裁剪。";
	if (item.type === "new-file" || item.type === "new-module") return "确认是否为本次需求必要新增,评估分包归属和懒加载边界。";
	return "对比本次变更,优先查看新增引用、共享模块和大对象常量。";
}
function createModuleSizeMap(result) {
	const map = /* @__PURE__ */ new Map();
	for (const pkg of result?.packages ?? []) for (const file of pkg.files) for (const module of file.modules ?? []) {
		const bytes = module.bytes ?? module.originalBytes ?? 0;
		const existing = map.get(module.id);
		if (existing && existing.bytes >= bytes) continue;
		map.set(module.id, {
			source: module.source,
			sourceType: module.sourceType,
			bytes,
			packageLabel: pkg.label,
			file: file.file
		});
	}
	return map;
}
function createDuplicateAdvice(sourceType, packageIds, packageTypeMap, estimatedSavingBytes) {
	if (packageIds.some((packageId) => packageTypeMap.get(packageId) === "independent")) return estimatedSavingBytes > 0 ? "含独立分包,先确认隔离要求,再评估是否抽公共入口。" : "含独立分包,重复可能来自隔离边界。";
	if (sourceType === "node_modules") return "依赖被多个包带入,检查引用边界或考虑主包公共入口。";
	if (sourceType === "src" || sourceType === "workspace") return "共享源码跨包重复,优先抽公共模块或调整分包归属。";
	if (sourceType === "plugin") return "插件生成内容跨包重复,检查插件产物输出策略。";
	return "检查该模块是否需要在多个包内重复存在。";
}
function formatBudgetStatus(item) {
	if (item.status === "ok") return "正常";
	return `${item.status === "exceeded" ? "超预算" : "接近预算"} ${(item.ratio * 100).toFixed(1)}%`;
}
function createActionItems(options) {
	const actions = [];
	const exceededItems = options.budgetItems.filter((item) => item.status === "exceeded");
	const warningItems = options.budgetItems.filter((item) => item.status === "warning");
	const topDuplicate = options.duplicateInsights.find((item) => item.estimatedSavingBytes > 0);
	if (exceededItems.length > 0) actions.push(`处理 ${exceededItems[0].label} 预算超限:当前 ${formatAnalyzeBytes(exceededItems[0].currentBytes)},限制 ${formatAnalyzeBytes(exceededItems[0].limitBytes)}。`);
	else if (warningItems.length > 0) actions.push(`关注 ${warningItems[0].label} 预算接近阈值:当前 ${(warningItems[0].ratio * 100).toFixed(1)}%。`);
	if (topDuplicate) actions.push(`优先处理重复模块 ${topDuplicate.source},估算可节省 ${formatAnalyzeBytes(topDuplicate.estimatedSavingBytes)}。`);
	if (actions.length === 0) actions.push("当前没有预算超限或高收益重复模块,保持观察即可。");
	return actions;
}
function createAnalyzeBudgetCheck(result) {
	const budgets = result.metadata?.budgets;
	if (!budgets) return [];
	const items = [];
	const totalBytes = result.packages.flatMap((pkg) => pkg.files).reduce((sum, file) => sum + getFileSize(file), 0);
	const warningRatio = budgets.warningRatio;
	const createItem = (options) => {
		const ratio = options.limitBytes > 0 ? options.currentBytes / options.limitBytes : 0;
		const status = ratio >= 1 ? "exceeded" : ratio >= warningRatio ? "warning" : "ok";
		return {
			...options,
			ratio,
			status
		};
	};
	items.push(createItem({
		id: "__total__",
		label: "总包",
		scope: "total",
		currentBytes: totalBytes,
		limitBytes: budgets.totalBytes
	}));
	for (const pkg of result.packages) {
		const limitBytes = getBudgetLimit(pkg.type, budgets);
		if (!limitBytes) continue;
		items.push(createItem({
			id: pkg.id,
			label: pkg.label,
			scope: pkg.type,
			currentBytes: pkg.files.reduce((sum, file) => sum + getFileSize(file), 0),
			limitBytes
		}));
	}
	return items.sort((a, b) => b.ratio - a.ratio || a.label.localeCompare(b.label));
}
function createDuplicateModuleInsights(result) {
	const moduleByteMap = createModuleByteMap(result);
	const packageTypeMap = createPackageTypeMap(result);
	return result.modules.filter((module) => module.packages.length > 1).map((module) => {
		const bytes = moduleByteMap.get(module.id) ?? 0;
		const packageIds = module.packages.map((pkg) => pkg.packageId);
		const estimatedSavingBytes = bytes * Math.max(module.packages.length - 1, 0);
		return {
			id: module.id,
			source: module.source,
			sourceType: module.sourceType,
			packageCount: module.packages.length,
			bytes,
			estimatedSavingBytes,
			packages: packageIds,
			advice: createDuplicateAdvice(module.sourceType, packageIds, packageTypeMap, estimatedSavingBytes)
		};
	}).sort((a, b) => b.estimatedSavingBytes - a.estimatedSavingBytes || b.packageCount - a.packageCount || a.source.localeCompare(b.source));
}
function createAnalyzeIncrementAttribution(result, previousResult) {
	if (!previousResult) return [];
	const previousFiles = createFileSizeMap(previousResult);
	const previousModules = createModuleSizeMap(previousResult);
	const currentModules = createModuleSizeMap(result);
	const items = [];
	for (const pkg of result.packages) for (const file of pkg.files) {
		const currentBytes = getFileSize(file);
		const previousBytes = previousFiles.get(createFileKey(pkg.id, file.file)) ?? 0;
		const deltaBytes = currentBytes - previousBytes;
		if (deltaBytes <= 0) continue;
		const type = previousBytes > 0 ? "increased-file" : "new-file";
		const item = {
			key: `file:${pkg.id}:${file.file}`,
			type,
			label: file.file,
			category: classifyIncrementCategory(file.source ?? file.file),
			packageLabel: pkg.label,
			file: file.file,
			currentBytes,
			previousBytes,
			deltaBytes,
			advice: ""
		};
		items.push({
			...item,
			advice: createIncrementAdvice(item)
		});
	}
	for (const [id, module] of currentModules) {
		const previousBytes = previousModules.get(id)?.bytes ?? 0;
		const deltaBytes = module.bytes - previousBytes;
		if (deltaBytes <= 0) continue;
		const type = previousBytes > 0 ? "increased-module" : "new-module";
		const item = {
			key: `module:${id}`,
			type,
			label: module.source,
			category: classifyIncrementCategory(module.source, module.sourceType),
			packageLabel: module.packageLabel,
			file: module.file,
			currentBytes: module.bytes,
			previousBytes,
			deltaBytes,
			advice: ""
		};
		items.push({
			...item,
			advice: createIncrementAdvice(item)
		});
	}
	return items.sort((a, b) => b.deltaBytes - a.deltaBytes || a.category.localeCompare(b.category) || a.label.localeCompare(b.label));
}
function createAnalyzeIncrementCategorySummary(items) {
	const map = /* @__PURE__ */ new Map();
	for (const item of items) {
		const entry = map.get(item.category) ?? {
			category: item.category,
			count: 0,
			deltaBytes: 0
		};
		entry.count += 1;
		entry.deltaBytes += item.deltaBytes;
		map.set(item.category, entry);
	}
	return [...map.values()].sort((a, b) => b.deltaBytes - a.deltaBytes || b.count - a.count || a.category.localeCompare(b.category));
}
function createAnalyzePrMarkdownReport(result, previousResult) {
	const files = result.packages.flatMap((pkg) => pkg.files.map((file) => ({
		pkg,
		file
	})));
	const totalBytes = files.reduce((sum, item) => sum + getFileSize(item.file), 0);
	const compressedBytes = files.reduce((sum, item) => sum + getCompressedSize(item.file), 0);
	const previousTotalBytes = previousResult?.packages.flatMap((pkg) => pkg.files).reduce((sum, file) => sum + getFileSize(file), 0);
	const incrementItems = createAnalyzeIncrementAttribution(result, previousResult);
	const incrementSummary = createAnalyzeIncrementCategorySummary(incrementItems);
	const duplicateInsights = createDuplicateModuleInsights(result);
	const budgetItems = createAnalyzeBudgetCheck(result);
	const budgetIssues = budgetItems.filter((item) => item.status !== "ok");
	const actionItems = createActionItems({
		budgetItems,
		duplicateInsights
	}).slice(0, 3);
	const budgetRows = budgetIssues.slice(0, 5).map((item) => `| ${item.label} | ${formatAnalyzeBytes(item.currentBytes)} | ${formatAnalyzeBytes(item.limitBytes)} | ${formatBudgetStatus(item)} |`).join("\n");
	const incrementRows = incrementItems.slice(0, 8).map((item) => `| ${item.label} | ${item.category} | ${item.packageLabel} | ${formatAnalyzeBytes(item.deltaBytes)} | ${item.advice} |`).join("\n");
	const sourceRows = incrementSummary.slice(0, 6).map((item) => `| ${item.category} | ${item.count} | ${formatAnalyzeBytes(item.deltaBytes)} |`).join("\n");
	const duplicateRows = duplicateInsights.slice(0, 5).map((module) => `| ${module.source} | ${module.packageCount} | ${formatAnalyzeBytes(module.estimatedSavingBytes)} | ${module.advice} |`).join("\n");
	return [
		"## weapp-vite analyze PR 摘要",
		"",
		`- 总产物体积:${formatAnalyzeBytes(totalBytes)}(较上次 ${formatDelta(typeof previousTotalBytes === "number" ? totalBytes - previousTotalBytes : void 0)})`,
		`- 压缩后体积:${formatAnalyzeBytes(compressedBytes)}`,
		`- 预算告警:${budgetIssues.length}`,
		`- 增量归因:${incrementItems.length > 0 ? `${incrementItems.length} 项正向增长` : "无正向增长"}`,
		`- 跨包复用:${duplicateInsights.length}`,
		"",
		"### 建议动作",
		"",
		...actionItems.map((item) => `- ${item}`),
		"",
		"### 预算状态",
		"",
		"| 对象 | 当前体积 | 预算 | 状态 |",
		"| --- | ---: | ---: | --- |",
		budgetRows || "| - | 0 B | 0 B | 正常 |",
		"",
		"### 增量来源",
		"",
		"| 来源 | 项数 | 增量 |",
		"| --- | ---: | ---: |",
		sourceRows || "| - | 0 | 0 B |",
		"",
		"### Top 增量",
		"",
		"| 文件/模块 | 来源 | 包 | 增量 | 建议 |",
		"| --- | --- | --- | ---: | --- |",
		incrementRows || "| - | - | - | 0 B | - |",
		"",
		"### 重复模块",
		"",
		"| 模块 | 包数量 | 估算可节省 | 建议 |",
		"| --- | ---: | ---: | --- |",
		duplicateRows || "| - | 0 | 0 B | - |",
		""
	].join("\n");
}
function createAnalyzeMarkdownReport(result, previousResult) {
	const files = result.packages.flatMap((pkg) => pkg.files.map((file) => ({
		pkg,
		file
	})));
	const totalBytes = files.reduce((sum, item) => sum + getFileSize(item.file), 0);
	const compressedBytes = files.reduce((sum, item) => sum + getCompressedSize(item.file), 0);
	const duplicateInsights = createDuplicateModuleInsights(result);
	const incrementItems = createAnalyzeIncrementAttribution(result, previousResult);
	const incrementSummary = createAnalyzeIncrementCategorySummary(incrementItems);
	const budgetItems = createAnalyzeBudgetCheck(result);
	const previousTotalBytes = previousResult?.packages.flatMap((pkg) => pkg.files).reduce((sum, file) => sum + getFileSize(file), 0);
	const previousPackageSizes = createPackageSizeMap(previousResult);
	const budgets = result.metadata?.budgets;
	const budgetIssues = budgetItems.filter((item) => item.status !== "ok");
	const actionItems = createActionItems({
		budgetItems,
		duplicateInsights
	});
	const packageRows = result.packages.map((pkg) => {
		const size = pkg.files.reduce((sum, file) => sum + getFileSize(file), 0);
		const compressed = pkg.files.reduce((sum, file) => sum + getCompressedSize(file), 0);
		const previousSize = previousPackageSizes.get(pkg.id);
		const budgetStatus = budgetItems.find((item) => item.id === pkg.id);
		return `| ${pkg.label} | ${pkg.type} | ${formatAnalyzeBytes(size)} | ${formatAnalyzeBytes(compressed)} | ${formatDelta(typeof previousSize === "number" ? size - previousSize : void 0)} | ${budgetStatus ? formatBudgetStatus(budgetStatus) : "正常"} |`;
	}).join("\n");
	const topFileRows = files.sort((a, b) => getFileSize(b.file) - getFileSize(a.file) || a.file.file.localeCompare(b.file.file)).slice(0, 10).map((item) => `| ${item.file.file} | ${item.pkg.label} | ${item.file.type} | ${formatAnalyzeBytes(getFileSize(item.file))} | ${formatAnalyzeBytes(getCompressedSize(item.file))} |`).join("\n");
	const duplicateRows = duplicateInsights.slice(0, 10).map((module) => `| ${module.source} | ${module.sourceType} | ${module.packageCount} | ${formatAnalyzeBytes(module.estimatedSavingBytes)} | ${module.advice} |`).join("\n");
	const budgetRows = budgetIssues.map((item) => `| ${item.label} | ${item.scope} | ${formatAnalyzeBytes(item.currentBytes)} | ${formatAnalyzeBytes(item.limitBytes)} | ${formatBudgetStatus(item)} |`).join("\n");
	const incrementRows = incrementItems.slice(0, 10).map((item) => `| ${item.label} | ${item.category} | ${item.packageLabel} | ${formatAnalyzeBytes(item.deltaBytes)} | ${item.advice} |`).join("\n");
	const incrementSummaryRows = incrementSummary.slice(0, 8).map((item) => `| ${item.category} | ${item.count} | ${formatAnalyzeBytes(item.deltaBytes)} |`).join("\n");
	return [
		"# weapp-vite analyze 报告",
		"",
		`生成时间:${result.metadata?.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString()}`,
		"",
		"## 本次变化摘要",
		"",
		`- 总产物体积:${formatAnalyzeBytes(totalBytes)}`,
		`- 压缩后体积:${formatAnalyzeBytes(compressedBytes)}`,
		`- 较上次:${formatDelta(typeof previousTotalBytes === "number" ? totalBytes - previousTotalBytes : void 0)}`,
		`- 包体数量:${result.packages.length}`,
		`- 源码模块:${result.modules.length}`,
		`- 跨包复用:${duplicateInsights.length}`,
		`- 预算来源:${budgets?.source === "config" ? "配置" : "默认"}`,
		"",
		"## 预算告警",
		"",
		"| 对象 | 范围 | 当前体积 | 预算 | 状态 |",
		"| --- | --- | ---: | ---: | --- |",
		budgetRows || "| - | - | 0 B | 0 B | 正常 |",
		"",
		"## 建议动作",
		"",
		...actionItems.map((item) => `- ${item}`),
		"",
		"## 增量归因",
		"",
		"| 来源 | 项数 | 增量 |",
		"| --- | ---: | ---: |",
		incrementSummaryRows || "| - | 0 | 0 B |",
		"",
		"| 文件/模块 | 来源 | 包 | 增量 | 建议 |",
		"| --- | --- | --- | ---: | --- |",
		incrementRows || "| - | - | - | 0 B | - |",
		"",
		"## 包体预算",
		"",
		"| 包 | 类型 | 体积 | 压缩后 | 较上次 | 预算 |",
		"| --- | --- | ---: | ---: | ---: | --- |",
		packageRows || "| - | - | 0 B | 0 B | 无变化 | 正常 |",
		"",
		"## Top 文件",
		"",
		"| 文件 | 包 | 类型 | 体积 | 压缩后 |",
		"| --- | --- | --- | ---: | ---: |",
		topFileRows || "| - | - | - | 0 B | 0 B |",
		"",
		"## 重复模块",
		"",
		"| 模块 | 来源 | 包数量 | 估算可节省 | 建议 |",
		"| --- | --- | ---: | ---: | --- |",
		duplicateRows || "| - | - | 0 | 0 B | - |",
		""
	].join("\n");
}
//#endregion
//#region src/cli/analyze/dashboard.ts
const ANALYZE_GLOBAL_KEY = "__WEAPP_VITE_ANALYZE_RESULT__";
const PREVIOUS_ANALYZE_GLOBAL_KEY = "__WEAPP_VITE_PREVIOUS_ANALYZE_RESULT__";
const DASHBOARD_EVENTS_GLOBAL_KEY = "__WEAPP_VITE_DASHBOARD_EVENTS__";
const ANALYZE_DASHBOARD_PACKAGE_NAME = "@weapp-vite/dashboard";
const ANALYZE_SSE_PATH = "/__weapp_vite_analyze";
const FILE_CONTENT_PATH = "/__weapp_vite_file_content";
const DASHBOARD_EVENT_NAME = "weapp-dashboard:event";
const MAX_FILE_CONTENT_BYTES = 2097152;
const require = createRequire(import.meta.url);
function createInstallCommand(agent) {
	const resolved = resolveCommand(agent ?? "npm", "install", [ANALYZE_DASHBOARD_PACKAGE_NAME]);
	if (!resolved) return `npm install ${ANALYZE_DASHBOARD_PACKAGE_NAME}`;
	return `${resolved.command} ${resolved.args.join(" ")}`;
}
function formatEventTimestamp(date = /* @__PURE__ */ new Date()) {
	return date.toLocaleTimeString("zh-CN", { hour12: false });
}
function createDashboardRuntimeEvent(input) {
	return {
		id: `dashboard:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`,
		kind: input.kind,
		level: input.level,
		title: input.title,
		detail: input.detail,
		timestamp: formatEventTimestamp(),
		source: input.source ?? "weapp-vite",
		durationMs: input.durationMs,
		tags: input.tags,
		profile: input.profile
	};
}
function readDashboardManifest(packageJsonPath) {
	try {
		return parseCommentJson(fs.readFileSync(packageJsonPath, "utf8"));
	} catch {
		return;
	}
}
function resolveDashboardDistRoot(packageRoot, manifest) {
	const distDir = manifest?.weappViteDashboard?.distDir ?? "dist";
	const distRoot = path.resolve(packageRoot, distDir);
	if (!fs.existsSync(distRoot)) return;
	return { root: distRoot };
}
function resolveDashboardDevRoot(packageRoot, manifest) {
	const devRoot = manifest?.weappViteDashboard?.devRoot;
	const devConfigFile = manifest?.weappViteDashboard?.devConfigFile;
	if (!devRoot || !devConfigFile) return;
	const root = path.resolve(packageRoot, devRoot);
	const configFile = path.resolve(root, devConfigFile);
	if (!fs.existsSync(root) || !fs.existsSync(configFile)) return;
	return {
		root,
		configFile
	};
}
function resolveDashboardRoot(options) {
	const resolvePaths = options?.cwd && options.cwd !== process.cwd() ? [options.cwd, process.cwd()] : options?.cwd ? [options.cwd] : void 0;
	let dashboardPackageRoot;
	let dashboardManifest;
	try {
		const dashboardPackageJsonPath = require.resolve(`${ANALYZE_DASHBOARD_PACKAGE_NAME}/package.json`, { paths: resolvePaths });
		dashboardPackageRoot = path.dirname(dashboardPackageJsonPath);
		dashboardManifest = readDashboardManifest(dashboardPackageJsonPath);
	} catch {
		dashboardPackageRoot = void 0;
		dashboardManifest = void 0;
	}
	if (dashboardPackageRoot) {
		const devResolved = resolveDashboardDevRoot(dashboardPackageRoot, dashboardManifest);
		if (devResolved) return devResolved;
		const distResolved = resolveDashboardDistRoot(dashboardPackageRoot, dashboardManifest);
		if (distResolved) return distResolved;
	}
	logger_default.warn(`[weapp-vite ui] 未安装可选仪表盘包 ${colors.bold(colors.green(ANALYZE_DASHBOARD_PACKAGE_NAME))},已自动降级关闭 dashboard 能力。`);
	logger_default.info(`如需启用,请执行 ${colors.bold(colors.green(createInstallCommand(options?.packageManagerAgent)))}`);
}
function normalizeDashboardRelativePath(value) {
	return value.replaceAll("\\", "/");
}
function stripDashboardFileQuery(value) {
	const queryIndex = value.indexOf("?");
	return queryIndex === -1 ? value : value.slice(0, queryIndex);
}
function addDashboardAllowedPath(paths, value) {
	if (!value || value.includes("\0")) return;
	const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(value));
	if (!normalizedPath || path.isAbsolute(normalizedPath)) return;
	paths.add(normalizedPath);
}
function createDashboardContentAllowlist(result) {
	const artifactPaths = /* @__PURE__ */ new Set();
	const sourcePaths = /* @__PURE__ */ new Set();
	for (const packageReport of result.packages) for (const file of packageReport.files) {
		addDashboardAllowedPath(artifactPaths, file.file);
		addDashboardAllowedPath(sourcePaths, file.source);
		for (const module of file.modules ?? []) addDashboardAllowedPath(sourcePaths, module.source);
	}
	return {
		artifactPaths,
		sourcePaths
	};
}
function resolveDashboardContentPath(root, requestPath, options) {
	if (!root || !requestPath || requestPath.includes("\0")) return;
	const normalizedRequestPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
	if (path.isAbsolute(normalizedRequestPath)) return;
	if (!options.allowedPaths.has(normalizedRequestPath)) return;
	const resolvedRoot = path.resolve(root);
	const absolutePath = path.resolve(resolvedRoot, normalizedRequestPath);
	const relativePath = path.relative(resolvedRoot, absolutePath);
	if (!relativePath) return;
	if (!options.allowParent && (relativePath.startsWith("..") || path.isAbsolute(relativePath))) return;
	return {
		absolutePath,
		relativePath: options.allowParent ? normalizedRequestPath : normalizeDashboardRelativePath(relativePath)
	};
}
function resolveDashboardFileLanguage(filePath) {
	const extension = path.extname(filePath).toLowerCase();
	if (extension === ".js" || extension === ".mjs" || extension === ".cjs" || extension === ".wxs" || extension === ".sjs") return "javascript";
	if (extension === ".ts" || extension === ".mts" || extension === ".cts") return "typescript";
	if (extension === ".json" || extension === ".map") return "json";
	if (extension === ".css" || extension === ".wxss" || extension === ".scss" || extension === ".sass" || extension === ".less") return "css";
	if (extension === ".vue" || extension === ".wxml" || extension === ".html") return "html";
	return "plaintext";
}
function sendDashboardJson(res, statusCode, payload) {
	res.statusCode = statusCode;
	res.setHeader("Content-Type", "application/json; charset=utf-8");
	res.end(JSON.stringify(payload));
}
async function sendDashboardFileContent(res, roots, allowlist, kind, requestPath) {
	const resolved = resolveDashboardContentPath(kind === "artifact" ? roots.artifactRoot : kind === "source" ? roots.sourceRoot : void 0, requestPath, {
		allowParent: kind === "source",
		allowedPaths: kind === "artifact" ? allowlist.artifactPaths : allowlist.sourcePaths
	});
	if (!resolved || kind !== "source" && kind !== "artifact") {
		sendDashboardJson(res, 400, {
			error: "invalid_request",
			message: "必须传入合法的 kind 和相对路径。"
		});
		return;
	}
	try {
		const stat = await fs.promises.stat(resolved.absolutePath);
		if (!stat.isFile()) {
			sendDashboardJson(res, 400, {
				error: "not_file",
				message: "目标路径不是文件。"
			});
			return;
		}
		if (stat.size > MAX_FILE_CONTENT_BYTES) {
			sendDashboardJson(res, 413, {
				error: "file_too_large",
				message: `文件超过 ${MAX_FILE_CONTENT_BYTES} 字节,已拒绝读取。`
			});
			return;
		}
		sendDashboardJson(res, 200, {
			kind,
			path: resolved.relativePath,
			language: resolveDashboardFileLanguage(resolved.relativePath),
			size: stat.size,
			content: await fs.promises.readFile(resolved.absolutePath, "utf8")
		});
	} catch (error) {
		const code = typeof error === "object" && error && "code" in error ? String(error.code) : "";
		sendDashboardJson(res, code === "ENOENT" ? 404 : 500, {
			error: code === "ENOENT" ? "not_found" : "read_failed",
			message: code === "ENOENT" ? "文件不存在。" : "读取文件失败。"
		});
	}
}
function createAnalyzeHtmlPlugin(state, runtimeEvents, contentRoots, contentAllowlist, onServerInstance, onBroadcastReady) {
	const sseClients = /* @__PURE__ */ new Set();
	const hotBridgeScript = `
    const applyAnalyzePayload = (payload) => {
      const current = payload?.current ?? payload
      const previous = payload?.previous ?? window.${PREVIOUS_ANALYZE_GLOBAL_KEY} ?? null
      window.${ANALYZE_GLOBAL_KEY} = current
      window.${PREVIOUS_ANALYZE_GLOBAL_KEY} = previous
      window.dispatchEvent(new CustomEvent('weapp-analyze:update', { detail: { current, previous } }))
    }
    const applyDashboardEvents = (payload) => {
      const events = Array.isArray(payload) ? payload : [payload]
      const nextEvents = events.filter(Boolean)
      if (nextEvents.length === 0) {
        return
      }
      window.${DASHBOARD_EVENTS_GLOBAL_KEY} = [
        ...(window.${DASHBOARD_EVENTS_GLOBAL_KEY} ?? []),
        ...nextEvents,
      ]
      window.dispatchEvent(new CustomEvent('${DASHBOARD_EVENT_NAME}', { detail: nextEvents }))
    }
    const source = new EventSource('${ANALYZE_SSE_PATH}')
    source.onmessage = (event) => {
      try {
        applyAnalyzePayload(JSON.parse(event.data))
      }
      catch {}
    }
    if (import.meta.hot) {
      import.meta.hot.on('weapp-analyze:update', (payload) => {
        applyAnalyzePayload(payload)
      })
      import.meta.hot.on('${DASHBOARD_EVENT_NAME}', (payload) => {
        applyDashboardEvents(payload)
      })
    }
  `.trim();
	const broadcast = (payload) => {
		const serialized = `data: ${JSON.stringify(payload)}\n\n`;
		for (const client of sseClients) client.write(serialized);
	};
	onBroadcastReady(broadcast);
	return {
		name: "weapp-vite-analyze-html",
		transformIndexHtml(html) {
			return {
				html,
				tags: [
					{
						tag: "script",
						children: `window.${ANALYZE_GLOBAL_KEY} = ${JSON.stringify(state.current)}`,
						injectTo: "head-prepend"
					},
					{
						tag: "script",
						children: `window.${PREVIOUS_ANALYZE_GLOBAL_KEY} = ${JSON.stringify(state.previous)}`,
						injectTo: "head-prepend"
					},
					{
						tag: "script",
						children: `window.${DASHBOARD_EVENTS_GLOBAL_KEY} = ${JSON.stringify(runtimeEvents.current)}`,
						injectTo: "head-prepend"
					},
					{
						tag: "script",
						attrs: {
							type: "module",
							src: "/@vite/client"
						},
						injectTo: "head"
					},
					{
						tag: "script",
						attrs: { type: "module" },
						children: hotBridgeScript,
						injectTo: "body"
					}
				]
			};
		},
		configureServer(server) {
			onServerInstance(server);
			server.middlewares.use((req, res, next) => {
				const url = new URL(req.url ?? "/", "http://127.0.0.1");
				if (url.pathname === FILE_CONTENT_PATH) {
					sendDashboardFileContent(res, contentRoots, contentAllowlist.current, url.searchParams.get("kind"), url.searchParams.get("path"));
					return;
				}
				if (url.pathname !== ANALYZE_SSE_PATH) {
					next();
					return;
				}
				res.statusCode = 200;
				res.setHeader("Content-Type", "text/event-stream");
				res.setHeader("Cache-Control", "no-cache, no-transform");
				res.setHeader("Connection", "keep-alive");
				res.write(`data: ${JSON.stringify(state)}\n\n`);
				sseClients.add(res);
				req.on("close", () => {
					sseClients.delete(res);
				});
			});
		}
	};
}
async function waitForServerExit(server) {
	let resolved = false;
	const cleanup = async () => {
		if (resolved) return;
		resolved = true;
		try {
			await server.close();
		} catch (error) {
			logger_default.error(error);
		}
	};
	const signals = ["SIGINT", "SIGTERM"];
	await new Promise((resolvePromise) => {
		const resolveOnce = async () => {
			await cleanup();
			signals.forEach((signal) => {
				process.removeListener(signal, resolveOnce);
			});
			resolvePromise();
		};
		signals.forEach((signal) => {
			process.once(signal, resolveOnce);
		});
		server.httpServer?.once("close", resolveOnce);
	});
}
async function startAnalyzeDashboard(result, options) {
	const resolved = resolveDashboardRoot(options);
	if (!resolved) return;
	const { root, configFile } = resolved;
	const state = {
		current: result,
		previous: options?.previousResult ?? null
	};
	const contentAllowlist = { current: createDashboardContentAllowlist(result) };
	const runtimeEvents = { current: [createDashboardRuntimeEvent({
		kind: "command",
		level: "success",
		title: options?.watch ? "dashboard watch session started" : "dashboard static session started",
		detail: options?.watch ? "weapp-vite UI 已进入实时分析模式,后续 analyze 结果会继续推送到 dashboard。" : "weapp-vite UI 已进入静态分析模式,当前页面展示的是一次性分析结果。",
		tags: options?.watch ? ["watch", "analyze"] : ["static", "analyze"]
	}), ...(options?.initialEvents ?? []).map((event) => createDashboardRuntimeEvent(event))] };
	let serverRef;
	let broadcastAnalyzeResult;
	const plugins = [createAnalyzeHtmlPlugin(state, runtimeEvents, {
		artifactRoot: options?.artifactRoot ?? (options?.cwd ? path.resolve(options.cwd, "dist") : void 0),
		sourceRoot: options?.cwd
	}, contentAllowlist, (server) => {
		serverRef = server;
	}, (broadcast) => {
		broadcastAnalyzeResult = broadcast;
	})];
	const serverOptions = {
		root,
		configFile: configFile ?? false,
		clearScreen: false,
		appType: "spa",
		publicDir: false,
		plugins,
		server: {
			host: "127.0.0.1",
			port: 0,
			watch: { ignored: ["**/*"] }
		},
		logLevel: "error"
	};
	const server = await createServer(serverOptions);
	const requestedPort = typeof serverOptions.server?.port === "number" ? serverOptions.server.port : void 0;
	await server.listen(requestedPort);
	serverRef ??= server;
	server.printUrls();
	const urls = (() => {
		const resolved = server.resolvedUrls;
		if (!resolved) return [];
		return [...resolved.local ?? [], ...resolved.network ?? []];
	})();
	const waitPromise = waitForServerExit(server);
	if (serverRef?.ws) {
		serverRef.ws.send({
			type: "custom",
			event: "weapp-analyze:update",
			data: state
		});
		serverRef.ws.send({
			type: "custom",
			event: DASHBOARD_EVENT_NAME,
			data: runtimeEvents.current
		});
	}
	broadcastAnalyzeResult?.(state);
	const emitRuntimeEvents = (events) => {
		if (events.length === 0) return;
		const nextEvents = events.map((event) => createDashboardRuntimeEvent(event));
		runtimeEvents.current = [...nextEvents, ...runtimeEvents.current].slice(0, 24);
		if (serverRef) serverRef.ws.send({
			type: "custom",
			event: DASHBOARD_EVENT_NAME,
			data: nextEvents
		});
	};
	const handle = {
		async update(nextResult, previousResult) {
			state.previous = previousResult ?? state.current;
			state.current = nextResult;
			contentAllowlist.current = createDashboardContentAllowlist(nextResult);
			emitRuntimeEvents([{
				kind: "build",
				level: "info",
				title: "analyze payload refreshed",
				detail: `已推送新的 analyze 结果,当前包含 ${nextResult.packages.length} 个包与 ${nextResult.modules.length} 个模块。`,
				tags: ["analyze", "refresh"]
			}]);
			if (serverRef) serverRef.ws.send({
				type: "custom",
				event: "weapp-analyze:update",
				data: state
			});
			broadcastAnalyzeResult?.(state);
		},
		emitRuntimeEvents,
		waitForExit: () => waitPromise,
		close: async () => {
			await server.close();
		},
		urls
	};
	if (options?.watch) {
		if (!options.silentStartupLog) {
			logger_default.info("weapp-vite UI 已启动(分析视图,实时模式),按 Ctrl+C 退出。");
			for (const url of handle.urls) logger_default.info(`  ➜  ${colors.bold(colors.cyan(url))}`);
		}
		return handle;
	}
	if (!options?.silentStartupLog) {
		logger_default.info("weapp-vite UI 已启动(分析视图,静态模式),按 Ctrl+C 退出。");
		for (const url of handle.urls) logger_default.info(`  ➜  ${colors.bold(colors.cyan(url))}`);
	}
	await waitPromise;
}
//#endregion
//#region src/cli/processCleanup.ts
function isSassEmbeddedChild(handle) {
	if (!handle || typeof handle !== "object") return false;
	const child = handle;
	const spawnfile = typeof child.spawnfile === "string" ? child.spawnfile : "";
	const spawnargs = Array.isArray(child.spawnargs) ? child.spawnargs : [];
	return Boolean("kill" in handle && "spawnfile" in handle && (spawnfile.includes("sass-embedded") || spawnfile.includes("dart-sass") && spawnargs.includes("--embedded")));
}
function terminateStaleSassEmbeddedProcess() {
	const getHandles = process._getActiveHandles;
	const handles = typeof getHandles === "function" ? getHandles() : void 0;
	if (!Array.isArray(handles)) return;
	for (const handle of handles) if (isSassEmbeddedChild(handle)) try {
		handle.kill();
	} catch {}
}
//#endregion
//#region src/cli/commands/analyze.ts
function normalizeDisplayPath(value) {
	return value || ".";
}
function getDefaultWebAnalyzeScopes() {
	return {
		supported: [
			"weapp.web 配置解析(enable/root/srcDir/outDir)",
			"runtime.executionMode 静态解析(compat/safe/strict)",
			"JSON 报告输出(--json/--output)"
		],
		unsupported: [
			"分包产物体积分析(仅小程序)",
			"源码模块包体映射(仅小程序)",
			"分析仪表盘(dashboard)"
		]
	};
}
function createWebAnalyzeResult(configService, options) {
	const webConfig = configService.weappWebConfig;
	const executionMode = webConfig?.pluginOptions.runtime?.executionMode ?? "compat";
	const scope = getDefaultWebAnalyzeScopes();
	const limitations = ["当前仅提供静态配置分析,不执行 Web 产物扫描。"];
	if (!webConfig?.enabled) limitations.push("未检测到启用的 weapp.web 配置。");
	return {
		runtime: "web",
		platform: options.platform,
		mode: configService.mode,
		generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
		experimental: true,
		configFile: configService.configFilePath ? normalizeDisplayPath(configService.relativeCwd(configService.configFilePath)) : void 0,
		web: {
			enabled: Boolean(webConfig?.enabled),
			root: webConfig?.root ? normalizeDisplayPath(configService.relativeCwd(webConfig.root)) : void 0,
			srcDir: webConfig?.srcDir,
			outDir: webConfig?.outDir ? normalizeDisplayPath(configService.relativeCwd(webConfig.outDir)) : void 0,
			executionMode
		},
		supportedScopes: scope.supported,
		unsupportedScopes: scope.unsupported,
		limitations
	};
}
function printAnalysisSummary(result) {
	const packageLabelMap = /* @__PURE__ */ new Map();
	const packageModuleSet = /* @__PURE__ */ new Map();
	for (const pkg of result.packages) packageLabelMap.set(pkg.id, pkg.label);
	for (const module of result.modules) for (const pkgRef of module.packages) {
		const set = packageModuleSet.get(pkgRef.packageId) ?? /* @__PURE__ */ new Set();
		set.add(module.id);
		packageModuleSet.set(pkgRef.packageId, set);
	}
	logger_default.success("分包分析完成");
	for (const pkg of result.packages) {
		const chunkCount = pkg.files.filter((file) => file.type === "chunk").length;
		const assetCount = pkg.files.length - chunkCount;
		const moduleCount = packageModuleSet.get(pkg.id)?.size ?? 0;
		logger_default.info(`- ${pkg.label}:${chunkCount} 个模块产物,${assetCount} 个资源,覆盖 ${moduleCount} 个源码模块`);
	}
	if (result.subPackages.length > 0) {
		logger_default.info("分包配置:");
		for (const descriptor of result.subPackages) {
			const segments = [descriptor.root];
			if (descriptor.name) segments.push(`别名:${descriptor.name}`);
			if (descriptor.independent) segments.push("独立构建");
			logger_default.info(`- ${segments.join(",")}`);
		}
	}
	const componentUsages = result.components ?? [];
	if (componentUsages.length > 0) {
		const suggestions = componentUsages.flatMap((component) => component.suggestions);
		logger_default.info(`组件依赖:${componentUsages.length} 个组件,${suggestions.length} 条分包优化建议`);
		for (const suggestion of suggestions.slice(0, 5)) logger_default.info(`- ${suggestion.message}`);
		if (suggestions.length > 5) logger_default.info(`- …其余 ${suggestions.length - 5} 条组件建议请使用 ${colors.bold(colors.green("weapp-vite analyze --json"))} 查看`);
	}
	const duplicates = result.modules.filter((module) => module.packages.length > 1);
	if (duplicates.length === 0) {
		logger_default.info("未检测到跨包复用的源码模块。");
		return;
	}
	logger_default.info(`跨包复用/复制源码共 ${duplicates.length} 项:`);
	const limit = 10;
	const entries = duplicates.slice(0, limit);
	for (const module of entries) {
		const placements = module.packages.map((pkgRef) => {
			return `${packageLabelMap.get(pkgRef.packageId) ?? pkgRef.packageId} → ${pkgRef.files.join(", ")}`;
		}).join(";");
		logger_default.info(`- ${module.source} (${module.sourceType}):${placements}`);
	}
	if (duplicates.length > limit) logger_default.info(`- …其余 ${duplicates.length - limit} 项请使用 ${colors.bold(colors.green("weapp-vite analyze --json"))} 查看`);
}
function printBudgetCheckSummary(result) {
	const exceededItems = createAnalyzeBudgetCheck(result).filter((item) => item.status === "exceeded");
	if (exceededItems.length === 0) {
		logger_default.success("包体预算检查通过");
		return false;
	}
	logger_default.error(`包体预算检查失败:${exceededItems.length} 项超限`);
	for (const item of exceededItems) logger_default.error(`- ${item.label}:${formatAnalyzeBytes(item.currentBytes)} / ${formatAnalyzeBytes(item.limitBytes)} (${(item.ratio * 100).toFixed(1)}%)`);
	return true;
}
function printGlassEaselCheckSummary(result) {
	const { glassEasel } = result;
	if (!glassEasel.detected) {
		logger_default.info("未检测到 glass-easel 配置。");
		return false;
	}
	if (glassEasel.diagnostics.length === 0) {
		logger_default.success("glass-easel 迁移检查通过");
		return false;
	}
	logger_default.info(`glass-easel 迁移检查:${glassEasel.summary.errors} 个错误,${glassEasel.summary.warnings} 个警告`);
	for (const diagnostic of glassEasel.diagnostics) {
		const location = diagnostic.line ? `:${diagnostic.line}:${diagnostic.column}` : "";
		const message = `[${diagnostic.code}] ${diagnostic.file}${location} ${diagnostic.message}`;
		if (diagnostic.severity === "error") logger_default.error(message);
		else logger_default.warn(message);
	}
	return glassEasel.summary.errors > 0;
}
function printWebAnalysisSummary(result) {
	logger_default.success("Web 静态分析完成");
	logger_default.info(`- 配置状态:${result.web.enabled ? "已启用 weapp.web" : "未启用 weapp.web"}`);
	if (result.web.enabled) {
		logger_default.info(`- root:${result.web.root ?? "."}`);
		logger_default.info(`- srcDir:${result.web.srcDir ?? "."}`);
		logger_default.info(`- outDir:${result.web.outDir ?? "dist/web"}`);
	}
	logger_default.info(`- executionMode:${result.web.executionMode}`);
	logger_default.info(`- 支持范围:${result.supportedScopes.join(";")}`);
	logger_default.warn(`- 未支持范围:${result.unsupportedScopes.join(";")}`);
	for (const limitation of result.limitations) logger_default.warn(`- 限制:${limitation}`);
}
function printPreloadAnalysisSummary(result) {
	logger_default.success("preloadRule 静态分析完成");
	if (result.suggestions.length === 0) logger_default.info("未检测到可以证明的跨分包静态跳转。");
	for (const suggestion of result.suggestions) {
		const packages = suggestion.packages.join("、");
		const covered = suggestion.alreadyConfigured.length > 0 ? `,已配置:${suggestion.alreadyConfigured.join("、")}` : "";
		logger_default.info(`- ${suggestion.page} -> ${packages}${covered}`);
		for (const evidence of suggestion.evidence.slice(0, 5)) logger_default.info(`  - ${evidence.source}:${evidence.target}`);
	}
	if (result.uncoveredPages.length > 0) logger_default.warn(`- 未找到可扫描源码的页面:${result.uncoveredPages.join("、")}`);
	if (result.budgets.length > 0) {
		logger_default.info("预下载额度:");
		for (const budget of result.budgets) {
			const size = `${formatAnalyzeBytes(budget.estimatedBytes)} / ${formatAnalyzeBytes(budget.limitBytes)}`;
			const unknown = budget.unknownPackages.length > 0 ? `,未知:${budget.unknownPackages.join("、")}` : "";
			const status = budget.status === "exceeded" ? "超限" : budget.status === "unknown" ? "待确认" : "正常";
			logger_default.info(`- ${budget.sourcePackage}:${size},${status}${unknown}`);
		}
	}
	for (const limitation of result.limitations) logger_default.warn(`- 限制:${limitation}`);
}
async function writeAnalyzeResult(result, outputOption, configService, format = "json", previousResult) {
	if (!outputOption) return;
	const baseDir = configService.cwd;
	const resolvedOutputPath = path.isAbsolute(outputOption) ? outputOption : path.resolve(baseDir, outputOption);
	await fs$1.ensureDir(path.dirname(resolvedOutputPath));
	const content = format === "markdown" && "packages" in result ? createAnalyzeMarkdownReport(result, previousResult) : format === "pr" && "packages" in result ? createAnalyzePrMarkdownReport(result, previousResult) : JSON.stringify(result, null, 2);
	await fs$1.writeFile(resolvedOutputPath, `${content}\n`, "utf8");
	const relativeOutput = configService.relativeCwd(resolvedOutputPath);
	logger_default.success(`分析结果已写入 ${colors.green(relativeOutput)}`);
	return resolvedOutputPath;
}
function formatMetricSummary(label, metric) {
	if (!metric.count || metric.averageMs === void 0 || metric.maxMs === void 0) return;
	return `${label} avg ${metric.averageMs.toFixed(2)} ms,max ${metric.maxMs.toFixed(2)} ms`;
}
function formatOperationSummary(label, operation) {
	if (!operation.count || operation.average === void 0 || operation.max === void 0) return;
	return `${label} avg ${operation.average.toFixed(1)},max ${operation.max.toFixed(0)}`;
}
function formatCountItems(items, limit = 5) {
	return items.slice(0, limit).map((item) => `${item.name} x${item.count}`).join(",");
}
function printHmrProfileAnalysisSummary(result, configService) {
	logger_default.success("HMR profile 分析完成");
	logger_default.info(`- profile:${colors.green(configService.relativeCwd(result.profilePath))}`);
	logger_default.info(`- 样本:${result.sampleCount} 条`);
	if (result.firstTimestamp && result.lastTimestamp) logger_default.info(`- 时间范围:${result.firstTimestamp} -> ${result.lastTimestamp}`);
	const totalSummary = formatMetricSummary("total", result.metrics.totalMs);
	const buildStartSummary = formatMetricSummary("build-start", result.metrics.buildStartMs);
	const pluginResolveSummary = formatMetricSummary("plugin-resolve", result.metrics.pluginResolveMs);
	const coreLoadSummary = formatMetricSummary("core-load", result.metrics.coreLoadMs);
	const entryLoadSummary = formatMetricSummary("entry-load", result.metrics.entryLoadMs);
	const vueReadSourceSummary = formatMetricSummary("vue-read", result.metrics.vueReadSourceMs);
	const vueCompileSummary = formatMetricSummary("vue-compile", result.metrics.vueCompileMs);
	const vueFinalizeCompiledSummary = formatMetricSummary("vue-finalize-compiled", result.metrics.vueFinalizeCompiledMs);
	const vueFinalizeCodeSummary = formatMetricSummary("vue-finalize-code", result.metrics.vueFinalizeCodeMs);
	const entryEmitOutputSummary = formatMetricSummary("entry-emit-output", result.metrics.entryEmitOutputMs);
	const entryTemplateScanSummary = formatMetricSummary("entry-template-scan", result.metrics.entryTemplateScanMs);
	const entryAutoImportSummary = formatMetricSummary("entry-auto-import", result.metrics.entryAutoImportMs);
	const entryScriptSetupSummary = formatMetricSummary("entry-script-setup", result.metrics.entryScriptSetupMs);
	const entryVueSignatureSummary = formatMetricSummary("entry-vue-signature", result.metrics.entryVueSignatureMs);
	const entrySidecarResolveSummary = formatMetricSummary("entry-sidecar-resolve", result.metrics.entrySidecarResolveMs);
	const entryJsonReadSummary = formatMetricSummary("entry-json-read", result.metrics.entryJsonReadMs);
	const entryVueConfigSummary = formatMetricSummary("entry-vue-config", result.metrics.entryVueConfigMs);
	const entryPrepareSummary = formatMetricSummary("entry-prepare", result.metrics.entryPrepareMs);
	const entryResolveSummary = formatMetricSummary("entry-resolve", result.metrics.entryResolveMs);
	const entryStyleScanSummary = formatMetricSummary("entry-style-scan", result.metrics.entryStyleScanMs);
	const entryStyleReadSummary = formatMetricSummary("entry-style-read", result.metrics.entryStyleReadMs);
	const entryCodeReadSummary = formatMetricSummary("entry-code-read", result.metrics.entryCodeReadMs);
	const entryChunkEmitSummary = formatMetricSummary("entry-chunk-emit", result.metrics.entryChunkEmitMs);
	const entryChunkLoadSummary = formatMetricSummary("entry-chunk-load", result.metrics.entryChunkLoadMs);
	const entryChunkEmitFileSummary = formatMetricSummary("entry-chunk-emit-file", result.metrics.entryChunkEmitFileMs);
	const entryLayoutSummary = formatMetricSummary("entry-layout", result.metrics.entryLayoutMs);
	const requestGlobalsSummary = formatMetricSummary("request-globals", result.metrics.requestGlobalsMs);
	const weapiResolveSummary = formatMetricSummary("weapi-resolve", result.metrics.weapiResolveMs);
	const snapshotResolveSummary = formatMetricSummary("snapshot-resolve", result.metrics.snapshotResolveMs);
	const snapshotBuildSummary = formatMetricSummary("snapshot-build", result.metrics.snapshotBuildMs);
	const writeSummary = formatMetricSummary("write", result.metrics.writeMs);
	const watchSummary = formatMetricSummary("watch->dirty", result.metrics.watchToDirtyMs);
	const emitSummary = formatMetricSummary("emit", result.metrics.emitMs);
	const sharedSummary = formatMetricSummary("shared", result.metrics.sharedChunkResolveMs);
	const chunkEmitSummary = formatOperationSummary("chunk emit", result.operations.chunkEmitCount);
	const loadSummary = formatOperationSummary("load calls", result.operations.loadCount);
	const resolveSummary = formatOperationSummary("resolve calls", result.operations.resolveCount);
	const skippedLoadedSummary = formatOperationSummary("loaded skips", result.operations.skippedLoadedCount);
	for (const summary of [
		totalSummary,
		buildStartSummary,
		pluginResolveSummary,
		coreLoadSummary,
		entryLoadSummary,
		vueReadSourceSummary,
		vueCompileSummary,
		vueFinalizeCompiledSummary,
		vueFinalizeCodeSummary,
		entryEmitOutputSummary,
		entryTemplateScanSummary,
		entryAutoImportSummary,
		entryScriptSetupSummary,
		entryVueSignatureSummary,
		entrySidecarResolveSummary,
		entryJsonReadSummary,
		entryVueConfigSummary,
		entryPrepareSummary,
		entryResolveSummary,
		entryStyleScanSummary,
		entryStyleReadSummary,
		entryCodeReadSummary,
		entryChunkEmitSummary,
		entryChunkLoadSummary,
		entryChunkEmitFileSummary,
		entryLayoutSummary,
		requestGlobalsSummary,
		weapiResolveSummary,
		snapshotResolveSummary,
		snapshotBuildSummary,
		writeSummary,
		watchSummary,
		emitSummary,
		sharedSummary
	]) if (summary) logger_default.info(`- ${summary}`);
	for (const summary of [
		loadSummary,
		resolveSummary,
		chunkEmitSummary,
		skippedLoadedSummary
	]) if (summary) logger_default.info(`- ${summary}`);
	if (result.events.length) logger_default.info(`- 事件分布:${formatCountItems(result.events)}`);
	if (result.dirtyReasons.length) logger_default.info(`- 主要 dirty 原因:${formatCountItems(result.dirtyReasons)}`);
	if (result.pendingReasons.length) logger_default.info(`- 主要 pending 原因:${formatCountItems(result.pendingReasons)}`);
	if (result.skippedLineCount > 0) logger_default.warn(`- 跳过 ${result.skippedLineCount} 条无法解析的 profile 记录`);
	if (result.slowestSamples.length) {
		logger_default.info("- 最慢样本:");
		for (const sample of result.slowestSamples.slice(0, 3)) {
			const fileLabel = sample.file ? configService.relativeCwd(sample.file) : "(unknown)";
			logger_default.info(`  - ${sample.totalMs?.toFixed(2) ?? "0.00"} ms,${sample.event ?? "unknown"},${fileLabel}`);
		}
	}
}
function registerAnalyzeCommand(cli) {
	cli.command("analyze [root]", "analyze 两端包体与源码映射").option("--hmr-profile [file]", `[string | boolean] 分析 HMR JSONL profile,省略值时优先读取配置,否则回退到默认路径`).option("--json", `[boolean] 输出 JSON 结果`).option("--markdown", `[boolean] 输出 Markdown 报告`).option("--report <type>", `[string] 输出指定报告类型(pr)`).option("--budget-check", `[boolean] 检查 analyze 预算,超过预算时返回非 0 退出码`).option("--glass-easel-check", `[boolean] 检查 glass-easel 配置、模板和 SelectorQuery 迁移兼容性`).option("--preload", `[boolean] 分析静态页面跳转、实际分包体积和 preloadRule 额度`).option("--output <file>", `[string] 将分析结果写入指定文件(JSON 或 Markdown)`).option("-p, --platform <platform>", `[string] target platform (weapp | web)`).option("--project-config <path>", `[string] project config path (miniprogram only)`).action(async (root, options) => {
		filterDuplicateOptions(options);
		const configFile = resolveConfigFile(options);
		const outputJson = coerceBooleanOption(options.json);
		const outputMarkdown = coerceBooleanOption(options.markdown);
		const reportType = typeof options.report === "string" ? options.report.trim() : "";
		const outputPrReport = reportType === "pr";
		if (reportType && !outputPrReport) throw new Error(`不支持的 analyze report 类型:${reportType}`);
		const budgetCheck = coerceBooleanOption(options.budgetCheck);
		const glassEaselCheck = coerceBooleanOption(options.glassEaselCheck);
		const targets = resolveRuntimeTargets(options);
		const inlineConfig = createInlineConfig(targets);
		let ctx;
		try {
			ctx = await createCompilerContext({
				cwd: root,
				mode: options.mode ?? "production",
				configFile,
				inlineConfig,
				cliPlatform: targets.rawPlatform,
				projectConfigPath: options.projectConfig
			});
			logRuntimeTarget(targets, {
				silent: outputJson || outputMarkdown,
				resolvedConfigPlatform: ctx.configService.platform
			});
			const outputOption = typeof options.output === "string" ? options.output.trim() : "";
			if (options.hmrProfile !== void 0 && options.hmrProfile !== false) {
				const profileOption = typeof options.hmrProfile === "string" && options.hmrProfile.trim() ? options.hmrProfile.trim() : ctx.configService.weappViteConfig.hmr?.profileJson;
				const profilePath = resolveHmrProfileJsonPath({
					cwd: ctx.configService.cwd,
					option: profileOption,
					fallbackToDefault: true
				});
				if (!profilePath) throw new Error("未找到可用的 HMR profile 文件路径");
				const hmrProfileResult = await analyzeHmrProfile({ profilePath });
				const writtenPath = await writeAnalyzeResult(hmrProfileResult, outputOption, ctx.configService);
				if (outputJson) {
					if (!writtenPath) process.stdout.write(`${JSON.stringify(hmrProfileResult, null, 2)}\n`);
				} else printHmrProfileAnalysisSummary(hmrProfileResult, ctx.configService);
				return;
			}
			if (coerceBooleanOption(options.preload)) {
				if (targets.kind !== "miniprogram" || ctx.configService.platform !== "weapp") throw new Error("preloadRule 分析目前仅支持微信小程序平台。");
				const packageAnalysis = await analyzeSubpackages(ctx);
				const preloadResult = await analyzePreloadRules(ctx, { packageAnalysis });
				const writtenPath = await writeAnalyzeResult(preloadResult, outputOption, ctx.configService);
				if (outputJson && !writtenPath) process.stdout.write(`${JSON.stringify(preloadResult, null, 2)}\n`);
				if (!outputJson && !writtenPath) printPreloadAnalysisSummary(preloadResult);
				return;
			}
			if (getBackendForCapability(targets, "web", "analyze")) {
				const webResult = createWebAnalyzeResult(ctx.configService, { platform: "web" });
				const writtenPath = await writeAnalyzeResult(webResult, outputOption, ctx.configService);
				if (outputJson) {
					if (!writtenPath) process.stdout.write(`${JSON.stringify(webResult, null, 2)}\n`);
				} else printWebAnalysisSummary(webResult);
				return;
			}
			if (!getBackendForCapability(targets, "miniprogram", "analyze")) {
				logger_default.warn("当前命令不支持该平台,请通过 --platform weapp 或 --platform web 指定目标。");
				return;
			}
			const previousResult = await readLatestAnalyzeHistorySnapshot(ctx.configService);
			ctx.runtimeState.glassEasel.silent = Boolean(outputJson || outputMarkdown || outputPrReport || glassEaselCheck);
			const result = await analyzeSubpackages(ctx);
			await writeAnalyzeHistorySnapshot(result, ctx.configService);
			const writtenPath = await writeAnalyzeResult(result, outputOption, ctx.configService, outputPrReport ? "pr" : outputMarkdown ? "markdown" : "json", previousResult);
			if (outputPrReport) {
				if (!writtenPath) process.stdout.write(`${createAnalyzePrMarkdownReport(result, previousResult)}\n`);
			} else if (outputMarkdown) {
				if (!writtenPath) process.stdout.write(`${createAnalyzeMarkdownReport(result, previousResult)}\n`);
			} else if (outputJson) {
				if (!writtenPath) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
			}
			const budgetFailed = budgetCheck ? printBudgetCheckSummary(result) : false;
			const glassEaselFailed = glassEaselCheck && !outputJson ? printGlassEaselCheckSummary(result) : glassEaselCheck && result.glassEasel.summary.errors > 0;
			if (budgetFailed || glassEaselFailed) process.exitCode = 1;
			if (budgetCheck || glassEaselCheck) return;
			if (!outputPrReport && !outputMarkdown && !outputJson) {
				printAnalysisSummary(result);
				await startAnalyzeDashboard(result, {
					artifactRoot: ctx.configService.outDir,
					cwd: ctx.configService.cwd,
					packageManagerAgent: ctx.configService.packageManager.agent,
					previousResult
				});
			}
		} catch (error) {
			logger_default.error(error);
			process.exitCode = 1;
		} finally {
			if (ctx) {
				for (const backend of [...targets.entries].reverse()) if (backend.descriptor.capabilities.analyze) await backend.driver?.close?.(ctx);
			}
			terminateStaleSassEmbeddedProcess();
		}
	});
}
//#endregion
//#region src/cli/formatDuration.ts
/**
* 将毫秒耗时格式化为适合 CLI 展示的文本。
*/
function formatDuration(durationMs) {
	return `${durationMs}ms`;
}
//#endregion
//#region src/cli/logBuildAppFinish.ts
let logBuildAppFinishOnlyShowOnce = false;
function collectServerUrls(webServer) {
	const urls = webServer?.resolvedUrls;
	if (!urls) return [];
	return [...urls.local ?? [], ...urls.network ?? []];
}
function logBuildAppFinish(configService, webServer, options = {}) {
	if (logBuildAppFinishOnlyShowOnce) return;
	const { skipMini = false, skipWeb = false, uiUrls = [] } = options;
	const webUrls = skipWeb ? [] : collectServerUrls(webServer);
	if (skipMini) {
		logger_default.success("开发服务已就绪:");
		if (webUrls.length > 0) logger_default.info(`Web:${colors.cyan(webUrls[0])}`);
		else logger_default.info("Web:已启动");
		logBuildAppFinishOnlyShowOnce = true;
		return;
	}
	const { command, args } = resolveCommand(configService.packageManager.agent, "run", ["open"]) ?? {
		command: "npm",
		args: ["run", "open"]
	};
	const devCommand = `${command} ${args.join(" ")}`;
	logger_default.success("开发服务已就绪:");
	logger_default.info(`小程序:执行 ${colors.bold(colors.green(devCommand))},或手动导入 ${colors.green(getProjectConfigFileName(configService.platform))}`);
	if (uiUrls.length > 0) logger_default.info(`UI:${colors.cyan(uiUrls[0])}`);
	else if (!skipMini) logger_default.info("UI:未启用");
	if (webUrls.length > 0) logger_default.info(`Web:${colors.cyan(webUrls[0])}`);
	const projectConfigFileName = getProjectConfigFileName(configService.platform);
	if (!uiUrls.length && !webUrls.length) logger_default.info(`提示:手动打开对应平台开发者工具,导入根目录(${colors.green(projectConfigFileName)} 文件所在目录)`);
	logBuildAppFinishOnlyShowOnce = true;
}
const WINDOWS_SEPARATOR_RE = /\\/g;
function normalizeFileName(fileName) {
	return fileName.replace(WINDOWS_SEPARATOR_RE, "/");
}
function getOutputItemBytes(item) {
	if (item.type === "chunk") return typeof item.code === "string" ? Buffer.byteLength(item.code, "utf8") : 0;
	if (typeof item.source === "string") return Buffer.byteLength(item.source, "utf8");
	if (item.source instanceof Uint8Array) return item.source.byteLength;
	return 0;
}
function resolveSubPackageRoot(fileName, roots) {
	const normalized = normalizeFileName(fileName);
	return roots.find((root) => normalized === root || normalized.startsWith(`${root}/`));
}
function collectPackageSizeReports(output, subPackageMap) {
	const outputs = Array.isArray(output) ? output : [output];
	const roots = [...subPackageMap?.keys() ?? []].filter(Boolean).sort((a, b) => b.length - a.length || a.localeCompare(b));
	const packageBytes = /* @__PURE__ */ new Map([["__main__", 0]]);
	for (const root of roots) packageBytes.set(root, 0);
	for (const current of outputs) for (const item of current.output ?? []) {
		const root = resolveSubPackageRoot(item.fileName, roots) ?? "__main__";
		packageBytes.set(root, (packageBytes.get(root) ?? 0) + getOutputItemBytes(item));
	}
	const reports = [{
		root: "__main__",
		label: "主包",
		bytes: packageBytes.get("__main__") ?? 0
	}];
	for (const root of roots) {
		const meta = subPackageMap?.get(root);
		const isIndependent = Boolean(meta?.subPackage.independent);
		reports.push({
			root,
			label: `${isIndependent ? "独立分包" : "分包"} ${root}`,
			bytes: packageBytes.get(root) ?? 0
		});
	}
	return reports;
}
function logBuildPackageSizeReport(options) {
	const warningBytes = Number(options.warningBytes ?? 2097152);
	const reports = collectPackageSizeReports(options.output, options.subPackageMap);
	logger_default.success("主包/分包体积报告:");
	for (const report of reports) logger_default.info(`${report.label}:${formatBytes(report.bytes)}`);
	if (!(Number.isFinite(warningBytes) && warningBytes > 0)) return;
	for (const report of reports) {
		if (report.bytes <= warningBytes) continue;
		logger_default.warn(`[包体积] ${colors.yellow(report.label)} 体积 ${colors.yellow(formatBytes(report.bytes))},已超过阈值 ${colors.yellow(formatBytes(warningBytes))}。`);
	}
}
//#endregion
//#region src/cli/nodeEnv.ts
function setCommandNodeEnv(nodeEnv) {
	process.env.NODE_ENV = nodeEnv;
}
//#endregion
//#region src/cli/commands/build.ts
function emitDashboardEvents$1(handle, events) {
	handle?.emitRuntimeEvents(events);
}
function normalizeBuildSourcemap(value) {
	if (value === "true") return true;
	if (value === "false") return false;
	return value;
}
function normalizeBuildMinify(value) {
	if (value === "true") return true;
	if (value === "false") return false;
	return value;
}
function hasCliOption(options, key) {
	return Object.prototype.hasOwnProperty.call(options, key);
}
function createBuildInlineConfig(options) {
	const build = {};
	if (typeof options.outDir === "string") build.outDir = options.outDir;
	if (hasCliOption(options, "sourcemap")) build.sourcemap = normalizeBuildSourcemap(options.sourcemap);
	if (hasCliOption(options, "minify")) build.minify = normalizeBuildMinify(options.minify);
	if (typeof options.emptyOutDir === "boolean") build.emptyOutDir = options.emptyOutDir;
	return Object.keys(build).length ? { build } : void 0;
}
function shouldScheduleCompletedProductionBuildExit(options, analyzeHandle) {
	return process.env.VITEST !== "true" && process.env.NODE_ENV !== "test" && process.env.WEAPP_VITE_DISABLE_COMPLETED_BUILD_EXIT !== "1" && options.watch !== true && options.open !== true && analyzeHandle === void 0;
}
function scheduleCompletedProductionBuildExit(options, analyzeHandle) {
	if (!shouldScheduleCompletedProductionBuildExit(options, analyzeHandle)) return;
	setTimeout(() => {
		if (process.exitCode === void 0 || process.exitCode === 0) process.exit(0);
	}, 0).unref?.();
}
function registerBuildCommand(cli) {
	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) => {
		let analyzeHandle;
		let ctx;
		let targets;
		let buildCompleted = false;
		try {
			filterDuplicateOptions(options);
			setCommandNodeEnv("production");
			const cwd = root ?? process.cwd();
			const configFile = resolveConfigFile(options);
			targets = resolveRuntimeTargets(options);
			const inlineConfig = createInlineConfig(targets, {
				scope: options.scope,
				inlineConfig: createBuildInlineConfig(options)
			});
			ctx = await createCompilerContext({
				cwd,
				mode: options.mode ?? "production",
				configFile,
				inlineConfig,
				cliPlatform: targets.rawPlatform,
				projectConfigPath: options.projectConfig,
				emitDefaultAutoImportOutputs: false,
				preloadAppEntry: false
			});
			const { configService } = ctx;
			const miniBackend = getBackendForCapability(targets, "miniprogram", "build");
			const webBackend = getBackendForCapability(targets, "web", "build");
			logRuntimeTarget(targets, { resolvedConfigPlatform: configService.platform });
			const enableAnalyze = Boolean(isUiEnabled(options) && miniBackend);
			for (const backend of targets.select("build")) {
				if (backend.descriptor.id === "miniprogram") {
					const miniBuildStartedAt = Date.now();
					const output = await backend.driver.build(ctx, options);
					const miniBuildDurationMs = Date.now() - miniBuildStartedAt;
					logger_default.success(`小程序构建完成,耗时:${colors.green(formatDuration(miniBuildDurationMs))}`);
					if (!Array.isArray(output) && "output" in output) logBuildPackageSizeReport({
						output,
						subPackageMap: ctx.scanService?.subPackageMap,
						warningBytes: configService.weappViteConfig.packageSizeWarningBytes
					});
					if (enableAnalyze) {
						const analyzeStartedAt = Date.now();
						const previousAnalyzeResult = await readLatestAnalyzeHistorySnapshot(configService);
						const analyzeResult = await analyzeSubpackages(ctx);
						await writeAnalyzeHistorySnapshot(analyzeResult, configService);
						const analyzeDurationMs = Date.now() - analyzeStartedAt;
						analyzeHandle = await startAnalyzeDashboard(analyzeResult, {
							watch: true,
							artifactRoot: configService.outDir,
							cwd: configService.cwd,
							packageManagerAgent: configService.packageManager.agent,
							previousResult: previousAnalyzeResult,
							initialEvents: [{
								kind: "build",
								level: "success",
								title: "mini build completed",
								detail: `生产构建已完成,当前 analyze 结果包含 ${analyzeResult.packages.length} 个包。`,
								durationMs: miniBuildDurationMs,
								tags: ["build", "mini"]
							}, {
								kind: "build",
								level: "success",
								title: "analyze completed",
								detail: `分析已完成,当前包含 ${analyzeResult.packages.length} 个包与 ${analyzeResult.modules.length} 个模块。`,
								durationMs: analyzeDurationMs,
								tags: ["build", "analyze"]
							}]
						}) ?? void 0;
					}
					continue;
				}
				const webConfig = configService.weappWebConfig;
				if (backend.descriptor.id !== "web" || !webConfig?.enabled) continue;
				const webBuildStartedAt = Date.now();
				try {
					await backend.driver.build(ctx, options);
					const webBuildDurationMs = Date.now() - webBuildStartedAt;
					logger_default.success(`Web 构建完成,输出目录:${colors.green(configService.relativeCwd(webConfig.outDir))},耗时:${colors.green(formatDuration(webBuildDurationMs))}`);
					emitDashboardEvents$1(analyzeHandle, [{
						kind: "build",
						level: "success",
						title: "web build completed",
						detail: `Web 构建已完成,输出目录 ${configService.relativeCwd(webConfig.outDir)}。`,
						durationMs: webBuildDurationMs,
						tags: ["build", "web"]
					}]);
				} catch (error) {
					emitDashboardEvents$1(analyzeHandle, [{
						kind: "diagnostic",
						level: "error",
						title: "web build failed",
						detail: error instanceof Error ? error.message : String(error),
						durationMs: Date.now() - webBuildStartedAt,
						tags: ["build", "web"]
					}]);
					logger_default.error(error);
					throw error;
				}
			}
			if (miniBackend) logBuildAppFinish(configService, void 0, { skipWeb: !webBackend });
			if (options.open && getBackendForCapability(targets, "miniprogram", "ide")) {
				emitDashboardEvents$1(analyzeHandle, [{
					kind: "command",
					level: "info",
					title: "opening ide",
					detail: "构建完成后准备打开 IDE 项目。",
					tags: ["ide", "open"]
				}]);
				await openIde(configService.platform, resolveIdeProjectPath(configService.mpDistRoot), {
					openRecovery: options.openRecovery,
					trustProject: options.trustProject,
					openStrategy: options.ideOpenStrategy
				});
			}
			if (analyzeHandle) await analyzeHandle.waitForExit();
			buildCompleted = true;
		} finally {
			if (ctx && targets) for (const backend of [...targets.select("build")].reverse()) await backend.driver.close(ctx);
			terminateStaleSassEmbeddedProcess();
			if (buildCompleted) scheduleCompletedProductionBuildExit(options, analyzeHandle);
		}
	});
}
//#endregion
//#region src/cli/commands/close.ts
function registerCloseCommand(cli) {
	cli.command("close", "close Wechat DevTools").action(async () => {
		if (!await closeIde()) throw new Error("关闭微信开发者工具失败。");
	});
}
//#endregion
//#region src/schematics.ts
function composePath(outDir, filename) {
	return `${outDir}${outDir ? "/" : ""}${filename}`;
}
const defaultExtensions = {
	js: "js",
	json: "json",
	wxml: "wxml",
	wxss: "wxss"
};
function resolveExtension(extension) {
	return extension ? extension.startsWith(".") ? extension : `.${extension}` : "";
}
async function readTemplateFile(templatePath, context) {
	const absolutePath = path.isAbsolute(templatePath) ? templatePath : path.resolve(context.cwd, templatePath);
	return fs$1.readFile(absolutePath, "utf8");
}
async function loadTemplate(template, context) {
	if (template === void 0) return;
	if (typeof template === "function") {
		const result = await template(context);
		return result == null ? void 0 : String(result);
	}
	if (typeof template === "string") return readTemplateFile(template, context);
	if ("content" in template && typeof template.content === "string") return template.content;
	if ("path" in template && typeof template.path === "string") return readTemplateFile(template.path, context);
}
function resolveTemplate(templates, type, fileType) {
	const scoped = templates?.[type]?.[fileType];
	if (scoped !== void 0) return scoped;
	return templates?.shared?.[fileType];
}
async function generate(options) {
	let { fileName, outDir, extensions, type, cwd, templates } = defu(options, {
		type: "component",
		extensions: { ...defaultExtensions },
		cwd: process.cwd(),
		templates: void 0
	});
	if (fileName === void 0) fileName = path.basename(outDir);
	const basepath = path.resolve(cwd, outDir);
	const targetFileTypes = type === "app" ? [
		"js",
		"wxss",
		"json"
	] : [
		"js",
		"wxss",
		"json",
		"wxml"
	];
	const files = [];
	for (const fileType of targetFileTypes) {
		const configuredExt = extensions[fileType] ?? defaultExtensions[fileType];
		let resolvedExt = configuredExt;
		let defaultCode;
		if (fileType === "js") defaultCode = generateJs(type);
		else if (fileType === "wxss") defaultCode = generateWxss();
		else if (fileType === "wxml") defaultCode = generateWxml(path.join(outDir, fileName));
		else if (fileType === "json") {
			defaultCode = generateJson(type, configuredExt);
			if (configuredExt === "js" || configuredExt === "ts") resolvedExt = `json.${configuredExt}`;
		}
		const context = {
			type,
			fileType,
			fileName,
			outDir,
			extension: resolvedExt,
			cwd,
			defaultCode
		};
		const finalCode = await loadTemplate(resolveTemplate(templates, type, fileType), context) ?? defaultCode;
		if (finalCode !== void 0) files.push({
			fileName: `${fileName}${resolveExtension(resolvedExt)}`,
			code: finalCode
		});
	}
	for (const { code, fileName } of files) if (code !== void 0) {
		await fs$1.outputFile(path.resolve(basepath, fileName), code, "utf8");
		logger_default.success(`${composePath(outDir, fileName)} 创建成功!`);
	}
}
//#endregion
//#region src/cli/loadConfig.ts
async function loadConfig(configFile) {
	const cwd = process.cwd();
	let resolvedConfigFile = configFile;
	if (resolvedConfigFile && !path.isAbsolute(resolvedConfigFile)) resolvedConfigFile = path.resolve(cwd, resolvedConfigFile);
	const configEnv = {
		command: "serve",
		mode: "development"
	};
	let loaded;
	try {
		loaded = await loadViteConfigFile(configEnv, resolvedConfigFile, cwd, void 0, void 0, "runner");
	} catch (error) {
		const cjsError = createCjsConfigLoadError({
			error,
			configPath: resolvedConfigFile,
			cwd
		});
		if (cjsError) throw cjsError;
		throw error;
	}
	const weappConfigFilePath = await resolveWeappConfigFile({
		root: cwd,
		specified: resolvedConfigFile
	});
	let weappLoaded;
	if (weappConfigFilePath) {
		const normalizedWeappPath = path.resolve(weappConfigFilePath);
		const normalizedLoadedPath = loaded?.path ? path.resolve(loaded.path) : void 0;
		if (normalizedLoadedPath && normalizedLoadedPath === normalizedWeappPath) weappLoaded = loaded;
		else try {
			weappLoaded = await loadViteConfigFile(configEnv, weappConfigFilePath, cwd, void 0, void 0, "runner");
		} catch (error) {
			const cjsError = createCjsConfigLoadError({
				error,
				configPath: weappConfigFilePath,
				cwd
			});
			if (cjsError) throw cjsError;
			throw error;
		}
	}
	if (!loaded && !weappLoaded) return;
	const config = weappLoaded?.config ? defu(weappLoaded.config, loaded?.config ?? {}) : loaded?.config ?? {};
	const dependencySet = /* @__PURE__ */ new Set();
	for (const dependency of loaded?.dependencies ?? []) dependencySet.add(dependency);
	for (const dependency of weappLoaded?.dependencies ?? []) dependencySet.add(dependency);
	return {
		config,
		path: weappLoaded?.path ?? loaded?.path ?? resolvedConfigFile,
		dependencies: Array.from(dependencySet)
	};
}
//#endregion
//#region src/cli/commands/generate.ts
function registerGenerateCommand(cli) {
	cli.command("g [filepath]", "generate component").alias("generate").option("-a, --app", "type app").option("-p, --page", "type app").option("-n, --name <name>", "filename").action(async (filepath, options) => {
		filterDuplicateOptions(options);
		const config = await loadConfig(resolveConfigFile(options));
		let type = "component";
		let fileName = options.name;
		if (options.app) {
			type = "app";
			if (filepath === void 0) filepath = "";
			fileName = "app";
		}
		if (filepath === void 0) {
			logger_default.error("weapp-vite generate <outDir> 命令必须传入路径参数 outDir");
			return;
		}
		if (options.page) type = "page";
		const generateOptions = config?.config.weapp?.generate;
		fileName = generateOptions?.filenames?.[type] ?? fileName;
		await generate({
			outDir: path.join(generateOptions?.dirs?.[type] ?? "", filepath),
			type,
			fileName,
			extensions: generateOptions?.extensions,
			templates: generateOptions?.templates
		});
	});
}
//#endregion
//#region src/cli/forwardConsole.ts
const DEFAULT_FORWARD_CONSOLE_LEVELS = [
	"log",
	"info",
	"warn",
	"error"
];
let activeForwardConsoleSession;
let activeForwardConsoleBridgeOptions;
let activeForwardConsoleStart;
let forwardConsoleLifecycle = 0;
const FORWARD_CONSOLE_RETRY_DELAY_MS = 1e3;
const FORWARD_CONSOLE_RETRY_TIMES = 5;
const FORWARD_CONSOLE_RETRY_WINDOW_MS = 1e4;
const FORWARD_CONSOLE_RECOVERABLE_START_TIMEOUT_MS = 6e4;
const FORWARD_CONSOLE_START_TIMEOUT_MS = 12e4;
async function detectAgent() {
	try {
		const result = await determineAgent();
		return {
			isAgent: result.isAgent,
			agentName: result.isAgent ? result.agent.name : void 0
		};
	} catch {
		return {
			isAgent: false,
			agentName: void 0
		};
	}
}
function formatForwardConsolePrefix(level, color) {
	const label = `[mini:${level.padEnd(5)}]`;
	if (!color) return label;
	if (level === "error") return colors.bold(colors.red(label));
	if (level === "warn") return colors.bold(colors.yellow(label));
	if (level === "info") return colors.bold(colors.cyan(label));
	if (level === "debug") return colors.dim(label);
	return colors.bold(colors.green(label));
}
function formatForwardConsoleMessage(level, message, color) {
	if (!color) return message;
	if (level === "error") return colors.red(message);
	if (level === "warn") return colors.yellow(message);
	if (level === "debug") return colors.dim(message);
	return message;
}
function writeForwardConsoleLine(line) {
	process.stdout.write(`${line}\n`);
}
function isDevtoolsPortNotReadyError(error) {
	return error instanceof Error && (error.message === "DEVTOOLS_HTTP_PORT_ERROR" || error.message === "DEVTOOLS_WS_CONNECT_ERROR" || error.message === "DEVTOOLS_EXTENSION_CONTEXT_INVALIDATED");
}
function isDevtoolsProtocolTimeoutError(error) {
	return error instanceof Error && (error.message === "DEVTOOLS_PROTOCOL_TIMEOUT" || /DevTools did not respond to protocol method \S+ within \d+ms/i.test(error.message));
}
function isRecoverableAutomatorStartError(error) {
	return isDevtoolsPortNotReadyError(error) || isDevtoolsProtocolTimeoutError(error) || isRetryableAutomatorLaunchError(error);
}
function sleep(ms) {
	return new Promise((resolve) => setTimeout(resolve, ms));
}
async function withForwardConsoleRetry(runner) {
	let lastError;
	const startedAt = Date.now();
	for (let attempt = 0; attempt <= FORWARD_CONSOLE_RETRY_TIMES; attempt++) try {
		return await runner();
	} catch (error) {
		lastError = error;
		if (!isDevtoolsPortNotReadyError(error) || attempt === FORWARD_CONSOLE_RETRY_TIMES || Date.now() - startedAt >= FORWARD_CONSOLE_RETRY_WINDOW_MS) break;
		await sleep(FORWARD_CONSOLE_RETRY_DELAY_MS);
	}
	throw lastError instanceof Error ? lastError : new Error(String(lastError));
}
/**
* @description 解析 forwardConsole 配置,并在 auto 模式下检测 AI 终端。
*/
async function resolveForwardConsoleOptions(config) {
	const rawConfig = config?.forwardConsole;
	const defaults = {
		logLevels: DEFAULT_FORWARD_CONSOLE_LEVELS,
		unhandledErrors: true
	};
	if (rawConfig === false) return {
		enabled: false,
		...defaults
	};
	if (rawConfig === true) return {
		enabled: true,
		...defaults
	};
	const normalizedConfig = rawConfig ?? {};
	const enabledMode = normalizedConfig.enabled ?? "auto";
	const logLevels = normalizedConfig.logLevels?.length ? normalizedConfig.logLevels : defaults.logLevels;
	const unhandledErrors = normalizedConfig.unhandledErrors ?? defaults.unhandledErrors;
	if (enabledMode === true) return {
		enabled: true,
		logLevels,
		unhandledErrors
	};
	if (enabledMode === false) return {
		enabled: false,
		logLevels,
		unhandledErrors
	};
	const agentResult = await detectAgent();
	return {
		enabled: agentResult.isAgent,
		agentName: agentResult.agentName,
		logLevels,
		unhandledErrors
	};
}
/**
* @description 统一启动 DevTools 日志桥,并在 IDE 刚启动时做短暂重试。
*/
async function startForwardConsoleBridge(options) {
	return await withForwardConsoleRetry(async () => {
		const color = options.color ?? true;
		return await startForwardConsole({
			projectPath: options.projectPath,
			logLevels: options.logLevels,
			openedOnly: options.openedOnly,
			preferOpenedSession: options.preferOpenedSession,
			preserveProjectRoot: options.preserveProjectRoot,
			port: options.port,
			timeout: options.timeout,
			unhandledErrors: options.unhandledErrors,
			onReady: () => {
				const suffix = options.agentName ? `(AI 终端:${options.agentName})` : "";
				logger_default.info(`${options.onReadyMessage}${suffix}`);
			},
			onLog: (event) => {
				writeForwardConsoleLine(`${formatForwardConsolePrefix(event.level, color)} ${formatForwardConsoleMessage(event.level, event.message, color)}`);
			}
		});
	});
}
/**
* @description 暂停当前 DevTools 日志桥,并返回恢复函数。
*/
async function pauseActiveForwardConsole() {
	const session = activeForwardConsoleSession;
	const bridgeOptions = activeForwardConsoleBridgeOptions;
	if (!session || !bridgeOptions) return;
	activeForwardConsoleSession = void 0;
	activeForwardConsoleBridgeOptions = void 0;
	await session.close();
	return async () => {
		if (activeForwardConsoleSession) return true;
		try {
			activeForwardConsoleSession = await startForwardConsoleBridge(bridgeOptions);
			activeForwardConsoleBridgeOptions = bridgeOptions;
			return true;
		} catch (error) {
			activeForwardConsoleSession = void 0;
			activeForwardConsoleBridgeOptions = void 0;
			const message = error instanceof Error ? error.message : String(error);
			logger_default.warn(`[forwardConsole] 恢复失败:${message}`);
			return false;
		}
	};
}
/**
* @description 关闭当前 DevTools 日志桥并释放共享 automator 会话。
*/
async function closeActiveForwardConsole() {
	forwardConsoleLifecycle += 1;
	const session = activeForwardConsoleSession;
	const pendingStart = activeForwardConsoleStart;
	activeForwardConsoleSession = void 0;
	activeForwardConsoleBridgeOptions = void 0;
	await session?.close();
	await pendingStart?.catch(() => {});
}
/**
* @description 在 weapp 开发态按需启动控制台转发。
*/
async function maybeStartForwardConsole(options) {
	if (options.platform !== "weapp") return false;
	const projectPath = resolveIdeProjectPath(options.mpDistRoot) ?? options.cwd;
	if (!projectPath) return false;
	if (activeForwardConsoleSession) return true;
	if (activeForwardConsoleStart) return await activeForwardConsoleStart;
	const lifecycle = forwardConsoleLifecycle;
	const startTask = (async () => {
		const resolved = await resolveForwardConsoleOptions(options.weappViteConfig);
		if (!resolved.enabled || lifecycle !== forwardConsoleLifecycle) return false;
		const bridgeOptions = {
			agentName: resolved.agentName,
			color: !resolved.agentName,
			projectPath,
			port: resolveProjectAutomatorPort(projectPath),
			timeout: options.recoverAutomatorSession ? FORWARD_CONSOLE_RECOVERABLE_START_TIMEOUT_MS : FORWARD_CONSOLE_START_TIMEOUT_MS,
			logLevels: resolved.logLevels,
			openedOnly: options.openedOnly,
			preferOpenedSession: options.preferOpenedSession,
			preserveProjectRoot: options.preserveProjectRoot ?? !shouldUseAutomatorProjectWrapper(projectPath),
			unhandledErrors: resolved.unhandledErrors,
			onReadyMessage: "[forwardConsole] 已连接微信开发者工具日志"
		};
		const activateSession = async (session, activeOptions) => {
			if (lifecycle !== forwardConsoleLifecycle) {
				await session.close();
				return false;
			}
			activeForwardConsoleSession = session;
			activeForwardConsoleBridgeOptions = activeOptions;
			return true;
		};
		const recoverAutomatorSession = async () => {
			try {
				await options.recoverAutomatorSession?.();
				if (lifecycle !== forwardConsoleLifecycle) return false;
				const recoveredOptions = {
					...bridgeOptions,
					openedOnly: true,
					preferOpenedSession: true
				};
				return await activateSession(await startForwardConsoleBridge(recoveredOptions), recoveredOptions);
			} catch (recoveryError) {
				if (lifecycle !== forwardConsoleLifecycle) return false;
				const message = recoveryError instanceof Error ? recoveryError.message : String(recoveryError);
				logger_default.warn(`[forwardConsole] automator 会话恢复失败,回退到普通 IDE 打开流程:${message}`);
				return false;
			}
		};
		try {
			return await activateSession(await startForwardConsoleBridge(bridgeOptions), bridgeOptions);
		} catch (error) {
			if (lifecycle !== forwardConsoleLifecycle) return false;
			if (options.recoverAutomatorSession && isRecoverableAutomatorStartError(error)) return await recoverAutomatorSession();
			if (!isDevtoolsPortNotReadyError(error)) {
				const message = error instanceof Error ? error.message : String(error);
				logger_default.warn(`[forwardConsole] 启动失败,回退到普通 IDE 打开流程:${message}`);
				return false;
			}
			const fallbackOptions = {
				...bridgeOptions,
				openedOnly: true,
				port: void 0,
				preferOpenedSession: true
			};
			try {
				return await activateSession(await startForwardConsoleBridge(fallbackOptions), fallbackOptions);
			} catch (fallbackError) {
				if (lifecycle !== forwardConsoleLifecycle) return false;
				const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
				logger_default.warn(`[forwardConsole] 启动失败,回退到普通 IDE 打开流程:${message}`);
				return false;
			}
		}
	})();
	activeForwardConsoleStart = startTask;
	try {
		return await startTask;
	} finally {
		if (activeForwardConsoleStart === startTask) activeForwardConsoleStart = void 0;
	}
}
//#endregion
//#region src/cli/hmrProfileSummary.ts
function isFiniteNumber(value) {
	return typeof value === "number" && Number.isFinite(value);
}
function parseLatestHmrProfileSample(content) {
	const lines = content.split(/\r?\n/);
	for (let index = lines.length - 1; index >= 0; index -= 1) {
		const trimmed = lines[index]?.trim();
		if (!trimmed) continue;
		try {
			const parsed = JSON.parse(trimmed);
			if (isFiniteNumber(parsed.totalMs)) return parsed;
		} catch {
			continue;
		}
	}
}
function formatPhaseHint(sample) {
	const topPhase = [
		{
			label: "build-core",
			value: sample.buildCoreMs
		},
		{
			label: "transform",
			value: sample.transformMs
		},
		{
			label: "build-start",
			value: sample.buildStartMs
		},
		{
			label: "plugin-resolve",
			value: sample.pluginResolveMs
		},
		{
			label: "core-transform",
			value: sample.coreTransformMs
		},
		{
			label: "wevu-transform",
			value: sample.wevuTransformMs
		},
		{
			label: "vue-transform",
			value: sample.vueTransformMs
		},
		{
			label: "vue-read",
			value: sample.vueReadSourceMs
		},
		{
			label: "vue-compile",
			value: sample.vueCompileMs
		},
		{
			label: "vue-finalize-compiled",
			value: sample.vueFinalizeCompiledMs
		},
		{
			label: "vue-finalize-code",
			value: sample.vueFinalizeCodeMs
		},
		{
			label: "core-load",
			value: sample.coreLoadMs
		},
		{
			label: "entry-load",
			value: sample.entryLoadMs
		},
		{
			label: "entry-emit-output",
			value: sample.entryEmitOutputMs
		},
		{
			label: "entry-template-scan",
			value: sample.entryTemplateScanMs
		},
		{
			label: "entry-auto-import",
			value: sample.entryAutoImportMs
		},
		{
			label: "entry-script-setup",
			value: sample.entryScriptSetupMs
		},
		{
			label: "entry-vue-signature",
			value: sample.entryVueSignatureMs
		},
		{
			label: "entry-sidecar-resolve",
			value: sample.entrySidecarResolveMs
		},
		{
			label: "entry-json-read",
			value: sample.entryJsonReadMs
		},
		{
			label: "entry-vue-config",
			value: sample.entryVueConfigMs
		},
		{
			label: "entry-prepare",
			value: sample.entryPrepareMs
		},
		{
			label: "entry-resolve",
			value: sample.entryResolveMs
		},
		{
			label: "entry-style-scan",
			value: sample.entryStyleScanMs
		},
		{
			label: "entry-style-read",
			value: sample.entryStyleReadMs
		},
		{
			label: "entry-code-read",
			value: sample.entryCodeReadMs
		},
		{
			label: "entry-chunk-emit",
			value: sample.entryChunkEmitMs
		},
		{
			label: "entry-chunk-load",
			value: sample.entryChunkLoadMs
		},
		{
			label: "entry-chunk-emit-file",
			value: sample.entryChunkEmitFileMs
		},
		{
			label: "entry-layout",
			value: sample.entryLayoutMs
		},
		{
			label: "request-globals",
			value: sample.requestGlobalsMs
		},
		{
			label: "weapi-resolve",
			value: sample.weapiResolveMs
		},
		{
			label: "render-start",
			value: sample.renderStartMs
		},
		{
			label: "generate",
			value: sample.generateBundleMs
		},
		{
			label: "generate-shared",
			value: sample.generateSharedMs
		},
		{
			label: "generate-rewrite",
			value: sample.generateRewriteMs
		},
		{
			label: "module-graph",
			value: sample.generateModuleGraphMs
		},
		{
			label: "snapshot-resolve",
			value: sample.snapshotResolveMs
		},
		{
			label: "snapshot-build",
			value: sample.snapshotBuildMs
		},
		{
			label: "watch->dirty",
			value: sample.watchToDirtyMs
		},
		{
			label: "emit",
			value: sample.emitMs
		},
		{
			label: "shared",
			value: sample.sharedChunkResolveMs
		},
		{
			label: "write",
			value: sample.writeMs
		}
	].filter((phase) => isFiniteNumber(phase.value)).sort((left, right) => (right.value ?? 0) - (left.value ?? 0))[0];
	if (!topPhase || (topPhase.value ?? 0) < 5) return;
	return `${topPhase.label} ${topPhase.value.toFixed(2)} ms`;
}
/**
* @description 读取最近一次 HMR profile,并格式化为 IDE 日志启动前的单行摘要。
*/
async function readLatestHmrProfileSummary(options) {
	const profilePath = resolveHmrProfileJsonPath({
		cwd: options.cwd,
		option: options.weappViteConfig?.hmr?.profileJson
	});
	if (!profilePath) return;
	const content = await fs$1.readFile(profilePath, "utf8").catch(() => void 0);
	if (!content) return;
	const sample = parseLatestHmrProfileSample(content);
	if (!sample || !isFiniteNumber(sample.totalMs)) return;
	const relativeCwd = options.relativeCwd ?? ((value) => value);
	const segments = [`[hmr] 最近一次热更新 ${sample.totalMs.toFixed(2)} ms`];
	if (sample.event) segments.push(sample.event);
	if (sample.file) segments.push(relativeCwd(sample.file));
	const phaseHint = formatPhaseHint(sample);
	if (phaseHint) segments.push(`主耗时 ${phaseHint}`);
	if (isFiniteNumber(sample.loadCount) || isFiniteNumber(sample.resolveCount) || isFiniteNumber(sample.chunkEmitCount) || isFiniteNumber(sample.skippedLoadedCount)) segments.push(`load/resolve/chunk/skip ${sample.loadCount ?? 0}/${sample.resolveCount ?? 0}/${sample.chunkEmitCount ?? 0}/${sample.skippedLoadedCount ?? 0}`);
	return {
		file: sample.file,
		profilePath,
		line: segments.join(",")
	};
}
//#endregion
//#region src/cli/commands/ide.ts
async function runIdeDoctor(projectPath, options) {
	const resolvedCli = await resolveCliPath();
	const servicePort = await detectWechatDevtoolsServicePort().then((result) => ({
		...result,
		error: void 0
	})).catch((error) => ({
		servicePortEnabled: void 0,
		servicePort: void 0,
		error
	}));
	const cliCheck = resolvedCli.cliPath ? {
		status: "ok",
		value: {
			path: resolvedCli.cliPath,
			source: resolvedCli.source
		}
	} : {
		fix: "执行 `weapp config set cliPath <path-to-cli>` 配置微信开发者工具 CLI。",
		message: "未找到可用的微信开发者工具 CLI。",
		status: "failed",
		value: { source: resolvedCli.source }
	};
	const servicePortCheck = servicePort.servicePortEnabled === false ? {
		fix: "在微信开发者工具中打开:设置 -> 安全设置 -> 服务端口。",
		message: "微信开发者工具服务端口已关闭。",
		status: "failed",
		value: servicePort
	} : servicePort.error ? {
		fix: "执行 `wv ide doctor --json` 查看底层错误,并确认微信开发者工具已安装。",
		message: servicePort.error instanceof Error ? servicePort.error.message : String(servicePort.error),
		status: "warning"
	} : {
		status: servicePort.servicePortEnabled === true ? "ok" : "unknown",
		value: {
			enabled: servicePort.servicePortEnabled ?? null,
			port: servicePort.servicePort ?? null
		}
	};
	let loginCheck = {
		fix: "打开微信开发者工具完成登录后重试。",
		message: "暂未确认登录状态。",
		status: "unknown"
	};
	if (resolvedCli.cliPath) try {
		await isWechatIdeLoggedIn({
			nonInteractive: true,
			silent: true
		});
		loginCheck = {
			status: "ok",
			value: true
		};
	} catch (error) {
		loginCheck = {
			fix: "打开微信开发者工具完成登录后重试,或执行 `wv ide doctor --json` 查看 CLI 错误。",
			message: error instanceof Error ? error.message : String(error),
			status: "warning",
			value: false
		};
	}
	const projectCheck = projectPath ? {
		status: "ok",
		value: projectPath
	} : {
		fix: "在项目目录执行命令,或显式传入 `wv ide doctor <project-root>`。",
		message: "未解析到微信开发者工具项目目录。",
		status: "warning"
	};
	let automatorCheck = {
		message: projectPath ? "当前项目没有可连接的已打开 automator 会话。" : "缺少项目目录,跳过 automator 检查。",
		status: "unknown"
	};
	let toolCheck = {
		message: "当前没有可读取的 DevTools Tool.getInfo。",
		status: "unknown"
	};
	if (projectPath) try {
		const miniProgram = await connectOpenedAutomator({
			projectPath,
			port: resolveProjectAutomatorPort(projectPath),
			timeout: 3e3
		});
		automatorCheck = {
			status: "ok",
			value: true
		};
		if (typeof miniProgram.toolInfo === "function") toolCheck = {
			status: "ok",
			value: await miniProgram.toolInfo()
		};
		miniProgram.disconnect?.();
	} catch (error) {
		automatorCheck = {
			fix: "先执行 `wv open` 打开项目;若服务端口已开启,关闭多余 DevTools 窗口后重试。",
			message: error instanceof Error ? error.message : String(error),
			status: "warning"
		};
	}
	const report = {
		checks: {
			automator: automatorCheck,
			cli: cliCheck,
			login: loginCheck,
			project: projectCheck,
			servicePort: servicePortCheck,
			tool: toolCheck
		},
		configFile: defaultCustomConfigFilePath,
		nodeVersion: process.version,
		platform: process.platform,
		weappViteVersion: VERSION
	};
	const output = JSON.stringify(report, null, 2);
	if (options.json) process.stdout.write(`${output}\n`);
	else logger_default.info(output);
	if (options.strict && Object.values(report.checks).some((check) => check.status === "failed")) throw new Error("微信开发者工具环境诊断未通过,请根据 doctor 输出修复失败项后重试。");
	return report;
}
async function waitForTermination(cleanup) {
	await new Promise((resolve) => {
		const signals = ["SIGINT", "SIGTERM"];
		let cleaning = false;
		const handlers = /* @__PURE__ */ new Map();
		const teardown = async () => {
			if (cleaning) return;
			cleaning = true;
			for (const signal of signals) process.off(signal, handlers.get(signal));
			await cleanup();
			resolve();
		};
		for (const signal of signals) {
			const handler = () => {
				teardown();
			};
			handlers.set(signal, handler);
			process.on(signal, handler);
		}
	});
}
function formatIdeOutput(data, options) {
	if (options.json) return JSON.stringify(data, null, 2);
	return JSON.stringify(data, null, 2);
}
/**
* @description 执行 ide 子命令。
*/
async function runIdeCommand(action, root, options) {
	if (action !== "logs" && action !== "setup" && action !== "info" && action !== "test-accounts" && action !== "ticket" && action !== "ticket:set" && action !== "ticket:refresh" && action !== "doctor") throw new Error(`未知 ide 子命令: ${action ?? "(empty)"}`);
	filterDuplicateOptions(options);
	const configFile = resolveConfigFile(options);
	const targets = resolveRuntimeTargets(options);
	const ideBackend = getBackendForCapability(targets, "miniprogram", "ide");
	if (!ideBackend) throw new Error("`weapp-vite ide` 当前仅支持小程序平台。");
	const resolved = await resolveIdeCommandContext({
		configFile,
		mode: options.mode ?? "development",
		platform: ideBackend.platform,
		projectPath: root,
		cliPlatform: targets.rawPlatform
	});
	if (action !== "doctor" && resolved.platform !== "weapp") throw new Error("`weapp-vite ide logs` 当前仅支持微信小程序平台。");
	if (action === "doctor") {
		await runIdeDoctor(resolved.projectPath, options);
		return;
	}
	const projectPath = resolved.projectPath;
	if (!projectPath) throw new Error("无法解析微信开发者工具项目目录,请显式传入 root 或检查 project.config.json。");
	if (action === "setup") {
		const result = await bootstrapWechatDevtoolsSettings({
			projectPath,
			trustProject: options.trustProject
		});
		logger_default.info(`已完成微信开发者工具配置预热:扫描实例 ${result.touchedInstanceCount} 个,检测服务端口配置 ${result.detectedSecurityCount} 处,写入项目信任 ${result.trustedProjectCount} 处。`);
		return;
	}
	if (options.open) await openIde(resolved.platform, projectPath, {
		openRecovery: options.openRecovery,
		trustProject: options.trustProject,
		openStrategy: options.ideOpenStrategy
	});
	if (action === "info") {
		const result = await getWechatIdeToolInfo({ projectPath });
		logger_default.info(formatIdeOutput(result, options));
		return;
	}
	if (action === "test-accounts") {
		const result = await getWechatIdeTestAccounts({ projectPath });
		logger_default.info(formatIdeOutput(result, options));
		return;
	}
	if (action === "ticket") {
		const result = await getWechatIdeTicket({ projectPath });
		logger_default.info(formatIdeOutput(result, options));
		return;
	}
	if (action === "ticket:set") {
		if (!options.ticket) throw new Error("`weapp-vite ide ticket:set` 需要提供 --ticket。");
		await setWechatIdeTicket({
			projectPath,
			ticket: options.ticket
		});
		logger_default.info(`已设置微信开发者工具 ticket:${options.ticket}`);
		return;
	}
	if (action === "ticket:refresh") {
		await refreshWechatIdeTicket({ projectPath });
		logger_default.info("已刷新微信开发者工具 ticket。");
		return;
	}
	const forwardConsoleOptions = await resolveForwardConsoleOptions({
		...resolved.weappViteConfig,
		forwardConsole: resolved.weappViteConfig?.forwardConsole === false ? { enabled: true } : {
			...typeof resolved.weappViteConfig?.forwardConsole === "object" ? resolved.weappViteConfig.forwardConsole : {},
			enabled: true
		}
	});
	const latestHmrSummary = await readLatestHmrProfileSummary({
		cwd: resolved.cwd ?? process.cwd(),
		relativeCwd: (value) => resolved.cwd ? value.replace(`${resolved.cwd}/`, "") : value,
		weappViteConfig: resolved.weappViteConfig
	});
	if (latestHmrSummary) logger_default.info(latestHmrSummary.line);
	const session = await startForwardConsoleBridge({
		projectPath,
		port: resolveProjectAutomatorPort(projectPath),
		agentName: void 0,
		logLevels: forwardConsoleOptions.logLevels,
		unhandledErrors: forwardConsoleOptions.unhandledErrors,
		onReadyMessage: "[forwardConsole] 已进入持续监听模式,按 Ctrl+C 退出。"
	});
	await waitForTermination(async () => {
		logger_default.info("[forwardConsole] 正在关闭日志桥接...");
		await session.close();
	});
}
/**
* @description 注册 IDE 相关子命令。
*/
function registerIdeCommand(cli) {
	cli.command("ide [action] [root]", "run Wechat DevTools utility actions and log bridge commands").option("-o, --open", "[boolean] open ide before attaching log bridge").option("-p, --platform <platform>", "[string] target platform (weapp | web)").option("--project-config <path>", "[string] project config path (miniprogram only)").option("--json", "[boolean] output the doctor report as JSON").option("--ticket <value>", "[string] ticket used by `ide ticket:set`").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("--strict", "[boolean] fail when doctor finds blocking environment errors").option("--no-open-recovery", "[boolean] disable automatic Wechat DevTools close-and-reopen recovery").action(async (action, root, options) => {
		await runIdeCommand(action, root, options);
	});
}
//#endregion
//#region src/cli/commands/init.ts
function registerInitCommand(cli) {
	cli.command("init").action(async () => {
		try {
			await initConfig({ command: "weapp-vite" });
		} catch (error) {
			logger_default.error(error);
		}
	});
}
//#endregion
//#region src/cli/mcpClient/shared.ts
const CODEX_BLOCK_PREFIX = "# >>> weapp-vite mcp ";
const CODEX_BLOCK_SUFFIX = " >>>";
const CODEX_BLOCK_END_PREFIX = "# <<< weapp-vite mcp ";
const CODEX_BLOCK_END_SUFFIX = " <<<";
const DEFAULT_HTTP_TIMEOUT_MS = 1500;
const WORKSPACE_FOLDER_TOKEN = "${workspaceFolder}";
const REG_CodexUrl = /^\s*url\s*=\s*"([^"]+)"/m;
const REG_CodexCommand = /^\s*command\s*=\s*"([^"]+)"/m;
const REG_CodexArgs = /^\s*args\s*=\s*\[([^\]]*)\]/m;
const REG_FirstTomlString = /"([^"]+)"/;
function resolveTargetWorkspaceRoot(target) {
	if (target.client === "cursor") return path.dirname(path.dirname(target.configPath));
	if (target.client === "claude-code") return path.dirname(target.configPath);
	return process.cwd();
}
function escapeRegExp(value) {
	return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function renderTomlString(value) {
	return JSON.stringify(value);
}
function sanitizeServerNameSegment(value) {
	return value.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
}
function resolveServerName(workspaceRoot) {
	return `weapp-vite-${sanitizeServerNameSegment(path.basename(workspaceRoot)) || "project"}`;
}
function resolveNodeBinCommand() {
	return process.execPath || "node";
}
function resolveProjectCliBin(workspaceRoot) {
	return path.join(workspaceRoot, "node_modules", "weapp-vite", "bin", "weapp-vite.js");
}
function renderCommandArgs(args) {
	return args.map(renderTomlString).join(", ");
}
function renderCodexBlock(serverName, entry) {
	const startMarker = `${CODEX_BLOCK_PREFIX}${serverName}${CODEX_BLOCK_SUFFIX}`;
	const endMarker = `${CODEX_BLOCK_END_PREFIX}${serverName}${CODEX_BLOCK_END_SUFFIX}`;
	if (entry.url) return `${startMarker}
[mcp_servers.${serverName}]
url = ${renderTomlString(entry.url)}
${endMarker}
`;
	return `${startMarker}
[mcp_servers.${serverName}]
command = ${renderTomlString(entry.command ?? resolveNodeBinCommand())}
args = [${renderCommandArgs(entry.args ?? [])}]
${endMarker}
`;
}
function renderJsonPreview(serverName, entry) {
	return `${JSON.stringify({ mcpServers: { [serverName]: entry } }, null, 2)}
`;
}
function resolveCommandEntry(client, workspaceRoot) {
	const binPath = resolveProjectCliBin(workspaceRoot);
	if (client === "cursor") return {
		command: "node",
		args: [
			`${WORKSPACE_FOLDER_TOKEN}/node_modules/weapp-vite/bin/weapp-vite.js`,
			"mcp",
			"--workspace-root",
			WORKSPACE_FOLDER_TOKEN
		]
	};
	return {
		command: resolveNodeBinCommand(),
		args: [
			binPath,
			"mcp",
			"--workspace-root",
			workspaceRoot
		]
	};
}
function resolveHttpEntry(client, url) {
	if (client === "claude-code") return {
		type: "http",
		url
	};
	return { url };
}
function resolveConfigEntry(client, transport, workspaceRoot, url) {
	return transport === "http" ? resolveHttpEntry(client, url ?? "") : resolveCommandEntry(client, workspaceRoot);
}
function resolveCodexBlockPattern(serverName) {
	const startMarker = `${CODEX_BLOCK_PREFIX}${serverName}${CODEX_BLOCK_SUFFIX}`;
	const endMarker = `${CODEX_BLOCK_END_PREFIX}${serverName}${CODEX_BLOCK_END_SUFFIX}`;
	return new RegExp(`${escapeRegExp(startMarker)}[\\s\\S]*?${escapeRegExp(endMarker)}\\n?`, "g");
}
function upsertCodexManagedBlock(content, serverName, block) {
	const withoutExisting = content.trimEnd().replace(resolveCodexBlockPattern(serverName), "").trimEnd();
	if (!withoutExisting) return block;
	return `${withoutExisting}\n\n${block}`;
}
function parseJsonConfig(content, configPath) {
	if (!content.trim()) return {};
	try {
		return JSON.parse(content);
	} catch (error) {
		const message = error instanceof Error ? error.message : String(error);
		throw new Error(`无法解析 MCP 配置文件 ${configPath}:${message}`);
	}
}
function resolveTarget(client, workspaceRoot) {
	const serverName = resolveServerName(workspaceRoot);
	if (client === "codex") return {
		client,
		configPath: path.join(os.homedir(), ".codex", "config.toml"),
		displayName: "Codex",
		serverName
	};
	if (client === "claude-code") return {
		client,
		configPath: path.join(workspaceRoot, ".mcp.json"),
		displayName: "Claude Code",
		serverName
	};
	return {
		client,
		configPath: path.join(workspaceRoot, ".cursor", "mcp.json"),
		displayName: "Cursor",
		serverName
	};
}
//#endregion
//#region src/cli/mcpClient/inspect.ts
async function probeHttpEndpoint(url) {
	const controller = new AbortController();
	const timer = setTimeout$1(() => {
		controller.abort();
	}, DEFAULT_HTTP_TIMEOUT_MS);
	try {
		return (await fetch(url, {
			method: "GET",
			signal: controller.signal
		})).status > 0;
	} catch {
		return false;
	} finally {
		clearTimeout$1(timer);
		controller.abort();
	}
}
async function inspectJsonConfig(target) {
	if (!await fs$1.pathExists(target.configPath)) return {
		configExists: false,
		configPath: target.configPath,
		displayName: target.displayName,
		issues: [`未找到配置文件:${target.configPath}`],
		serverName: target.serverName
	};
	const entry = parseJsonConfig(await fs$1.readFile(target.configPath, "utf8"), target.configPath).mcpServers?.[target.serverName];
	if (!entry) return {
		configExists: true,
		configPath: target.configPath,
		displayName: target.displayName,
		issues: [`配置文件中未找到服务器 ${target.serverName}`],
		serverName: target.serverName
	};
	const transport = entry.url ? "http" : "command";
	const issues = [];
	let httpReachable;
	if (transport === "command") {
		const normalizedBin = (entry.args?.[0])?.replaceAll(WORKSPACE_FOLDER_TOKEN, resolveTargetWorkspaceRoot(target));
		if (!normalizedBin || !await fs$1.pathExists(normalizedBin)) issues.push(`未找到本地 CLI 入口:${normalizedBin ?? "缺少 args[0]"}`);
	} else if (entry.url) {
		httpReachable = await probeHttpEndpoint(entry.url);
		if (!httpReachable) issues.push(`HTTP MCP 地址不可达:${entry.url}`);
	}
	return {
		configExists: true,
		configPath: target.configPath,
		displayName: target.displayName,
		httpReachable,
		issues,
		serverName: target.serverName,
		transport
	};
}
async function inspectCodexConfig(target) {
	if (!await fs$1.pathExists(target.configPath)) return {
		configExists: false,
		configPath: target.configPath,
		displayName: target.displayName,
		issues: [`未找到配置文件:${target.configPath}`],
		serverName: target.serverName
	};
	const blockMatch = (await fs$1.readFile(target.configPath, "utf8")).match(resolveCodexBlockPattern(target.serverName));
	if (!blockMatch?.[0]) return {
		configExists: true,
		configPath: target.configPath,
		displayName: target.displayName,
		issues: [`未找到 weapp-vite 生成的 Codex 配置区块:${target.serverName}`],
		serverName: target.serverName
	};
	const block = blockMatch[0];
	const issues = [];
	const urlMatch = block.match(REG_CodexUrl);
	const commandMatch = block.match(REG_CodexCommand);
	const argsMatch = block.match(REG_CodexArgs);
	if (urlMatch?.[1]) {
		const reachable = await probeHttpEndpoint(urlMatch[1]);
		if (!reachable) issues.push(`HTTP MCP 地址不可达:${urlMatch[1]}`);
		return {
			configExists: true,
			configPath: target.configPath,
			displayName: target.displayName,
			httpReachable: reachable,
			issues,
			serverName: target.serverName,
			transport: "http"
		};
	}
	const binPath = (argsMatch?.[1]?.match(REG_FirstTomlString))?.[1];
	if (!commandMatch?.[1]) issues.push("Codex 配置区块缺少 command 字段");
	if (!binPath || !await fs$1.pathExists(binPath)) issues.push(`未找到本地 CLI 入口:${binPath ?? "缺少 args[0]"}`);
	return {
		configExists: true,
		configPath: target.configPath,
		displayName: target.displayName,
		issues,
		serverName: target.serverName,
		transport: "command"
	};
}
async function inspectMcpClientConfig(options) {
	const target = resolveTarget(options.client, path.resolve(options.workspaceRoot));
	if (target.client === "codex") return await inspectCodexConfig(target);
	return await inspectJsonConfig(target);
}
//#endregion
//#region src/cli/mcpClient/plan.ts
function resolveSupportedMcpClient(input) {
	if (input === "codex" || input === "claude-code" || input === "cursor") return input;
	throw new Error(`不支持的 MCP 客户端:${input}。当前仅支持 codex、claude-code、cursor。`);
}
function buildMcpClientConfigPlan(options) {
	const workspaceRoot = path.resolve(options.workspaceRoot);
	const target = resolveTarget(options.client, workspaceRoot);
	const entry = resolveConfigEntry(options.client, options.transport, workspaceRoot, options.url);
	if (options.transport === "http" && !entry.url) throw new Error("HTTP 模式缺少 MCP 服务地址,请使用 --url 指定或在当前项目中检测到可用地址。");
	return {
		entry,
		preview: target.client === "codex" ? renderCodexBlock(target.serverName, entry) : renderJsonPreview(target.serverName, entry),
		target,
		transport: options.transport
	};
}
function formatMcpQuickStart(options) {
	const suffix = options.transport === "http" && options.httpUrl ? ` --transport http --url ${options.httpUrl}` : "";
	return [
		"在 AI 工具中接入 weapp-vite MCP:",
		`  - Codex: wv mcp init codex${suffix}`,
		`  - Claude Code: wv mcp init claude-code${suffix}`,
		`  - Cursor: wv mcp init cursor${suffix}`
	];
}
//#endregion
//#region src/cli/mcpClient/index.ts
async function writeMcpClientConfig(plan) {
	await fs$1.ensureDir(path.dirname(plan.target.configPath));
	const existing = await fs$1.readFile(plan.target.configPath, "utf8").catch(() => "");
	if (plan.target.client === "codex") {
		const nextContent = upsertCodexManagedBlock(existing, plan.target.serverName, plan.preview);
		await fs$1.writeFile(plan.target.configPath, nextContent, "utf8");
		return;
	}
	const parsed = parseJsonConfig(existing, plan.target.configPath);
	const nextConfig = {
		...parsed,
		mcpServers: {
			...parsed.mcpServers ?? {},
			[plan.target.serverName]: plan.entry
		}
	};
	await fs$1.writeFile(plan.target.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
}
//#endregion
//#region src/cli/commands/mcp.ts
function resolvePort(port) {
	if (typeof port === "number" && Number.isInteger(port)) return port;
	if (typeof port === "string" && port.trim()) {
		const parsed = Number.parseInt(port, 10);
		if (Number.isInteger(parsed)) return parsed;
	}
}
async function resolveHttpUrl(options) {
	if (options.url?.trim()) return options.url.trim();
	const workspaceRoot = options.workspaceRoot ? path.resolve(options.workspaceRoot) : process.cwd();
	const originalCwd = process.cwd();
	try {
		if (workspaceRoot !== originalCwd) process.chdir(workspaceRoot);
		const loaded = await loadConfig(resolveConfigFile(options));
		const resolved = resolveWeappMcpConfig(loaded?.config?.weapp?.mcp);
		return `http://${resolved.host}:${resolved.port}${resolved.endpoint}`;
	} catch {
		const resolved = resolveWeappMcpConfig(void 0);
		return `http://${resolved.host}:${resolved.port}${resolved.endpoint}`;
	} finally {
		if (process.cwd() !== originalCwd) process.chdir(originalCwd);
	}
}
async function confirmWrite() {
	if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
	return await runWithSuspendedSharedInput(async () => {
		const rl = createInterface({
			input: process.stdin,
			output: process.stdout
		});
		try {
			const normalized = (await rl.question("是否写入配置文件?(Y/n) ")).trim().toLowerCase();
			return normalized === "" || normalized === "y" || normalized === "yes";
		} finally {
			rl.close();
		}
	});
}
function resolveClientTransport(transport) {
	return transport === "http" ? "http" : "command";
}
async function handlePrint(clientName, options, write = false) {
	const client = resolveSupportedMcpClient(clientName);
	const workspaceRoot = options.workspaceRoot ?? process.cwd();
	const transport = resolveClientTransport(options.transport);
	const plan = buildMcpClientConfigPlan({
		client,
		transport,
		url: transport === "http" ? await resolveHttpUrl(options) : void 0,
		workspaceRoot
	});
	logger_default.info(`${plan.target.displayName} 配置文件:${plan.target.configPath}`);
	logger_default.info(`服务器名称:${plan.target.serverName}`);
	process.stdout.write(`${plan.preview}\n`);
	if (!write) return;
	if (options.yes || await confirmWrite()) {
		await writeMcpClientConfig(plan);
		logger_default.success(`已写入 ${plan.target.displayName} MCP 配置。`);
		logger_default.info(`请重启 ${plan.target.displayName},然后执行:wv mcp doctor ${client}`);
		return;
	}
	logger_default.info("已取消写入。");
}
async function handleDoctor(clientName, options) {
	const result = await inspectMcpClientConfig({
		client: resolveSupportedMcpClient(clientName),
		workspaceRoot: options.workspaceRoot ?? process.cwd()
	});
	logger_default.info(`${result.displayName} 配置文件:${result.configPath}`);
	logger_default.info(`服务器名称:${result.serverName}`);
	if (!result.configExists || result.issues.length > 0) {
		for (const issue of result.issues) logger_default.warn(issue);
		throw new Error("MCP 客户端配置检查未通过。");
	}
	if (result.transport) logger_default.info(`传输模式:${result.transport}`);
	if (result.httpReachable !== void 0) logger_default.info(`HTTP 服务可达:${result.httpReachable ? "是" : "否"}`);
	logger_default.success("MCP 客户端配置检查通过。");
}
async function handleServer(options) {
	const resolvedTransport = options.transport === "http" ? "streamable-http" : options.transport === "command" ? void 0 : options.transport;
	await startWeappViteMcpServer({
		endpoint: options.endpoint,
		host: options.host,
		port: resolvePort(options.port),
		restEndpoint: options.rest === false ? false : options.restEndpoint,
		transport: resolvedTransport,
		unref: options.unref,
		workspaceRoot: options.workspaceRoot
	});
	for (const line of formatMcpQuickStart({
		httpUrl: resolvedTransport === "streamable-http" ? `http://${options.host ?? "127.0.0.1"}:${resolvePort(options.port) ?? 3088}${options.endpoint ?? "/mcp"}` : void 0,
		transport: resolvedTransport === "streamable-http" ? "http" : "command"
	})) logger_default.info(line);
}
function registerMcpCommand(cli) {
	cli.command("mcp [...args]", "start weapp-vite MCP server or manage MCP client onboarding").option("--transport <type>", "[string] stdio | streamable-http | command | http", { default: "stdio" }).option("--host <host>", "[string] streamable-http host").option("--port <port>", "[number] streamable-http port").option("--endpoint <path>", "[string] streamable-http endpoint path").option("--rest-endpoint <path>", "[string] streamable-http REST runtime endpoint path").option("--no-rest", "[boolean] disable streamable-http REST runtime endpoints").option("--unref", "[boolean] unref HTTP server to not block process exit").option("--url <url>", "[string] explicit HTTP MCP url").option("--workspace-root <path>", "[string] workspace root path, defaults to cwd").option("-y, --yes", "[boolean] write config without prompt").action(async (args, options) => {
		const [subcommand, client] = args;
		if (subcommand === "init") {
			if (!client) throw new Error("缺少客户端名称,请使用:wv mcp init <codex|claude-code|cursor>");
			await handlePrint(client, options, true);
			return;
		}
		if (subcommand === "print") {
			if (!client) throw new Error("缺少客户端名称,请使用:wv mcp print <codex|claude-code|cursor>");
			await handlePrint(client, options, false);
			return;
		}
		if (subcommand === "doctor") {
			if (!client) throw new Error("缺少客户端名称,请使用:wv mcp doctor <codex|claude-code|cursor>");
			await handleDoctor(client, options);
			return;
		}
		if (subcommand) throw new Error(`未知的 mcp 子命令:${subcommand}`);
		await handleServer(options);
	});
}
//#endregion
//#region src/cli/commands/npm.ts
function registerNpmCommand(cli) {
	cli.command("npm").alias("build:npm").alias("build-npm").action(async () => {
		try {
			await buildWechatIdeNpm({ projectPath: process.cwd() });
		} catch (error) {
			logger_default.error(error);
		}
	});
}
//#endregion
//#region src/cli/mcpDetached.ts
function resolveCliEntrypoint() {
	return process.argv[1];
}
function resolveManifestPath(cwd) {
	return path$1.join(cwd, ".weapp-vite", "mcp-runtime.json");
}
async function isTcpPortOpen(host, port) {
	return await new Promise((resolve) => {
		const socket = net.createConnection({
			host,
			port
		});
		const cleanup = () => {
			socket.removeAllListeners();
			socket.destroy();
		};
		socket.once("connect", () => {
			cleanup();
			resolve(true);
		});
		socket.once("error", () => {
			cleanup();
			resolve(false);
		});
		socket.setTimeout(500, () => {
			cleanup();
			resolve(false);
		});
	});
}
async function writeRuntimeManifest(cwd, manifest) {
	const manifestPath = resolveManifestPath(cwd);
	await mkdir(path$1.dirname(manifestPath), { recursive: true });
	await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
}
async function maybeStartDetachedMcpServer(options) {
	const resolvedMcp = resolveWeappMcpConfig(options.mcpConfig, {
		agentName: options.agentName,
		cwd: options.cwd,
		isAgent: options.isAgent
	});
	if (!resolvedMcp.enabled || !resolvedMcp.autoStart) return;
	const entrypoint = resolveCliEntrypoint();
	if (!entrypoint) {
		logger_default.warn("[mcp] 找不到当前 CLI 入口,跳过 MCP 后台自动启动。");
		return;
	}
	const url = `http://${resolvedMcp.host}:${resolvedMcp.port}${resolvedMcp.endpoint}`;
	const restUrl = resolvedMcp.restEndpoint === false ? void 0 : `http://${resolvedMcp.host}:${resolvedMcp.port}${resolvedMcp.restEndpoint}`;
	if (await isTcpPortOpen(resolvedMcp.host, resolvedMcp.port)) {
		logger_default.info(`[mcp] MCP 服务已存在,继续复用:${colors.cyan(url)}`);
		return;
	}
	const args = [
		entrypoint,
		"mcp",
		"--transport",
		"streamable-http",
		"--host",
		resolvedMcp.host,
		"--port",
		String(resolvedMcp.port),
		"--endpoint",
		resolvedMcp.endpoint,
		"--workspace-root",
		options.cwd
	];
	if (resolvedMcp.restEndpoint === false) args.push("--no-rest");
	else args.push("--rest-endpoint", resolvedMcp.restEndpoint);
	const child = spawn(process.execPath, args, {
		cwd: options.cwd,
		detached: true,
		stdio: "ignore"
	});
	child.unref();
	if (child.pid) await writeRuntimeManifest(options.cwd, {
		agentName: resolvedMcp.agentName,
		endpoint: resolvedMcp.endpoint,
		pid: child.pid,
		projectRoot: options.cwd,
		restUrl,
		startedAt: (/* @__PURE__ */ new Date()).toISOString(),
		startedBy: "weapp-vite open",
		transport: "streamable-http",
		url
	});
	const suffix = resolvedMcp.agentName ? `(AI 终端:${resolvedMcp.agentName})` : "";
	logger_default.success(`MCP 服务已在后台自动启动:${suffix}`);
	logger_default.info(`  ➜  ${colors.cyan(url)}`);
	if (restUrl) logger_default.info(`  REST ➜  ${colors.cyan(restUrl)}`);
	for (const line of formatMcpQuickStart({
		httpUrl: url,
		transport: "http"
	})) logger_default.info(line);
}
//#endregion
//#region src/cli/mcpOptions.ts
function applyMcpCliOptions(config, options) {
	if (options.mcp === false) return false;
	if (options.mcp === true) return {
		...typeof config === "object" && config ? config : {},
		enabled: true,
		autoStart: true
	};
	return config;
}
//#endregion
//#region src/cli/commands/open.ts
function registerOpenCommand(cli) {
	cli.command("open [root]").option("-p, --platform <platform>", `[string] target platform (weapp | web)`).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("--login-retry <mode>", "[string] login retry mode for Wechat DevTools (never | once | always)").option("--login-retry-timeout <ms>", "[number] login retry prompt timeout in milliseconds").option("--non-interactive", "[boolean] fail immediately when Wechat DevTools login has expired").option("--no-open-recovery", "[boolean] disable automatic Wechat DevTools close-and-reopen recovery").option("--mcp", "[boolean] auto start MCP service before opening IDE").option("--no-mcp", "[boolean] disable MCP service before opening IDE").action(async (root, options) => {
		filterDuplicateOptions(options);
		const configFile = resolveConfigFile(options);
		const targets = resolveRuntimeTargets(options);
		const ideBackend = getBackendForCapability(targets, "miniprogram", "ide");
		if (!ideBackend) throw new Error("`weapp-vite open` 当前仅支持小程序平台。");
		const { cwd, platform, projectPath, mpDistRoot, weappViteConfig } = await resolveIdeCommandContext({
			configFile,
			mode: options.mode ?? "development",
			platform: ideBackend.platform,
			projectPath: root,
			cliPlatform: targets.rawPlatform
		});
		const latestHmrSummary = await readLatestHmrProfileSummary({
			cwd: cwd ?? process.cwd(),
			relativeCwd: (value) => cwd ? value.replace(`${cwd}/`, "") : value,
			weappViteConfig
		});
		if (latestHmrSummary) logger_default.info(latestHmrSummary.line);
		const cwdForMcp = cwd ?? process.cwd();
		const aiEnvironment = await detectAiDevelopmentEnvironment();
		await maybeStartDetachedMcpServer({
			agentName: aiEnvironment.agentName,
			cwd: cwdForMcp,
			isAgent: aiEnvironment.isAgent,
			mcpConfig: applyMcpCliOptions(weappViteConfig?.mcp, options)
		});
		await openIde(platform, projectPath ?? resolveIdeProjectRoot(mpDistRoot, process.cwd()), {
			loginRetry: options.loginRetry,
			loginRetryTimeout: options.loginRetryTimeout,
			nonInteractive: options.nonInteractive,
			openRecovery: options.openRecovery,
			trustProject: options.trustProject,
			openStrategy: options.ideOpenStrategy
		});
	});
}
//#endregion
//#region src/cli/commands/prepare.ts
function resolvePrepareRoot(input) {
	const values = Array.isArray(input) ? input.filter((item) => typeof item === "string" && item.length > 0) : [];
	if (values.length === 0) return ".";
	return (values[0] === "prepare" ? values.slice(1) : values)[0] ?? ".";
}
function formatPrepareSkipMessage$1(error) {
	return `跳过 .weapp-vite 支持文件预生成:${error instanceof Error ? error.message : String(error)}`;
}
function resolvePreparePlatform(options) {
	return typeof options.platform === "string" ? options.platform : typeof options.p === "string" ? options.p : void 0;
}
function registerPrepareCommand(cli) {
	cli.command("prepare [...input]", "generate .weapp-vite support files").option("-p, --platform <platform>", `[string] target platform (weapp | web)`).action(async (input, options) => {
		try {
			filterDuplicateOptions(options);
			const cwd = path.resolve(resolvePrepareRoot(input));
			const cliPlatform = resolvePreparePlatform(options);
			const ctx = await createCompilerContext({
				cwd,
				isDev: false,
				mode: typeof options.mode === "string" ? options.mode : "development",
				configFile: resolveConfigFile(options),
				configLoader: "native",
				syncSupportFiles: false,
				preloadAppEntry: false,
				...cliPlatform ? { cliPlatform } : {}
			});
			const supportFiles = await syncProjectSupportFiles(ctx);
			for (const warning of supportFiles.managedTsconfigWarnings) logger_default.warn(warning);
			logger_default.info("已生成 .weapp-vite 支持文件。");
		} catch (error) {
			logger_default.warn(`[prepare] ${formatPrepareSkipMessage$1(error)}`);
		}
	});
}
//#endregion
//#region src/cli/devHotkeys/devtools.ts
async function appendLatestHmrSummary(baseSummary, options) {
	const summary = await readLatestHmrProfileSummary({
		cwd: options.cwd,
		relativeCwd: (value) => value.replace(`${options.cwd}/`, ""),
		weappViteConfig: options.weappViteConfig
	});
	if (!summary) return baseSummary;
	return `${baseSummary};${summary.line}`;
}
/**
* @description 重置当前 DevTools automator 共享会话。
*/
async function runResetDevtoolsSessionAction(options) {
	logger_default.info("[dev action] 正在重置当前 DevTools 会话...");
	await closeSharedMiniProgram(options.projectPath);
	logger_default.success("[dev action] 当前 DevTools 会话已重置。");
	return "已重置当前 DevTools 会话";
}
/**
* @description 重置共享会话并重开当前项目,以恢复 DevTools 状态。
*/
async function runResetAndReopenDevtoolsAction(options) {
	if (!options.openIde) {
		logger_default.warn("[dev action] 当前 dev 会话未提供重新打开微信开发者工具的能力。");
		return "重置并重开微信开发者工具不可用";
	}
	logger_default.info("[dev action] 正在重置当前 DevTools 会话并重开项目...");
	await closeSharedMiniProgram(options.projectPath);
	const summary = await options.openIde();
	logger_default.success("[dev action] 当前 DevTools 会话已重置,并已重新打开项目。");
	return await appendLatestHmrSummary(summary ?? "已重置当前 DevTools 会话并重新打开项目", options);
}
/**
* @description 手动触发一次当前小程序 dev 重新构建。
*/
async function runDevRebuildAction(options) {
	if (!options.rebuild) {
		logger_default.warn("[dev action] 当前 dev 会话未提供手动重新构建能力。");
		return "手动重新构建不可用";
	}
	logger_default.info("[dev action] 正在手动重新构建当前小程序产物...");
	const summary = await options.rebuild();
	logger_default.success("[dev action] 当前小程序产物已手动重新构建。");
	return summary ?? "已手动重新构建当前小程序产物";
}
/**
* @description 重新打开当前微信开发者工具项目,并主动清理共享 automator 会话。
*/
async function runOpenIdeAction(options) {
	if (!options.openIde) {
		logger_default.warn("[dev action] 当前 dev 会话未提供重新打开微信开发者工具的能力。");
		return "重新打开微信开发者工具不可用";
	}
	logger_default.info(`[dev action] 正在重新打开微信开发者工具项目:${colors.cyan(options.projectPath)}`);
	await closeSharedMiniProgram(options.projectPath);
	const summary = await options.openIde();
	logger_default.success("[dev action] 微信开发者工具项目已重新打开。");
	return await appendLatestHmrSummary(summary ?? "已重新打开微信开发者工具项目", options);
}
//#endregion
//#region src/cli/devHotkeys/screenshot.ts
const DEV_SCREENSHOT_DIR = ".weapp-vite/dev-screenshots";
const DEFAULT_SCREENSHOT_TIMEOUT = 12e4;
/**
* @description 生成开发态截图输出路径。
*/
function resolveDevScreenshotOutputPath(cwd, now = /* @__PURE__ */ new Date()) {
	const stamp = now.toISOString().replace(/[:.]/g, "-");
	return path.join(cwd, DEV_SCREENSHOT_DIR, `screenshot-${stamp}.png`);
}
function formatLogPath(cwd, targetPath) {
	const relativePath = path.relative(cwd, targetPath);
	if (!relativePath || relativePath.startsWith("..")) return targetPath;
	return relativePath;
}
function formatResolvedScreenshotPath(cwd, fallbackPath, result) {
	return formatLogPath(cwd, result.path ?? fallbackPath);
}
async function resolveDefaultScreenshotPage(cwd) {
	try {
		const raw = await fs$2.readFile(path.join(cwd, "app.json"), "utf8");
		const appJson = JSON.parse(raw);
		const [firstPage] = Array.isArray(appJson.pages) ? appJson.pages : [];
		return typeof firstPage === "string" && firstPage.trim() ? firstPage : void 0;
	} catch {
		return;
	}
}
/**
* @description 执行当前页面截图并输出结果日志。
*/
async function runScreenshotAction(options) {
	const outputPath = resolveDevScreenshotOutputPath(options.cwd);
	await fs$2.mkdir(path.dirname(outputPath), { recursive: true });
	const page = await resolveDefaultScreenshotPage(options.cwd);
	logger_default.info(`[dev action] 正在截图当前页面,输出到 ${colors.cyan(formatLogPath(options.cwd, outputPath))}`);
	const resumeForwardConsole = await pauseActiveForwardConsole();
	try {
		const result = await takeScreenshot({
			projectPath: options.projectPath,
			fullPage: true,
			...page ? { page } : {},
			preserveProjectRoot: true,
			port: resolveProjectAutomatorPort(options.projectPath),
			retryWithFreshSession: false,
			outputPath,
			timeout: DEFAULT_SCREENSHOT_TIMEOUT
		});
		const resolvedPath = formatResolvedScreenshotPath(options.cwd, outputPath, result);
		logger_default.success(`[dev action] 当前页面截图完成:${colors.cyan(resolvedPath)}`);
		return resolvedPath;
	} finally {
		await resumeForwardConsole?.();
	}
}
//#endregion
//#region src/cli/devHotkeys/actions.ts
const DEV_HOTKEY_GROUPS = [
	{
		key: "development",
		title: "开发动作"
	},
	{
		key: "devtools",
		title: "会话动作"
	},
	{
		key: "process",
		title: "进程控制"
	},
	{
		key: "help",
		title: "帮助"
	}
];
const DEV_HOTKEY_DEFINITIONS = [
	{
		description: "截图当前页面并保存到本地",
		group: "development",
		key: "s",
		label: "截图",
		pendingLabel: "正在截图当前页面",
		run: async ({ options }) => {
			return `截图已保存到 ${await runScreenshotAction(options)}`;
		}
	},
	{
		description: "手动重新构建当前小程序产物",
		group: "development",
		key: "r",
		label: "手动重新构建产物",
		pendingLabel: "正在手动重新构建当前小程序产物",
		run: async ({ options }) => {
			return await runDevRebuildAction(options);
		}
	},
	{
		description: "重置当前 DevTools automator 会话",
		group: "devtools",
		key: "c",
		label: "重置 DevTools 会话",
		pendingLabel: "正在重置当前 DevTools 会话",
		run: async ({ options }) => {
			return await runResetDevtoolsSessionAction(options);
		}
	},
	{
		description: "重置会话并重开当前微信开发者工具项目",
		group: "devtools",
		key: "C",
		label: "重置并重开项目",
		pendingLabel: "正在重置当前 DevTools 会话并重开项目",
		run: async ({ options }) => {
			return await runResetAndReopenDevtoolsAction(options);
		}
	},
	{
		description: "重新打开当前微信开发者工具项目",
		group: "devtools",
		key: "o",
		label: "重新打开微信开发者工具",
		pendingLabel: "正在重新打开微信开发者工具项目",
		run: async ({ options }) => {
			return await runOpenIdeAction(options);
		}
	},
	{
		description: "开关 MCP 服务",
		group: "devtools",
		key: "m",
		label: "MCP 切换",
		run: async ({ toggleMcp }) => {
			return await toggleMcp();
		}
	},
	{
		description: "退出当前 dev",
		group: "process",
		key: "q"
	},
	{
		description: "强制中断当前 dev",
		group: "process",
		key: "Ctrl+C"
	},
	{
		description: "暂时挂起当前 dev,恢复终端控制",
		group: "process",
		key: "Ctrl+Z"
	},
	{
		description: "重新显示这份帮助",
		group: "help",
		key: "h"
	}
];
function resolveDevHotkeyRowsByGroup() {
	return DEV_HOTKEY_GROUPS.map((group) => ({
		key: group.key,
		rows: DEV_HOTKEY_DEFINITIONS.filter((action) => action.group === group.key),
		title: group.title
	}));
}
function resolveRunnableHotkeyDefinition(input) {
	return DEV_HOTKEY_DEFINITIONS.find((action) => action.run && action.key === input);
}
//#endregion
//#region package.json
var version = "7.0.3";
//#endregion
//#region src/cli/devHotkeys/format.ts
const FULLWIDTH_ASCII_START = 65281;
const FULLWIDTH_ASCII_END = 65374;
const FULLWIDTH_ASCII_OFFSET = 65248;
const REG_PENDING_PREFIX = /^正在/;
function formatProjectLabel(cwd) {
	return path.basename(cwd) || cwd;
}
function formatMcpStatus(state) {
	if (!state.mcpEnabled) return "已禁用";
	return state.mcpRunning ? "运行中" : "未启动";
}
function formatMcpHelpRows(state) {
	if (!state.mcpEnabled || !state.mcpHttpUrl) return [];
	return [
		"",
		"MCP 接入",
		`HTTP:${colors.cyan(state.mcpHttpUrl)}`,
		...state.mcpRestUrl ? [`REST:${colors.cyan(state.mcpRestUrl)}`] : [],
		...formatMcpQuickStart({
			httpUrl: state.mcpHttpUrl,
			transport: "http"
		})
	];
}
function formatFooterLine(state) {
	if (state.currentAction) return `执行中    ${state.currentAction}`;
	return "就绪      等待操作...";
}
function formatHotkeyRows(rows) {
	const key = (value) => colors.bold(colors.green(value));
	const formattedRows = rows.map((row) => ({
		description: row.description,
		key: key(row.key)
	}));
	const keyColumnWidth = Math.max(...formattedRows.map((row) => row.key.length));
	return formattedRows.map(({ key, description }) => `按 ${key.padEnd(keyColumnWidth)}  ${description}`);
}
function formatExtraHotkeyRows(rows) {
	const key = (value) => colors.bold(colors.green(value));
	const formattedRows = rows.map(([hotkey, description]) => ({
		description,
		key: key(hotkey)
	}));
	const keyColumnWidth = Math.max(...formattedRows.map((row) => row.key.length));
	return formattedRows.map(({ key, description }) => `按 ${key.padEnd(keyColumnWidth)}  ${description}`);
}
/**
* @description 生成带状态的开发态快捷键帮助文本。
*/
function formatDevHotkeyHelpWithState(state) {
	const sections = resolveDevHotkeyRowsByGroup().filter((section) => section.rows.length > 0).flatMap((section) => [
		"",
		section.title,
		...formatHotkeyRows(section.rows)
	]);
	const retrySections = [
		"",
		"登录重试",
		...formatExtraHotkeyRows([[RETRY_CONFIRM_KEYS.join(" / "), "微信开发者工具登录失效后确认重试当前命令"], [RETRY_CANCEL_KEYS.join(" / "), "取消当前登录重试提示"]])
	];
	return [
		`${colors.bold(colors.green("DEV"))}  weapp-vite v${version}  ${state.projectLabel ?? "weapp"}`,
		...sections,
		...retrySections,
		"",
		`当前状态:${state.currentAction ?? "等待操作"} / MCP ${formatMcpStatus(state)}`,
		...state.lastAction ? [`最近操作:${state.lastAction}`] : [],
		...formatMcpHelpRows(state)
	].join("\n");
}
/**
* @description 生成带状态的开发态快捷键简短提示。
*/
function formatDevHotkeyHintWithState(state) {
	const key = (value) => colors.bold(colors.green(value));
	if (state.currentAction) return `${formatFooterLine(state)},按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
	if (state.lastAction) return `开发快捷键已就绪,最近操作:${state.lastAction},按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
	return `开发快捷键已就绪,按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
}
function normalizeInputChar(input) {
	if (input.length !== 1) return input;
	const codePoint = input.codePointAt(0);
	if (!codePoint) return input;
	if (codePoint >= FULLWIDTH_ASCII_START && codePoint <= FULLWIDTH_ASCII_END) return String.fromCodePoint(codePoint - FULLWIDTH_ASCII_OFFSET);
	return input;
}
function resolveProjectLabel(cwd) {
	return formatProjectLabel(cwd);
}
//#endregion
//#region src/cli/devHotkeys/mcp.ts
const REG_EADDRINUSE$1 = /EADDRINUSE/;
function formatMcpUrl$1(host, port, endpoint) {
	return `http://${host}:${port}${endpoint}`;
}
function createToggleMcpAction(options) {
	const { cwd, getHandle, resolvedMcp, setHandle } = options;
	return async function toggleMcp(actionOptions = {}) {
		const silent = actionOptions.silent === true;
		if (!resolvedMcp.enabled) {
			logger_default.warn("[dev action] MCP 已在配置中禁用,跳过切换。");
			return "MCP 已禁用";
		}
		const existingHandle = getHandle();
		if (existingHandle?.close) {
			const url = formatMcpUrl$1(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
			logger_default.info(`[dev action] 正在关闭 MCP 服务:${colors.cyan(url)}`);
			await existingHandle.close();
			setHandle(void 0);
			logger_default.success(`[dev action] MCP 服务已关闭:${colors.cyan(url)}`);
			return `MCP 已关闭 (${url})`;
		}
		const url = formatMcpUrl$1(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
		if (!silent) logger_default.info(`[dev action] 正在启动 MCP 服务:${colors.cyan(url)}`);
		let handle;
		try {
			handle = await startWeappViteMcpServer({
				endpoint: resolvedMcp.endpoint,
				host: resolvedMcp.host,
				port: resolvedMcp.port,
				restEndpoint: resolvedMcp.restEndpoint,
				transport: "streamable-http",
				unref: false,
				onReady: () => {},
				workspaceRoot: cwd
			});
		} catch (error) {
			const message = error instanceof Error ? error.message : String(error);
			if (REG_EADDRINUSE$1.test(message)) {
				if (!silent) logger_default.info(`[dev action] MCP 服务已存在,继续复用:${colors.cyan(url)}`);
				return `MCP 已复用 (${url})`;
			}
			throw error;
		}
		setHandle(handle);
		const suffix = resolvedMcp.agentName ? `(AI 终端:${resolvedMcp.agentName})` : "";
		if (!silent) logger_default.success(`[dev action] MCP 服务已启动:${colors.cyan(url)}${suffix}`);
		return `MCP 已启动 (${url})`;
	};
}
//#endregion
//#region src/cli/devHotkeys/index.ts
function forwardSigint() {
	process.kill(process.pid, "SIGINT");
}
function forwardSigtstp() {
	process.kill(process.pid, "SIGTSTP");
}
function formatMcpUrl(host, port, endpoint) {
	return `http://${host}:${port}${endpoint}`;
}
/**
* @description 在开发态启动终端快捷键,并将动作输出到本地日志。
*/
function startDevHotkeys(options) {
	if (options.platform !== "weapp" || !process.stdin.isTTY) return;
	let closed = false;
	let running = false;
	let mcpHandle;
	let onData;
	let onKeypress;
	let onSigcont;
	let currentAction;
	let actionStartedAt;
	let lastAction;
	let lastRenderedPanel = "";
	const recentInputs = /* @__PURE__ */ new Map();
	const resolvedMcp = resolveWeappMcpConfig(options.mcpConfig, {
		agentName: options.agentName,
		cwd: options.cwd,
		isAgent: options.isAgent
	});
	const mcpHttpUrl = formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
	const mcpRestUrl = resolvedMcp.restEndpoint === false ? void 0 : formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.restEndpoint);
	const getState = () => ({
		currentAction,
		lastAction,
		mcpEnabled: resolvedMcp.enabled,
		mcpHttpUrl,
		mcpRunning: Boolean(mcpHandle?.close),
		mcpRestUrl,
		projectLabel: resolveProjectLabel(options.cwd)
	});
	let inputSession = createSharedInputSession({
		onData: (chunk) => {
			onData?.(chunk);
		},
		onKeypress: (str, key) => {
			onKeypress?.(str, key);
		}
	});
	const close = () => {
		if (closed) return;
		closed = true;
		if (onSigcont) process.off("SIGCONT", onSigcont);
		inputSession?.close();
		inputSession = void 0;
		if (mcpHandle?.close) mcpHandle.close().catch((error) => {
			logger_default.warn(`[dev action] MCP 服务关闭失败:${error instanceof Error ? error.message : String(error)}`);
		});
		closeSharedMiniProgram(options.projectPath).catch((error) => {
			logger_default.warn(`[dev action] DevTools 会话关闭失败:${error instanceof Error ? error.message : String(error)}`);
		});
	};
	const printPanel = (message, force = false) => {
		if (!force && message === lastRenderedPanel) return;
		lastRenderedPanel = message;
		logger_default.info(message);
	};
	const printHelp = () => {
		printPanel(formatDevHotkeyHelpWithState(getState()), true);
	};
	const printHint = () => {
		printPanel(formatDevHotkeyHintWithState(getState()));
	};
	const restore = () => {
		if (closed) return;
		inputSession?.resume();
		printHint();
	};
	const suspendInput = () => {
		if (closed) return;
		inputSession?.suspend();
	};
	const suspend = () => {
		lastRenderedPanel = "";
		suspendInput();
		forwardSigtstp();
	};
	const toggleMcp = createToggleMcpAction({
		cwd: options.cwd,
		getHandle: () => mcpHandle,
		resolvedMcp,
		setHandle: (handle) => {
			mcpHandle = handle;
		}
	});
	const resolvePendingLabel = (input) => {
		if (input === "m") return mcpHandle?.close ? "正在关闭 MCP 服务" : "正在启动 MCP 服务";
	};
	const runAction = (label, pendingLabel, action) => {
		if (running) {
			const current = currentAction ?? "已有命令";
			const elapsed = actionStartedAt === void 0 ? "" : `(已运行 ${Math.max(0, Date.now() - actionStartedAt)} ms)`;
			logger_default.warn(`[dev action] 当前正在${current.replace(REG_PENDING_PREFIX, "")}${elapsed},已忽略重复请求。`);
			return;
		}
		running = true;
		actionStartedAt = Date.now();
		currentAction = pendingLabel;
		printHint();
		action().then((summary) => {
			if (summary) lastAction = summary;
		}).catch((error) => {
			logger_default.error(`[dev action] ${label}失败:${error instanceof Error ? error.message : String(error)}`);
		}).finally(() => {
			running = false;
			actionStartedAt = void 0;
			currentAction = void 0;
			if (!closed) printHint();
		});
	};
	const runBackgroundAction = (label, pendingLabel, action) => {
		if (running) return;
		running = true;
		actionStartedAt = Date.now();
		currentAction = pendingLabel;
		action().then((summary) => {
			if (summary) lastAction = summary;
		}).catch((error) => {
			logger_default.error(`[dev action] ${label}失败:${error instanceof Error ? error.message : String(error)}`);
		}).finally(() => {
			running = false;
			actionStartedAt = void 0;
			currentAction = void 0;
		});
	};
	const handleInput = (input) => {
		if (closed) return;
		const normalizedInput = normalizeInputChar(input);
		if (normalizedInput === "") {
			close();
			forwardSigint();
			return;
		}
		if (normalizedInput === "") {
			suspend();
			return;
		}
		const normalized = normalizedInput.toLowerCase();
		if (normalized === "q") {
			close();
			forwardSigint();
			return;
		}
		if (normalized === "h") {
			printHelp();
			return;
		}
		const action = resolveRunnableHotkeyDefinition(normalizedInput) ?? resolveRunnableHotkeyDefinition(normalized);
		const run = action?.run;
		if (run) runAction(action.label ?? action.description, resolvePendingLabel(normalized) ?? action.pendingLabel ?? `正在执行 ${action.description}`, async () => {
			return await run({
				options,
				toggleMcp
			});
		});
	};
	const handleInputOnce = (input, source) => {
		const normalizedInput = normalizeInputChar(input);
		const now = Date.now();
		for (const [token, timestamp] of recentInputs) if (now - Number(timestamp.split(":")[1] ?? 0) > 32) recentInputs.delete(token);
		const dedupKey = normalizedInput;
		const recentEntry = recentInputs.get(dedupKey);
		if (recentEntry !== void 0) {
			const [recentSource, recentTimestamp] = recentEntry.split(":");
			if (recentSource !== source && now - Number(recentTimestamp) <= 32) return;
		}
		recentInputs.set(dedupKey, `${source}:${now}`);
		handleInput(normalizedInput);
	};
	onData = (chunk) => {
		const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
		for (const char of text) handleInputOnce(char, "data");
	};
	onKeypress = (str, key) => {
		if (key?.ctrl && key.name === "c") {
			handleInputOnce("", "keypress");
			return;
		}
		if (key?.ctrl && key.name === "z") {
			handleInputOnce("", "keypress");
			return;
		}
		if (typeof str === "string" && str) handleInputOnce(str, "keypress");
	};
	onSigcont = () => {
		restore();
	};
	process.on("SIGCONT", onSigcont);
	if (!options.silentStartupHint) printHint();
	if (resolvedMcp.enabled && resolvedMcp.autoStart) runBackgroundAction("MCP 自动启动", "正在启动 MCP 服务", async () => {
		await toggleMcp({ silent: true });
	});
	return {
		close,
		restore,
		suspend: suspendInput
	};
}
//#endregion
//#region src/cli/commands/serve/analyze.ts
const REG_DIST_PAGE_ENTRY = /pages\/.+\/index\.js$/;
const REG_DIST_POSIX_SEP = /\\/g;
function emitDashboardEvents(handle, events) {
	handle?.emitRuntimeEvents(events);
}
function formatHmrProfileFile(profile) {
	return profile.sourceRootFile ?? profile.relativeFile ?? profile.file ?? "未知文件";
}
function createHmrProfileEvent(profile) {
	if (!profile || typeof profile.totalMs !== "number") return;
	const file = formatHmrProfileFile(profile);
	const counts = [
		typeof profile.dirtyCount === "number" ? `dirty ${profile.dirtyCount}` : void 0,
		typeof profile.pendingCount === "number" ? `pending ${profile.pendingCount}` : void 0,
		typeof profile.emittedCount === "number" ? `emitted ${profile.emittedCount}` : void 0
	].filter(Boolean).join(" / ");
	return {
		kind: "hmr",
		level: profile.totalMs >= 1e3 ? "warning" : "success",
		title: "mini hmr rebuild completed",
		detail: counts ? `${file} 已完成热更新重建(${counts})。` : `${file} 已完成热更新重建。`,
		durationMs: profile.totalMs,
		tags: ["hmr", "rebuild"],
		profile
	};
}
function hasAnalyzeData(result) {
	return result.packages.length > 0 || result.modules.length > 0;
}
async function collectOutputFiles(root) {
	const entries = await fs$2.readdir(root, { withFileTypes: true });
	const files = [];
	for (const entry of entries) {
		const absolutePath = path.join(root, entry.name);
		if (entry.isDirectory()) {
			files.push(...await collectOutputFiles(absolutePath));
			continue;
		}
		if (entry.isFile()) files.push(absolutePath);
	}
	return files;
}
function getCompressedSizes(content) {
	return {
		gzipSize: gzipSync(content).byteLength,
		brotliSize: brotliCompressSync(content).byteLength
	};
}
async function analyzeUiFallback(ctx) {
	const { configService, scanService } = ctx;
	await scanService.loadAppEntry();
	const subPackageMetas = scanService.loadSubPackages();
	const distRoot = configService.outDir;
	const absoluteFiles = await collectOutputFiles(distRoot);
	const packages = /* @__PURE__ */ new Map();
	const ensurePackage = (packageId, packageType) => {
		const existing = packages.get(packageId);
		if (existing) return existing;
		const created = {
			id: packageId,
			label: packageId === "__main__" ? "主包" : packageType === "independent" ? `独立分包 ${packageId}` : `分包 ${packageId}`,
			type: packageType,
			files: []
		};
		packages.set(packageId, created);
		return created;
	};
	const classifyPackage = (relativeFile) => {
		const normalized = relativeFile.replace(REG_DIST_POSIX_SEP, "/");
		for (const meta of subPackageMetas) {
			const root = meta.subPackage.root;
			if (root && normalized.startsWith(`${root}/`)) return {
				id: root,
				type: meta.subPackage.independent ? "independent" : "subPackage"
			};
		}
		return {
			id: "__main__",
			type: "main"
		};
	};
	for (const absoluteFile of absoluteFiles) {
		const relativeFile = path.relative(distRoot, absoluteFile).replace(REG_DIST_POSIX_SEP, "/");
		const packageInfo = classifyPackage(relativeFile);
		const stat = await fs$2.stat(absoluteFile);
		const content = await fs$2.readFile(absoluteFile);
		ensurePackage(packageInfo.id, packageInfo.type).files.push({
			file: relativeFile,
			type: relativeFile.endsWith(".js") ? "chunk" : "asset",
			from: packageInfo.type === "independent" ? "independent" : "main",
			size: stat.size,
			...getCompressedSizes(content),
			isEntry: relativeFile === "app.js" || REG_DIST_PAGE_ENTRY.test(relativeFile),
			source: relativeFile.endsWith(".js") ? void 0 : relativeFile
		});
	}
	return {
		metadata: createAnalyzeMetadata(configService),
		packages: Array.from(packages.values()).sort((a, b) => {
			if (a.id === "__main__") return -1;
			if (b.id === "__main__") return 1;
			return a.id.localeCompare(b.id);
		}),
		modules: [],
		subPackages: subPackageMetas.map((meta) => ({
			root: meta.subPackage.root ?? "",
			independent: Boolean(meta.subPackage.independent),
			name: meta.subPackage.name
		})).filter((item) => item.root).sort((a, b) => a.root.localeCompare(b.root)),
		glassEasel: createGlassEaselAnalyzeResult(ctx)
	};
}
function createAnalyzeController(options) {
	const { configFile, ctx, options: cliOptions, targets } = options;
	const { configService } = ctx;
	let analyzeRunId = 0;
	let analyzeHandle;
	const runAnalyze = async () => {
		const startedAt = Date.now();
		const previousResult = await readLatestAnalyzeHistorySnapshot(configService);
		try {
			const result = await analyzeSubpackages(await createCompilerContext({
				key: `serve-ui-analyze:${process.pid}:${++analyzeRunId}`,
				cwd: configService.cwd,
				mode: configService.mode,
				isDev: false,
				configFile,
				inlineConfig: createInlineConfig(targets),
				cliPlatform: targets.rawPlatform,
				projectConfigPath: cliOptions.projectConfig,
				syncSupportFiles: false
			}));
			if (hasAnalyzeData(result)) {
				await writeAnalyzeHistorySnapshot(result, configService);
				return {
					result,
					previousResult,
					durationMs: Date.now() - startedAt,
					mode: "full"
				};
			}
		} catch (error) {
			const message = error instanceof Error ? error.message : String(error);
			logger_default.warn(`[ui] 完整分析失败,已回退到 dist 文件扫描:${message}`);
			const result = await analyzeUiFallback(ctx);
			await writeAnalyzeHistorySnapshot(result, configService);
			return {
				result,
				previousResult,
				durationMs: Date.now() - startedAt,
				mode: "fallback",
				fallbackReason: message
			};
		}
		const result = await analyzeUiFallback(ctx);
		await writeAnalyzeHistorySnapshot(result, configService);
		return {
			result,
			previousResult,
			durationMs: Date.now() - startedAt,
			mode: "fallback",
			fallbackReason: "完整分析结果为空,已回退到 dist 文件扫描。"
		};
	};
	const triggerAnalyzeUpdate = async (reason = "watch") => {
		if (!analyzeHandle) return;
		emitDashboardEvents(analyzeHandle, [{
			kind: reason === "watch" ? "hmr" : "build",
			level: "info",
			title: reason === "watch" ? "analyze refresh started" : "initial analyze started",
			detail: reason === "watch" ? "检测到新的构建结束事件,开始刷新 analyze 面板。" : "开发态 UI 已启动,开始生成第一份 analyze 结果。",
			tags: reason === "watch" ? ["watch", "analyze"] : ["initial", "analyze"]
		}]);
		const next = await runAnalyze();
		if (next.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
			kind: "diagnostic",
			level: "warning",
			title: "analyze fallback enabled",
			detail: next.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
			durationMs: next.durationMs,
			tags: ["analyze", "fallback"]
		}]);
		await analyzeHandle.update(next.result, next.previousResult);
		emitDashboardEvents(analyzeHandle, [{
			kind: next.mode === "fallback" ? "diagnostic" : "build",
			level: next.mode === "fallback" ? "warning" : "success",
			title: reason === "watch" ? "analyze refresh completed" : "initial analyze completed",
			detail: next.mode === "fallback" ? `analyze 已回退到 dist 扫描,当前包含 ${next.result.packages.length} 个包。` : `analyze 已刷新完成,当前包含 ${next.result.packages.length} 个包与 ${next.result.modules.length} 个模块。`,
			durationMs: next.durationMs,
			tags: next.mode === "fallback" ? ["analyze", "fallback"] : ["analyze", reason === "watch" ? "refresh" : "initial"]
		}]);
	};
	const bindWatcher = (buildResult) => {
		let updating = false;
		if (analyzeHandle && buildResult && typeof buildResult.on === "function") buildResult.on("event", (event) => {
			if (event.code !== "END" || updating) return;
			const recentProfiles = ctx.runtimeState.build.hmr.recentProfiles;
			const hmrEvent = createHmrProfileEvent(recentProfiles[recentProfiles.length - 1]);
			if (hmrEvent) emitDashboardEvents(analyzeHandle, [hmrEvent]);
			updating = true;
			triggerAnalyzeUpdate("watch").finally(() => {
				updating = false;
			});
		});
		return { async runInitialUpdate() {
			if (analyzeHandle) {
				updating = true;
				await triggerAnalyzeUpdate("initial");
				updating = false;
			}
		} };
	};
	return {
		getHandle: () => analyzeHandle,
		async startDashboard(startDashboard) {
			const initialAnalyze = await runAnalyze();
			analyzeHandle = await startDashboard(initialAnalyze.result, {
				watch: true,
				artifactRoot: configService.outDir,
				cwd: configService.cwd,
				packageManagerAgent: configService.packageManager.agent,
				silentStartupLog: true,
				previousResult: initialAnalyze.previousResult,
				initialEvents: [{
					kind: initialAnalyze.mode === "fallback" ? "diagnostic" : "build",
					level: initialAnalyze.mode === "fallback" ? "warning" : "success",
					title: initialAnalyze.mode === "fallback" ? "initial analyze fallback completed" : "initial analyze completed",
					detail: initialAnalyze.mode === "fallback" ? initialAnalyze.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。" : `开发态首份 analyze 结果已生成,包含 ${initialAnalyze.result.packages.length} 个包与 ${initialAnalyze.result.modules.length} 个模块。`,
					durationMs: initialAnalyze.durationMs,
					tags: initialAnalyze.mode === "fallback" ? [
						"analyze",
						"fallback",
						"initial"
					] : ["analyze", "initial"]
				}]
			}) ?? void 0;
			emitDashboardEvents(analyzeHandle, [{
				kind: "command",
				level: "success",
				title: "dev ui session ready",
				detail: `开发态分析面板已启动,当前包含 ${initialAnalyze.result.packages.length} 个包。`,
				durationMs: initialAnalyze.durationMs,
				tags: ["dev", "ui"]
			}]);
			if (initialAnalyze.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
				kind: "diagnostic",
				level: "warning",
				title: "initial analyze fallback enabled",
				detail: initialAnalyze.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
				durationMs: initialAnalyze.durationMs,
				tags: [
					"analyze",
					"fallback",
					"initial"
				]
			}]);
		},
		bindWatcher,
		emitRuntimeEvents(events) {
			emitDashboardEvents(analyzeHandle, events);
		},
		waitForExit() {
			return analyzeHandle?.waitForExit();
		}
	};
}
//#endregion
//#region src/cli/commands/serve/shared.ts
function startBackgroundForwardConsole(startForwardConsole) {
	if (!startForwardConsole) return;
	try {
		startForwardConsole().catch((error) => {
			const message = error instanceof Error ? error.message : String(error);
			logger_default.warn(`[forwardConsole] 后台启动失败:${message}`);
		});
	} catch (error) {
		const message = error instanceof Error ? error.message : String(error);
		logger_default.warn(`[forwardConsole] 后台启动失败:${message}`);
	}
}
function resolveWebHost(host) {
	if (host === void 0) return;
	if (typeof host === "boolean") return host;
	if (typeof host === "string") return host;
	return String(host);
}
/**
* @description 为 serve 模式构造统一的小程序开发动作,供热键与命令复用。
*/
function createServeMiniProgramDevActions(options) {
	const projectPath = options.projectPath ?? options.fallbackProjectPath;
	return {
		projectPath,
		openIde: async (openOptions = {}) => {
			if (!openOptions.forceOpen && await options.tryReuseForwardConsole?.()) return "已通过控制台转发复用当前开发者工具会话";
			await options.openIde(projectPath, openOptions);
			startBackgroundForwardConsole(options.startForwardConsole);
			return openOptions.forceReopen ? "已重新打开微信开发者工具项目" : "已打开或复用微信开发者工具项目";
		},
		rebuild: async () => {
			await options.build();
			return "已手动重新构建当前小程序产物";
		}
	};
}
function waitForServeShutdownSignal() {
	return new Promise((resolve) => {
		const onSignal = () => {
			process.off("SIGINT", onSignal);
			process.off("SIGTERM", onSignal);
			resolve();
		};
		process.on("SIGINT", onSignal);
		process.on("SIGTERM", onSignal);
	});
}
//#endregion
//#region src/cli/commands/serve/index.ts
function writePostOpenSeparator() {
	process.stdout?.write?.("\n");
}
function registerServeCommand(cli) {
	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 | web | 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("--ide-open-strategy <strategy>", "[string] IDE open strategy (cli | automator)", { default: "cli" }).option("--login-retry <mode>", "[string] login retry mode for Wechat DevTools (never | once | always)").option("--login-retry-timeout <ms>", "[number] login retry prompt timeout in milliseconds").option("--non-interactive", "[boolean] fail immediately when Wechat DevTools login has expired").option("--no-open-recovery", "[boolean] disable automatic Wechat DevTools close-and-reopen recovery").option("--mcp", "[boolean] auto start MCP service during dev").option("--no-mcp", "[boolean] disable MCP service during dev").option("--host [host]", `[string] web dev server host`).option("--ui", `[boolean] 启动调试 UI(当前提供分析视图)`, { default: false }).option("--analyze", `[boolean] 启动分包分析仪表盘 (实验特性)`, { default: false }).option("--scope <scope>", `[string] 局部构建范围,例如 main,packages/order`).action(async (root, options) => {
		filterDuplicateOptions(options);
		setCommandNodeEnv("development");
		const cwd = root ?? process.cwd();
		const configFile = resolveConfigFile(options);
		const targets = resolveRuntimeTargets(options);
		const webBackend = getBackendForCapability(targets, "web", "dev");
		const host = webBackend ? resolveWebHost(options.host) : void 0;
		const inlineConfig = createInlineConfig(targets, {
			scope: options.scope,
			host
		});
		const ctx = await createCompilerContext({
			cwd,
			mode: options.mode ?? "development",
			isDev: true,
			configFile,
			inlineConfig,
			cliPlatform: targets.rawPlatform,
			preloadAppEntry: false,
			syncAutoImportSupportFiles: false,
			projectConfigPath: options.projectConfig
		});
		const { configService } = ctx;
		const miniBackend = getBackendForCapability(targets, "miniprogram", "dev");
		const aiEnvironment = await detectAiDevelopmentEnvironment();
		const mcpConfig = applyMcpCliOptions(configService.weappViteConfig?.mcp, options);
		logRuntimeTarget(targets, { resolvedConfigPlatform: configService.platform });
		const enableAnalyze = Boolean(isUiEnabled(options) && miniBackend);
		let analyzeHandle;
		const miniProgramDevActions = createServeMiniProgramDevActions({
			build: async () => {
				await miniBackend?.driver.dev(ctx, options);
			},
			fallbackProjectPath: configService.cwd,
			openIde: async (projectPath, openOptions) => {
				const forceReopen = openOptions?.forceReopen === true;
				const useAutomatorOpen = openOptions?.openStrategy === "automator" || openOptions?.useAutomatorOpen === true;
				await openIde(configService.platform, projectPath, {
					loginRetry: options.loginRetry,
					loginRetryTimeout: options.loginRetryTimeout,
					nonInteractive: options.nonInteractive,
					openRecovery: false,
					prepareAutomatorSession: useAutomatorOpen,
					reuseOpenedProject: !forceReopen,
					skipAutomatorCompile: !forceReopen,
					skipPostOpenHealthCheck: true,
					trustProject: options.trustProject,
					openStrategy: openOptions?.openStrategy ?? options.ideOpenStrategy ?? "cli",
					useAutomatorOpen
				});
				writePostOpenSeparator();
			},
			projectPath: resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd),
			startForwardConsole: async () => {
				return await maybeStartForwardConsole({
					platform: configService.platform,
					mpDistRoot: configService.mpDistRoot,
					cwd: configService.cwd,
					preferOpenedSession: false,
					recoverAutomatorSession: async () => {
						const projectPath = resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd);
						await openIde(configService.platform, projectPath, {
							loginRetry: options.loginRetry,
							loginRetryTimeout: options.loginRetryTimeout,
							nonInteractive: options.nonInteractive,
							openRecovery: false,
							openStrategy: "automator",
							prepareAutomatorSession: true,
							reuseOpenedProject: false,
							skipAutomatorCompile: false,
							skipPostOpenHealthCheck: true,
							trustProject: options.trustProject,
							useAutomatorOpen: true
						});
					},
					weappViteConfig: configService.weappViteConfig
				});
			},
			tryReuseForwardConsole: async () => {
				return await maybeStartForwardConsole({
					openedOnly: true,
					platform: configService.platform,
					mpDistRoot: configService.mpDistRoot,
					cwd: configService.cwd,
					weappViteConfig: configService.weappViteConfig
				});
			}
		});
		const devHotkeysSession = miniBackend ? startDevHotkeys({
			cwd: configService.cwd,
			agentName: aiEnvironment.agentName,
			isAgent: aiEnvironment.isAgent,
			mcpConfig,
			openIde: async () => await miniProgramDevActions.openIde({
				forceOpen: true,
				forceReopen: true
			}),
			platform: configService.platform,
			projectPath: miniProgramDevActions.projectPath ?? configService.cwd,
			rebuild: miniProgramDevActions.rebuild,
			silentStartupHint: true,
			weappViteConfig: configService.weappViteConfig
		}) : void 0;
		try {
			const analyzeController = createAnalyzeController({
				configFile,
				ctx,
				options,
				targets
			});
			let webServer;
			for (const backend of targets.select("dev")) {
				if (backend.descriptor.id === "miniprogram") {
					const miniBuildStartedAt = Date.now();
					const buildResult = await backend.driver.dev(ctx, options);
					const miniBuildDurationMs = Date.now() - miniBuildStartedAt;
					logger_default.success(`小程序初次构建完成,耗时:${formatDuration(miniBuildDurationMs)}`);
					ctx.autoImportService?.syncSupportFileResolverComponents().catch((error) => {
						const message = error instanceof Error ? error.message : String(error);
						logger_default.warn(`[prepare] 后台同步 .weapp-vite 支持文件失败:${message}`);
					});
					if (enableAnalyze) {
						await analyzeController.startDashboard(startAnalyzeDashboard);
						analyzeHandle = analyzeController.getHandle();
						analyzeController.emitRuntimeEvents([{
							kind: "build",
							level: "success",
							title: "mini initial build completed",
							detail: "小程序开发态初次构建已完成,dashboard 可继续监听后续刷新。",
							durationMs: miniBuildDurationMs,
							tags: [
								"dev",
								"mini",
								"initial"
							]
						}]);
						await analyzeController.bindWatcher(buildResult).runInitialUpdate();
					}
					continue;
				}
				if (backend.descriptor.id !== "web") continue;
				const webServerStartedAt = Date.now();
				try {
					webServer = await backend.driver.dev(ctx, options);
					logger_default.success(`Web 开发服务启动完成,耗时:${formatDuration(Date.now() - webServerStartedAt)}`);
					analyzeController.emitRuntimeEvents([{
						kind: "system",
						level: "success",
						title: "web dev server started",
						detail: "Web 开发服务器已启动,可与小程序调试 UI 并行工作。",
						durationMs: Date.now() - webServerStartedAt,
						tags: ["dev", "web"]
					}]);
				} catch (error) {
					analyzeController.emitRuntimeEvents([{
						kind: "diagnostic",
						level: "error",
						title: "web dev server failed",
						detail: error instanceof Error ? error.message : String(error),
						durationMs: Date.now() - webServerStartedAt,
						tags: ["dev", "web"]
					}]);
					logger_default.error(error);
					throw error;
				}
			}
			if (miniBackend) {
				logBuildAppFinish(configService, webServer, {
					skipWeb: !webBackend,
					uiUrls: analyzeHandle?.urls
				});
				devHotkeysSession?.restore();
			} else if (webBackend) logBuildAppFinish(configService, webServer, { skipMini: true });
			if (options.open && getBackendForCapability(targets, "miniprogram", "ide")) {
				analyzeController.emitRuntimeEvents([{
					kind: "command",
					level: "info",
					title: "opening ide",
					detail: "开发服务已就绪,准备打开 IDE 项目。",
					tags: ["ide", "open"]
				}]);
				devHotkeysSession?.suspend();
				try {
					await miniProgramDevActions.openIde({
						forceOpen: true,
						forceReopen: false,
						openStrategy: options.ideOpenStrategy ?? "cli"
					});
				} finally {
					devHotkeysSession?.restore();
				}
			}
			if (analyzeHandle) await analyzeController.waitForExit();
			else if (targets.has("dev")) await waitForServeShutdownSignal();
		} finally {
			devHotkeysSession?.close();
			await closeActiveForwardConsole();
			for (const backend of [...targets.select("dev")].reverse()) await backend.driver.close(ctx);
		}
	});
}
//#endregion
//#region src/cli/error.ts
const watchLimitErrorCodes = /* @__PURE__ */ new Set(["EMFILE", "ENOSPC"]);
const watchLimitMessagePattern = /EMFILE|ENOSPC|unable to start FSEvent stream/i;
function findWatchLimitErrorCode(error) {
	const visited = /* @__PURE__ */ new Set();
	const visit = (value) => {
		if (!value || typeof value !== "object") return;
		if (visited.has(value)) return;
		visited.add(value);
		const maybeError = value;
		if (typeof maybeError.code === "string" && watchLimitErrorCodes.has(maybeError.code)) return maybeError.code;
		if (typeof maybeError.message === "string" && watchLimitMessagePattern.test(maybeError.message)) {
			const match = maybeError.message.match(/EMFILE|ENOSPC/i);
			if (match?.[0]) return match[0].toUpperCase();
			return "EMFILE";
		}
		if (Array.isArray(maybeError.errors)) for (const item of maybeError.errors) {
			const found = visit(item);
			if (found) return found;
		}
		const fromError = visit(maybeError.error);
		if (fromError) return fromError;
		return visit(maybeError.cause);
	};
	return visit(error);
}
function handleCLIError(error) {
	const code = findWatchLimitErrorCode(error);
	if (code) {
		const tip = process.platform === "darwin" ? "可先执行 `ulimit -n 65536` 后重试。" : "请提高系统文件描述符上限后重试。";
		logger_default.error(`[监听] 文件监听数量达到上限 (${code}),${tip}`);
		return;
	}
	logger_default.error(error);
}
//#endregion
//#region src/cli/ide.ts
const WEAPP_VITE_NATIVE_COMMANDS = /* @__PURE__ */ new Set([
	"dev",
	"serve",
	"build",
	"close",
	"analyze",
	"alipay",
	"init",
	"open",
	"npm",
	"build:npm",
	"build-npm",
	"generate",
	"g",
	"mcp"
]);
/**
* @description 让 weapp-vite 在未命中自身命令时,回退到 weapp-ide-cli。
*/
async function tryRunIdeCommand(argv) {
	const command = argv[0];
	if (!command) return false;
	if (command === "ide") {
		if (argv[1] === "logs" || argv[1] === "doctor" || argv[1] === "--help" || argv[1] === "-h") return false;
		if (!await dispatchWechatCliCommand(argv.slice(1))) await executeWechatIdeCliCommand(argv.slice(1));
		return true;
	}
	if (command.startsWith("-")) return false;
	if (command === "help") {
		const target = argv[1];
		if (!target || WEAPP_VITE_NATIVE_COMMANDS.has(target) || !isWeappIdeTopLevelCommand(target)) return false;
		await executeWechatIdeCliCommand(argv);
		return true;
	}
	if (WEAPP_VITE_NATIVE_COMMANDS.has(command) || !isWeappIdeTopLevelCommand(command)) return false;
	if (!await dispatchWechatCliCommand(argv)) await executeWechatIdeCliCommand(argv);
	return true;
}
//#endregion
//#region src/cli/mcpAutoStart.ts
const SKIP_COMMANDS = /* @__PURE__ */ new Set([
	"--help",
	"--version",
	"-h",
	"-v",
	"help",
	"ide",
	"mcp"
]);
const REG_EADDRINUSE = /EADDRINUSE/;
let started = false;
function shouldAutoStartMcp(argv) {
	const command = argv[0];
	if (!command || command.startsWith("-")) return true;
	if (SKIP_COMMANDS.has(command)) return false;
	return command.includes("/") || command.includes("\\") || command.startsWith(".");
}
async function maybeAutoStartMcpServer(argv, cliOptions) {
	if (started || !shouldAutoStartMcp(argv)) return;
	const configFile = resolveConfigFile(cliOptions);
	let rawMcpConfig;
	try {
		rawMcpConfig = (await loadConfig(configFile))?.config?.weapp?.mcp;
	} catch (error) {
		logger_default.warn(`[mcp] 读取配置失败,使用默认 MCP 配置自动启动:${error instanceof Error ? error.message : String(error)}`);
	}
	const maybeMcpConfig = rawMcpConfig;
	const aiEnvironment = await detectAiDevelopmentEnvironment();
	const resolvedMcp = resolveWeappMcpConfig(maybeMcpConfig, {
		agentName: aiEnvironment.agentName,
		cwd: process.cwd(),
		isAgent: aiEnvironment.isAgent
	});
	if (!resolvedMcp.enabled || !resolvedMcp.autoStart) return;
	try {
		await startWeappViteMcpServer({
			endpoint: resolvedMcp.endpoint,
			host: resolvedMcp.host,
			port: resolvedMcp.port,
			restEndpoint: resolvedMcp.restEndpoint,
			quiet: true,
			transport: "streamable-http",
			unref: true,
			workspaceRoot: process.cwd()
		});
		started = true;
		const mcpUrl = `http://${resolvedMcp.host}:${resolvedMcp.port}${resolvedMcp.endpoint}`;
		logger_default.success("MCP 服务已自动启动:");
		logger_default.info(`  ➜  ${colors.cyan(mcpUrl)}`);
		if (resolvedMcp.restEndpoint !== false) logger_default.info(`  REST ➜  ${colors.cyan(`http://${resolvedMcp.host}:${resolvedMcp.port}${resolvedMcp.restEndpoint}`)}`);
		for (const line of formatMcpQuickStart({
			httpUrl: mcpUrl,
			transport: "http"
		})) logger_default.info(line);
	} catch (error) {
		const message = error instanceof Error ? error.message : String(error);
		if (REG_EADDRINUSE.test(message)) {
			logger_default.info(`[mcp] 端口 ${resolvedMcp.port} 已被占用,跳过自动启动。`);
			started = true;
			return;
		}
		logger_default.warn(`[mcp] 自动启动失败:${message}`);
	}
}
//#endregion
//#region src/cli/prepareGuard.ts
function isPrepareCommandArgs(args) {
	return Array.isArray(args) && args[0] === "prepare";
}
function formatPrepareSkipMessage(error) {
	return `[prepare] 跳过 .weapp-vite 支持文件预生成:${error instanceof Error ? error.message : String(error)}`;
}
function handlePrepareLifecycleError(args, error) {
	if (!isPrepareCommandArgs(args)) return false;
	logger_default.warn(formatPrepareSkipMessage(error));
	process.exitCode = 0;
	return true;
}
//#endregion
//#region src/cli.ts
const cli = cac("weapp-vite");
try {
	checkRuntime({
		bun: "0.0.0",
		deno: "0.0.0",
		node: "^20.19.0 || >=22.12.0"
	});
} catch {}
cli.option("-c, --config <file>", `[string] use specified config file`).option("--base <path>", `[string] public base path (default: /)`, { type: [convertBase] }).option("-l, --logLevel <level>", `[string] info | warn | error | silent`).option("--clearScreen", `[boolean] allow/disable clear screen when logging`).option("-d, --debug [feat]", `[string | boolean] show debug logs`).option("-f, --filter <filter>", `[string] filter debug logs`).option("-m, --mode <mode>", `[string] set env mode`);
registerIdeCommand(cli);
registerAlipayCommand(cli);
registerBuildCommand(cli);
registerCloseCommand(cli);
registerAnalyzeCommand(cli);
registerInitCommand(cli);
registerOpenCommand(cli);
registerPrepareCommand(cli);
registerNpmCommand(cli);
registerGenerateCommand(cli);
registerMcpCommand(cli);
registerServeCommand(cli);
cli.help();
cli.version(VERSION);
const skipManagedTsconfigBootstrapCommands = /* @__PURE__ */ new Set([
	"g",
	"generate",
	"ide",
	"init",
	"mcp",
	"npm",
	"alipay"
]);
function resolveManagedTsconfigBootstrapRoot(args) {
	const [firstArg, secondArg] = args;
	if (!firstArg || firstArg === "--help" || firstArg === "-h" || firstArg === "--version" || firstArg === "-v") return;
	if (firstArg.startsWith("-")) return process.cwd();
	if (skipManagedTsconfigBootstrapCommands.has(firstArg)) {
		if (firstArg === "ide") {
			const thirdArg = args[2];
			if (secondArg === "logs" && thirdArg && !thirdArg.startsWith("-")) return path.resolve(thirdArg);
		}
		return;
	}
	if ([
		"analyze",
		"build",
		"close",
		"dev",
		"open",
		"prepare",
		"serve"
	].includes(firstArg)) {
		if (secondArg && !secondArg.startsWith("-")) return path.resolve(secondArg);
		return process.cwd();
	}
	return path.resolve(firstArg);
}
try {
	const args = process.argv.slice(2);
	if (!await tryRunIdeCommand(args)) {
		const managedTsconfigBootstrapRoot = resolveManagedTsconfigBootstrapRoot(args);
		if (managedTsconfigBootstrapRoot) await syncManagedTsconfigBootstrapFiles(managedTsconfigBootstrapRoot);
		cli.parse(process.argv, { run: false });
		await maybeAutoStartMcpServer(args, cli.options);
		await cli.runMatchedCommand();
	}
} catch (error) {
	if (!handlePrepareLifecycleError(process.argv.slice(2), error)) {
		handleCLIError(error);
		process.exitCode = 1;
	}
}
//#endregion
export {};