weapp-vite 7.1.4 → 7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
- 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-CsYIo9DO.mjs";
2
- import { f as VERSION, i as findJsEntry, o as findTemplateEntry, s as findVueEntry } from "./file-CHisoC18.mjs";
1
+ 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-403iJEyy.mjs";
2
+ import { f as VERSION, i as findJsEntry, o as findTemplateEntry, s as findVueEntry } from "./file-CfgcKGLR.mjs";
3
3
  import { r as logger_default, t as colors } from "./logger-mt4mSTqV.mjs";
4
4
  import { c as startWeappViteMcpServer, l as detectAiDevelopmentEnvironment, s as resolveWeappMcpConfig } from "./mcp-BG6TliEg.mjs";
5
5
  import { createRequire } from "node:module";
@@ -12,14 +12,19 @@ import process from "node:process";
12
12
  import { parse } from "vue/compiler-sfc";
13
13
  import fs$2, { mkdir, writeFile } from "node:fs/promises";
14
14
  import { build, createServer } from "vite";
15
+ import { createHash } from "node:crypto";
16
+ import { promisify } from "node:util";
15
17
  import os from "node:os";
16
18
  import { execFile, spawn } from "node:child_process";
17
19
  import { Buffer } from "node:buffer";
18
20
  import { cac } from "cac";
19
21
  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";
20
- import { promisify } from "node:util";
21
22
  import { brotliCompressSync, gzipSync } from "node:zlib";
23
+ import { buildOtpAuthUrl, refreshTempAuthCode } from "devframe/node/auth";
22
24
  import { resolveCommand } from "package-manager-detector/commands";
25
+ import { defineDevframe, defineRpcFunction } from "devframe";
26
+ import { Server } from "node:http";
27
+ import { initDevframe } from "devframe/initiate";
23
28
  import { generateJs, generateJson, generateWxml, generateWxss } from "@weapp-core/schematics";
24
29
  import { determineAgent } from "@vercel/detect-agent";
25
30
  import { initConfig } from "@weapp-core/init";
@@ -1468,7 +1473,9 @@ function resolveAnalyzeHistoryMetadata(configService) {
1468
1473
  }
1469
1474
  function createAnalyzeMetadata(configService, now = /* @__PURE__ */ new Date()) {
1470
1475
  const history = resolveAnalyzeHistoryMetadata(configService);
1476
+ const packageName = configService.packageJson?.name;
1471
1477
  return {
1478
+ projectName: typeof packageName === "string" && packageName.trim() ? packageName.trim() : path.basename(configService.cwd),
1472
1479
  generatedAt: now.toISOString(),
1473
1480
  budgets: resolveAnalyzeBudgets(configService),
1474
1481
  history: {
@@ -1489,6 +1496,7 @@ function classifyModuleSourceKind(options) {
1489
1496
  }
1490
1497
  function resolvePluginAssetAbsolute(normalizedFileName, pluginRoot) {
1491
1498
  if (!pluginRoot) return;
1499
+ if (posix.isAbsolute(normalizedFileName)) return isPathInside(pluginRoot, normalizedFileName) ? normalizedFileName : void 0;
1492
1500
  const pluginBase = posix.basename(pluginRoot);
1493
1501
  if (normalizedFileName !== pluginBase && !normalizedFileName.startsWith(`${pluginBase}/`)) return;
1494
1502
  const relative = normalizedFileName === pluginBase ? "" : normalizedFileName.slice(pluginBase.length + 1);
@@ -1546,7 +1554,7 @@ function resolveModuleSourceType(absoluteId, ctx) {
1546
1554
  function resolveAssetSource(fileName, ctx) {
1547
1555
  const { configService } = ctx;
1548
1556
  const normalized = posix.normalize(fileName);
1549
- const srcCandidate = posix.resolve(configService.absoluteSrcRoot, normalized);
1557
+ const srcCandidate = posix.isAbsolute(normalized) ? normalized : posix.resolve(configService.absoluteSrcRoot, normalized);
1550
1558
  if (isPathInside(configService.absoluteSrcRoot, srcCandidate)) return {
1551
1559
  absolute: srcCandidate,
1552
1560
  source: configService.relativeAbsoluteSrcRoot(srcCandidate),
@@ -1615,6 +1623,8 @@ function processChunk(chunk, origin, ctx, classifierContext, packages, modules)
1615
1623
  isEntry: chunk.isEntry,
1616
1624
  modules: []
1617
1625
  };
1626
+ if (chunk.imports.length > 0) chunkEntry.imports = [...chunk.imports].sort((a, b) => a.localeCompare(b));
1627
+ if (chunk.dynamicImports.length > 0) chunkEntry.dynamicImports = [...chunk.dynamicImports].sort((a, b) => a.localeCompare(b));
1618
1628
  const moduleEntries = Object.entries(chunk.modules ?? {});
1619
1629
  for (const [rawModuleId, info] of moduleEntries) {
1620
1630
  const absoluteId = normalizeModuleId(rawModuleId);
@@ -1644,17 +1654,27 @@ function processAsset(asset, origin, ctx, classifierContext, packages, modules)
1644
1654
  size: assetBuffer?.byteLength,
1645
1655
  ...getCompressedSizes$1(assetBuffer)
1646
1656
  };
1647
- const assetSource = resolveAssetSource(asset.fileName, ctx);
1657
+ let assetSource;
1658
+ for (const sourceFile of [...asset.originalFileNames ?? [], asset.fileName]) {
1659
+ assetSource = resolveAssetSource(sourceFile, ctx);
1660
+ if (assetSource) break;
1661
+ }
1648
1662
  if (assetSource) {
1649
1663
  entry.source = assetSource.source;
1664
+ entry.sourceType = assetSource.sourceType;
1650
1665
  registerModuleInPackage(modules, assetSource.absolute, assetSource.source, assetSource.sourceType, classification.id, asset.fileName);
1651
1666
  }
1652
1667
  packageEntry.files.set(asset.fileName, entry);
1653
1668
  }
1654
- function processOutput(output, origin, ctx, classifierContext, packages, modules) {
1669
+ function processOutput(output, origin, ctx, classifierContext, packages, modules, onArtifact) {
1655
1670
  if (!output) return;
1656
- for (const item of output.output ?? []) if (item.type === "chunk") processChunk(item, origin, ctx, classifierContext, packages, modules);
1657
- else if (item.type === "asset") processAsset(item, origin, ctx, classifierContext, packages, modules);
1671
+ for (const item of output.output ?? []) if (item.type === "chunk") {
1672
+ processChunk(item, origin, ctx, classifierContext, packages, modules);
1673
+ onArtifact?.(item.fileName, item.code);
1674
+ } else if (item.type === "asset") {
1675
+ processAsset(item, origin, ctx, classifierContext, packages, modules);
1676
+ onArtifact?.(item.fileName, item.source);
1677
+ }
1658
1678
  }
1659
1679
  //#endregion
1660
1680
  //#region src/analyze/subpackages/summary.ts
@@ -1757,7 +1777,7 @@ function summarizeSubPackages(metas) {
1757
1777
  }
1758
1778
  //#endregion
1759
1779
  //#region src/analyze/subpackages/index.ts
1760
- async function analyzeSubpackages(ctx) {
1780
+ async function analyzeSubpackages(ctx, options) {
1761
1781
  const { configService, scanService, buildService } = ctx;
1762
1782
  if (!configService || !scanService || !buildService) throw new Error("analyzeSubpackages 需要先初始化 configService、scanService 和 buildService。");
1763
1783
  await scanService.loadAppEntry();
@@ -1785,12 +1805,12 @@ async function analyzeSubpackages(ctx) {
1785
1805
  const modules = /* @__PURE__ */ new Map();
1786
1806
  const componentJsonConfigs = [];
1787
1807
  for (const output of mainOutputs) {
1788
- processOutput(output, "main", ctx, classifierContext, packages, modules);
1808
+ processOutput(output, "main", ctx, classifierContext, packages, modules, options?.onArtifact);
1789
1809
  componentJsonConfigs.push(...collectAnalyzeComponentJsonConfigs(output));
1790
1810
  }
1791
1811
  for (const root of independentRoots) {
1792
1812
  const output = buildService.getIndependentOutput(root);
1793
- processOutput(output, "independent", ctx, classifierContext, packages, modules);
1813
+ processOutput(output, "independent", ctx, classifierContext, packages, modules, options?.onArtifact);
1794
1814
  componentJsonConfigs.push(...collectAnalyzeComponentJsonConfigs(output));
1795
1815
  }
1796
1816
  expandVirtualModulePlacements(modules, packages, classifierContext);
@@ -2239,15 +2259,515 @@ function createAnalyzeMarkdownReport(result, previousResult) {
2239
2259
  ].join("\n");
2240
2260
  }
2241
2261
  //#endregion
2262
+ //#region src/cli/analyze/dashboardDevframe/artifacts.ts
2263
+ const MAX_DASHBOARD_FILE_CONTENT_BYTES = 2097152;
2264
+ /** 保留同一次分析的产物内容,不读取或改写开发构建目录。 */
2265
+ function createDashboardArtifactSnapshot() {
2266
+ const files = /* @__PURE__ */ new Map();
2267
+ let retainedBytes = 0;
2268
+ return {
2269
+ files,
2270
+ capture(fileName, source) {
2271
+ const key = fileName.replaceAll("\\", "/");
2272
+ const previous = files.get(key);
2273
+ if (previous?.content !== void 0) retainedBytes -= previous.size;
2274
+ const size = typeof source === "string" ? Buffer.byteLength(source, "utf8") : source.byteLength;
2275
+ if (size > 2097152) {
2276
+ files.set(key, {
2277
+ size,
2278
+ error: `文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`
2279
+ });
2280
+ return;
2281
+ }
2282
+ if (retainedBytes + size > 33554432) {
2283
+ files.set(key, {
2284
+ size,
2285
+ error: "分析产物内容超过快照容量上限,已拒绝读取。"
2286
+ });
2287
+ return;
2288
+ }
2289
+ const content = typeof source === "string" ? source : Buffer.from(source.buffer, source.byteOffset, source.byteLength).toString("utf8");
2290
+ files.set(key, {
2291
+ content,
2292
+ size
2293
+ });
2294
+ retainedBytes += size;
2295
+ }
2296
+ };
2297
+ }
2298
+ //#endregion
2299
+ //#region src/cli/analyze/dashboardDevframe/paths.ts
2300
+ function normalizeDashboardRelativePath(value) {
2301
+ return value.replaceAll("\\", "/");
2302
+ }
2303
+ function stripDashboardFileQuery(value) {
2304
+ const queryIndex = value.indexOf("?");
2305
+ return queryIndex === -1 ? value : value.slice(0, queryIndex);
2306
+ }
2307
+ function addDashboardAllowedPath(paths, value) {
2308
+ if (!value || value.includes("\0")) return;
2309
+ const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(value));
2310
+ if (!normalizedPath || path.isAbsolute(normalizedPath)) return;
2311
+ paths.add(normalizedPath);
2312
+ }
2313
+ function addDashboardAllowedSourcePath(paths, value, rootKind) {
2314
+ if (!value || value.includes("\0")) return;
2315
+ const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(value));
2316
+ if (!normalizedPath || path.isAbsolute(normalizedPath)) return;
2317
+ const rootKinds = paths.get(normalizedPath) ?? /* @__PURE__ */ new Set();
2318
+ rootKinds.add(rootKind);
2319
+ paths.set(normalizedPath, rootKinds);
2320
+ }
2321
+ function resolveDashboardSourceRootKind(sourceType) {
2322
+ if (sourceType === "plugin") return "plugin";
2323
+ if (sourceType === "workspace" || sourceType === "node_modules") return "project";
2324
+ return "src";
2325
+ }
2326
+ function createDashboardContentAllowlist(result) {
2327
+ const artifactPaths = /* @__PURE__ */ new Set();
2328
+ const sourcePaths = /* @__PURE__ */ new Map();
2329
+ for (const packageReport of result.packages) for (const file of packageReport.files) {
2330
+ addDashboardAllowedPath(artifactPaths, file.file);
2331
+ const fileSourceType = file.sourceType ?? file.modules?.find((module) => module.source === file.source)?.sourceType;
2332
+ addDashboardAllowedSourcePath(sourcePaths, file.source, resolveDashboardSourceRootKind(fileSourceType));
2333
+ for (const module of file.modules ?? []) addDashboardAllowedSourcePath(sourcePaths, module.source, module.sourceType === "src" ? "src" : module.sourceType === "plugin" ? "plugin" : "project");
2334
+ }
2335
+ return {
2336
+ artifactPaths,
2337
+ sourcePaths
2338
+ };
2339
+ }
2340
+ function resolveDashboardContentPath(root, requestPath, options) {
2341
+ if (!root || !requestPath || requestPath.includes("\0")) return;
2342
+ const normalizedRequestPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
2343
+ if (path.isAbsolute(normalizedRequestPath) || !options.allowedPaths.has(normalizedRequestPath)) return;
2344
+ const rootPath = path.resolve(root);
2345
+ const absolutePath = path.resolve(rootPath, normalizedRequestPath);
2346
+ const relativePath = path.relative(rootPath, absolutePath);
2347
+ const allowParent = options.allowParent === true;
2348
+ if (!relativePath || !allowParent && (relativePath.startsWith("..") || path.isAbsolute(relativePath))) return;
2349
+ return {
2350
+ absolutePath,
2351
+ allowParent,
2352
+ relativePath: allowParent ? normalizedRequestPath : normalizeDashboardRelativePath(relativePath),
2353
+ rootPath
2354
+ };
2355
+ }
2356
+ function resolveDashboardSourceContentPaths(roots, requestPath, allowedPaths) {
2357
+ const normalizedRequestPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
2358
+ const rootKinds = allowedPaths.get(normalizedRequestPath);
2359
+ if (!rootKinds || rootKinds.size !== 1) return [];
2360
+ const [rootKind] = rootKinds;
2361
+ if (rootKind === "project") {
2362
+ const resolved = resolveDashboardContentPath(roots.projectRoot, normalizedRequestPath, {
2363
+ allowParent: true,
2364
+ allowedPaths: /* @__PURE__ */ new Set([normalizedRequestPath])
2365
+ });
2366
+ return resolved ? [resolved] : [];
2367
+ }
2368
+ if (rootKind === "plugin") {
2369
+ if (!roots.pluginRoot) return [];
2370
+ const pluginBase = path.basename(roots.pluginRoot);
2371
+ if (normalizedRequestPath !== pluginBase && !normalizedRequestPath.startsWith(`${pluginBase}/`)) return [];
2372
+ const pluginRelativePath = normalizedRequestPath === pluginBase ? "" : normalizedRequestPath.slice(pluginBase.length + 1);
2373
+ const resolved = resolveDashboardContentPath(roots.pluginRoot, pluginRelativePath, { allowedPaths: /* @__PURE__ */ new Set([pluginRelativePath]) });
2374
+ return resolved ? [{
2375
+ ...resolved,
2376
+ relativePath: normalizedRequestPath
2377
+ }] : [];
2378
+ }
2379
+ if (!roots.srcRoot) return [];
2380
+ const candidates = (normalizedRequestPath.startsWith("src/") ? [normalizedRequestPath, normalizedRequestPath.slice(4)] : [normalizedRequestPath]).flatMap((relativePath) => {
2381
+ const resolved = resolveDashboardContentPath(roots.srcRoot, relativePath, { allowedPaths: /* @__PURE__ */ new Set([relativePath]) });
2382
+ return resolved ? [{
2383
+ ...resolved,
2384
+ relativePath: normalizedRequestPath
2385
+ }] : [];
2386
+ });
2387
+ return [...new Map(candidates.map((candidate) => [candidate.absolutePath, candidate])).values()];
2388
+ }
2389
+ function resolveDashboardArtifactPath(requestPath, allowedPaths) {
2390
+ const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
2391
+ if (!normalizedPath || normalizedPath.includes("\0") || path.isAbsolute(normalizedPath) || path.posix.normalize(normalizedPath).startsWith("..") || !allowedPaths.has(normalizedPath)) return;
2392
+ return normalizedPath;
2393
+ }
2394
+ //#endregion
2395
+ //#region src/cli/analyze/dashboardDevframe/content.ts
2396
+ function isPathWithin(rootPath, targetPath) {
2397
+ const relativePath = path.relative(rootPath, targetPath);
2398
+ return !relativePath || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
2399
+ }
2400
+ function resolveInspectionRoot(resolved) {
2401
+ let inspectionRoot = resolved.rootPath;
2402
+ if (!resolved.allowParent) return inspectionRoot;
2403
+ while (!isPathWithin(inspectionRoot, resolved.absolutePath)) {
2404
+ const parent = path.dirname(inspectionRoot);
2405
+ if (parent === inspectionRoot) return;
2406
+ inspectionRoot = parent;
2407
+ }
2408
+ return inspectionRoot;
2409
+ }
2410
+ async function assertDashboardPathHasNoSymlink(resolved) {
2411
+ const inspectionRoot = resolveInspectionRoot(resolved);
2412
+ if (!inspectionRoot) throw new Error("文件路径超出允许范围。");
2413
+ const relativePath = path.relative(inspectionRoot, resolved.absolutePath);
2414
+ let currentPath = inspectionRoot;
2415
+ for (const segment of relativePath.split(path.sep).filter(Boolean)) {
2416
+ currentPath = path.join(currentPath, segment);
2417
+ if ((await fs.promises.lstat(currentPath)).isSymbolicLink()) throw new Error("文件路径包含不允许的符号链接。");
2418
+ }
2419
+ }
2420
+ function isSameOpenedFile(pathStat, handleStat) {
2421
+ if (pathStat.dev !== handleStat.dev) return false;
2422
+ return pathStat.ino === 0 || handleStat.ino === 0 || pathStat.ino === handleStat.ino;
2423
+ }
2424
+ async function openDashboardFile(resolved) {
2425
+ await assertDashboardPathHasNoSymlink(resolved);
2426
+ const flags = process.platform === "win32" ? fs.constants.O_RDONLY : fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW;
2427
+ const handle = await fs.promises.open(resolved.absolutePath, flags);
2428
+ try {
2429
+ await assertDashboardPathHasNoSymlink(resolved);
2430
+ const [pathStat, handleStat] = await Promise.all([fs.promises.stat(resolved.absolutePath), handle.stat()]);
2431
+ if (!isSameOpenedFile(pathStat, handleStat)) throw new Error("文件路径在读取期间发生变化。");
2432
+ return handle;
2433
+ } catch (error) {
2434
+ await handle.close();
2435
+ throw error;
2436
+ }
2437
+ }
2438
+ async function readDashboardFileHandle(handle) {
2439
+ const stat = await handle.stat();
2440
+ if (!stat.isFile()) throw new Error("目标路径不是文件。");
2441
+ if (stat.size > 2097152) throw new Error(`文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`);
2442
+ let content = Buffer.allocUnsafe(Math.min(Math.max(stat.size + 1, 1), 2097153));
2443
+ let offset = 0;
2444
+ while (true) {
2445
+ if (offset === content.length) {
2446
+ if (content.length === 2097153) throw new Error(`文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`);
2447
+ const expanded = Buffer.allocUnsafe(Math.min(content.length * 2, 2097153));
2448
+ content.copy(expanded, 0, 0, offset);
2449
+ content = expanded;
2450
+ }
2451
+ const { bytesRead } = await handle.read(content, offset, content.length - offset, offset);
2452
+ if (bytesRead === 0) break;
2453
+ offset += bytesRead;
2454
+ if (offset > 2097152) throw new Error(`文件超过 ${MAX_DASHBOARD_FILE_CONTENT_BYTES} 字节,已拒绝读取。`);
2455
+ }
2456
+ return {
2457
+ content: content.subarray(0, offset).toString("utf8"),
2458
+ size: offset
2459
+ };
2460
+ }
2461
+ function resolveDashboardFileLanguage(filePath) {
2462
+ const extension = path.extname(filePath).toLowerCase();
2463
+ if ([
2464
+ ".js",
2465
+ ".mjs",
2466
+ ".cjs",
2467
+ ".wxs",
2468
+ ".sjs"
2469
+ ].includes(extension)) return "javascript";
2470
+ if ([
2471
+ ".ts",
2472
+ ".mts",
2473
+ ".cts"
2474
+ ].includes(extension)) return "typescript";
2475
+ if ([".json", ".map"].includes(extension)) return "json";
2476
+ if ([
2477
+ ".css",
2478
+ ".wxss",
2479
+ ".scss",
2480
+ ".sass",
2481
+ ".less"
2482
+ ].includes(extension)) return "css";
2483
+ if ([
2484
+ ".vue",
2485
+ ".wxml",
2486
+ ".html"
2487
+ ].includes(extension)) return "html";
2488
+ return "plaintext";
2489
+ }
2490
+ function normalizeDashboardFileRequest(input) {
2491
+ if (!input || typeof input !== "object" || !("kind" in input) || !("path" in input)) throw new Error("必须传入合法的 kind 和相对路径。");
2492
+ const kind = input.kind;
2493
+ const requestPath = input.path;
2494
+ if (kind !== "source" && kind !== "artifact" || typeof requestPath !== "string") throw new Error("必须传入合法的 kind 和相对路径。");
2495
+ return {
2496
+ kind,
2497
+ path: requestPath
2498
+ };
2499
+ }
2500
+ async function readAllowedDashboardFile(input, roots, allowlist, artifacts) {
2501
+ const request = normalizeDashboardFileRequest(input);
2502
+ if (request.kind === "artifact") {
2503
+ const filePath = resolveDashboardArtifactPath(request.path, allowlist.artifactPaths);
2504
+ if (!filePath) throw new Error("必须传入合法的 kind 和相对路径。");
2505
+ const file = artifacts.get(filePath);
2506
+ if (!file) throw new Error("分析快照中没有此产物内容。");
2507
+ if (file.error || file.content === void 0) throw new Error(file.error ?? "分析快照中没有此产物内容。");
2508
+ return {
2509
+ kind: request.kind,
2510
+ path: filePath,
2511
+ language: resolveDashboardFileLanguage(filePath),
2512
+ size: file.size,
2513
+ content: file.content
2514
+ };
2515
+ }
2516
+ const resolvedCandidates = resolveDashboardSourceContentPaths(roots, request.path, allowlist.sourcePaths);
2517
+ if (resolvedCandidates.length === 0) throw new Error("必须传入合法的 kind 和相对路径。");
2518
+ const openedCandidates = [];
2519
+ try {
2520
+ for (const resolved of resolvedCandidates) try {
2521
+ openedCandidates.push({
2522
+ handle: await openDashboardFile(resolved),
2523
+ resolved
2524
+ });
2525
+ } catch (error) {
2526
+ if ((typeof error === "object" && error && "code" in error ? String(error.code) : "") !== "ENOENT") throw error;
2527
+ }
2528
+ if (openedCandidates.length === 0) throw new Error("文件不存在。");
2529
+ if (openedCandidates.length > 1) throw new Error("源码路径存在多个候选文件,已拒绝读取。");
2530
+ const [{ handle, resolved }] = openedCandidates;
2531
+ const file = await readDashboardFileHandle(handle);
2532
+ return {
2533
+ kind: request.kind,
2534
+ path: resolved.relativePath,
2535
+ language: resolveDashboardFileLanguage(resolved.relativePath),
2536
+ size: file.size,
2537
+ content: file.content
2538
+ };
2539
+ } catch (error) {
2540
+ if (error instanceof Error && (error.message === "文件不存在。" || error.message === "源码路径存在多个候选文件,已拒绝读取。" || error.message === "目标路径不是文件。" || error.message.startsWith("文件超过 ") || error.message.startsWith("文件路径"))) throw error;
2541
+ throw new Error("读取文件失败。");
2542
+ } finally {
2543
+ await Promise.allSettled(openedCandidates.map((candidate) => candidate.handle.close()));
2544
+ }
2545
+ }
2546
+ function createDashboardFileReader(roots, result, artifacts) {
2547
+ let allowlist = createDashboardContentAllowlist(result);
2548
+ let currentArtifacts = artifacts;
2549
+ let disposed = false;
2550
+ return {
2551
+ read: async (input) => {
2552
+ if (disposed) throw new Error("Dashboard 文件读取会话已关闭。");
2553
+ return await readAllowedDashboardFile(input, roots, allowlist, currentArtifacts);
2554
+ },
2555
+ update(nextResult, nextArtifacts) {
2556
+ allowlist = createDashboardContentAllowlist(nextResult);
2557
+ currentArtifacts = nextArtifacts;
2558
+ },
2559
+ dispose() {
2560
+ disposed = true;
2561
+ currentArtifacts = /* @__PURE__ */ new Map();
2562
+ allowlist.artifactPaths.clear();
2563
+ allowlist.sourcePaths.clear();
2564
+ }
2565
+ };
2566
+ }
2567
+ //#endregion
2568
+ //#region src/cli/analyze/dashboardDevframe/index.ts
2569
+ const DEVFRAME_ID = "weapp-vite";
2570
+ const MAX_DASHBOARD_ANALYZE_PAGE_CHARACTERS = 65536;
2571
+ const STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE = "Analyze revision 已变化,请重新获取 Dashboard 状态。";
2572
+ const rejectDashboardSharedStateSet = defineRpcFunction({
2573
+ name: "devframe:rpc:server-state:set",
2574
+ type: "action",
2575
+ handler: () => {
2576
+ throw new Error("Dashboard Devframe shared state 只允许服务端修改。");
2577
+ }
2578
+ });
2579
+ const rejectDashboardSharedStatePatch = defineRpcFunction({
2580
+ name: "devframe:rpc:server-state:patch",
2581
+ type: "action",
2582
+ handler: () => {
2583
+ throw new Error("Dashboard Devframe shared state 只允许服务端修改。");
2584
+ }
2585
+ });
2586
+ function serializeDashboardAnalyzePayload(result) {
2587
+ const value = JSON.stringify(result);
2588
+ return {
2589
+ descriptor: {
2590
+ characters: value.length,
2591
+ hash: createHash("sha256").update(value).digest("hex"),
2592
+ pages: Math.max(1, Math.ceil(value.length / MAX_DASHBOARD_ANALYZE_PAGE_CHARACTERS))
2593
+ },
2594
+ source: result,
2595
+ value
2596
+ };
2597
+ }
2598
+ function serializeDashboardAnalyzeSnapshot(snapshot, cached) {
2599
+ const previous = snapshot.previous ? cached?.current.source === snapshot.previous ? cached.current : cached?.previous?.source === snapshot.previous ? cached.previous : serializeDashboardAnalyzePayload(snapshot.previous) : null;
2600
+ return {
2601
+ current: cached?.current.source === snapshot.current ? cached.current : serializeDashboardAnalyzePayload(snapshot.current),
2602
+ previous
2603
+ };
2604
+ }
2605
+ function createDashboardState(revision, snapshot, runtimeEvents) {
2606
+ return {
2607
+ analyze: {
2608
+ current: snapshot.current.descriptor,
2609
+ previous: snapshot.previous?.descriptor ?? null
2610
+ },
2611
+ revision,
2612
+ runtimeEvents: [...runtimeEvents]
2613
+ };
2614
+ }
2615
+ function normalizeAnalyzePageRequest(input) {
2616
+ if (!input || typeof input !== "object") throw new Error("必须传入合法的 Analyze 分页请求。");
2617
+ if (!("revision" in input) || !("target" in input) || !("index" in input)) throw new Error("必须传入合法的 Analyze 分页请求。");
2618
+ const revision = input.revision;
2619
+ const target = input.target;
2620
+ const index = input.index;
2621
+ if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 0 || target !== "current" && target !== "previous" || typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) throw new Error("必须传入合法的 Analyze 分页请求。");
2622
+ return {
2623
+ revision,
2624
+ target,
2625
+ index
2626
+ };
2627
+ }
2628
+ function readDashboardAnalyzePage(input, revision, snapshot) {
2629
+ const request = normalizeAnalyzePageRequest(input);
2630
+ if (request.revision !== revision) throw new Error(STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE);
2631
+ const payload = request.target === "current" ? snapshot.current : snapshot.previous;
2632
+ if (!payload || request.index >= payload.descriptor.pages) throw new Error("Analyze 分页不存在。");
2633
+ const start = request.index * MAX_DASHBOARD_ANALYZE_PAGE_CHARACTERS;
2634
+ return {
2635
+ content: payload.value.slice(start, start + MAX_DASHBOARD_ANALYZE_PAGE_CHARACTERS),
2636
+ descriptor: payload.descriptor,
2637
+ index: request.index,
2638
+ revision,
2639
+ target: request.target
2640
+ };
2641
+ }
2642
+ function createAnalyzeDashboardDevframe(options) {
2643
+ let revision = 0;
2644
+ let disposed = false;
2645
+ let serializedSnapshot = serializeDashboardAnalyzeSnapshot(options.getAnalyzeSnapshot());
2646
+ let broadcastDashboardState;
2647
+ const initialSnapshot = options.getAnalyzeSnapshot();
2648
+ const fileReader = createDashboardFileReader(options.roots, initialSnapshot.current, initialSnapshot.artifacts);
2649
+ const getDashboardState = defineRpcFunction({
2650
+ name: "get-dashboard-state",
2651
+ type: "query",
2652
+ jsonSerializable: true,
2653
+ handler: () => createDashboardState(revision, serializedSnapshot, options.getRuntimeEvents())
2654
+ });
2655
+ const getAnalyzePage = defineRpcFunction({
2656
+ name: "get-analyze-page",
2657
+ type: "query",
2658
+ jsonSerializable: true,
2659
+ handler: (input) => readDashboardAnalyzePage(input, revision, serializedSnapshot)
2660
+ });
2661
+ const readDashboardFile = defineRpcFunction({
2662
+ name: "read-dashboard-file",
2663
+ type: "query",
2664
+ jsonSerializable: true,
2665
+ handler: async (input) => {
2666
+ if (!input || typeof input !== "object" || !("revision" in input) || typeof input.revision !== "number" || !Number.isSafeInteger(input.revision) || input.revision < 0) throw new Error("必须传入合法的文件读取 revision。");
2667
+ const requestedRevision = input.revision;
2668
+ if (disposed || requestedRevision !== revision) throw new Error(STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE);
2669
+ const content = await fileReader.read(input);
2670
+ if (disposed || requestedRevision !== revision) throw new Error(STALE_DASHBOARD_ANALYZE_REVISION_MESSAGE);
2671
+ return content;
2672
+ }
2673
+ });
2674
+ return {
2675
+ definition: defineDevframe({
2676
+ id: DEVFRAME_ID,
2677
+ name: "weapp-vite",
2678
+ version: VERSION,
2679
+ packageName: "weapp-vite",
2680
+ importMetaUrl: import.meta.url,
2681
+ homepage: "https://vite.weapp.dev/",
2682
+ description: "weapp-vite 构建分析与小程序开发工具。",
2683
+ icon: "ph:rocket-launch-duotone",
2684
+ async setup(ctx) {
2685
+ ctx.rpc.register(rejectDashboardSharedStateSet, true);
2686
+ ctx.rpc.register(rejectDashboardSharedStatePatch, true);
2687
+ const dashboard = ctx.scope(DEVFRAME_ID);
2688
+ dashboard.rpc.register(getDashboardState);
2689
+ dashboard.rpc.register(getAnalyzePage);
2690
+ dashboard.rpc.register(readDashboardFile);
2691
+ broadcastDashboardState = async () => {
2692
+ await dashboard.rpc.broadcast({
2693
+ method: "dashboard-state-updated",
2694
+ args: [createDashboardState(revision, serializedSnapshot, options.getRuntimeEvents())],
2695
+ event: true
2696
+ });
2697
+ };
2698
+ }
2699
+ }),
2700
+ notifyAnalyzeUpdate() {
2701
+ if (disposed) return;
2702
+ revision += 1;
2703
+ const snapshot = options.getAnalyzeSnapshot();
2704
+ serializedSnapshot = serializeDashboardAnalyzeSnapshot(snapshot, serializedSnapshot);
2705
+ fileReader.update(snapshot.current, snapshot.artifacts);
2706
+ broadcastDashboardState?.();
2707
+ },
2708
+ syncRuntimeEvents() {
2709
+ broadcastDashboardState?.();
2710
+ },
2711
+ dispose() {
2712
+ disposed = true;
2713
+ fileReader.dispose();
2714
+ broadcastDashboardState = void 0;
2715
+ }
2716
+ };
2717
+ }
2718
+ //#endregion
2719
+ //#region src/cli/analyze/dashboardViteBridge.ts
2720
+ const ANALYZE_DASHBOARD_DEVFRAME_BASE = "/__weapp-vite/";
2721
+ function createAnalyzeDashboardViteBridge(definition) {
2722
+ let instance;
2723
+ let closing;
2724
+ const closeInstance = async () => {
2725
+ if (closing) return await closing;
2726
+ const current = instance;
2727
+ instance = void 0;
2728
+ if (!current) return;
2729
+ closing = current.close().finally(() => {
2730
+ closing = void 0;
2731
+ });
2732
+ await closing;
2733
+ };
2734
+ return {
2735
+ name: "weapp-vite:dashboard-devframe",
2736
+ apply: "serve",
2737
+ async configureServer(server) {
2738
+ await closeInstance();
2739
+ const httpServer = server.httpServer instanceof Server ? server.httpServer : void 0;
2740
+ const created = initDevframe(definition, {
2741
+ base: ANALYZE_DASHBOARD_DEVFRAME_BASE,
2742
+ distDir: false,
2743
+ ...httpServer ? { server: httpServer } : {
2744
+ host: "127.0.0.1",
2745
+ ws: { sidecar: true }
2746
+ },
2747
+ allowedOrigins: [],
2748
+ auth: true,
2749
+ mcp: false
2750
+ });
2751
+ server.middlewares.use(created.nodeMiddleware);
2752
+ try {
2753
+ await created.ready;
2754
+ instance = created;
2755
+ } catch (error) {
2756
+ await created.close();
2757
+ throw error;
2758
+ }
2759
+ server.httpServer?.once("close", () => {
2760
+ closeInstance();
2761
+ });
2762
+ },
2763
+ async closeBundle() {
2764
+ await closeInstance();
2765
+ }
2766
+ };
2767
+ }
2768
+ //#endregion
2242
2769
  //#region src/cli/analyze/dashboard.ts
2243
- const ANALYZE_GLOBAL_KEY = "__WEAPP_VITE_ANALYZE_RESULT__";
2244
- const PREVIOUS_ANALYZE_GLOBAL_KEY = "__WEAPP_VITE_PREVIOUS_ANALYZE_RESULT__";
2245
- const DASHBOARD_EVENTS_GLOBAL_KEY = "__WEAPP_VITE_DASHBOARD_EVENTS__";
2246
2770
  const ANALYZE_DASHBOARD_PACKAGE_NAME = "@weapp-vite/dashboard";
2247
- const ANALYZE_SSE_PATH = "/__weapp_vite_analyze";
2248
- const FILE_CONTENT_PATH = "/__weapp_vite_file_content";
2249
- const DASHBOARD_EVENT_NAME = "weapp-dashboard:event";
2250
- const MAX_FILE_CONTENT_BYTES = 2097152;
2251
2771
  const require = createRequire(import.meta.url);
2252
2772
  function createInstallCommand(agent) {
2253
2773
  const resolved = resolveCommand(agent ?? "npm", "install", [ANALYZE_DASHBOARD_PACKAGE_NAME]);
@@ -2310,217 +2830,13 @@ function resolveDashboardRoot(options) {
2310
2830
  }
2311
2831
  if (dashboardPackageRoot) {
2312
2832
  const devResolved = resolveDashboardDevRoot(dashboardPackageRoot, dashboardManifest);
2313
- if (devResolved) return devResolved;
2314
2833
  const distResolved = resolveDashboardDistRoot(dashboardPackageRoot, dashboardManifest);
2315
- if (distResolved) return distResolved;
2834
+ const resolved = options?.watch ? devResolved ?? distResolved : distResolved ?? devResolved;
2835
+ if (resolved) return resolved;
2316
2836
  }
2317
2837
  logger_default.warn(`[weapp-vite ui] 未安装可选仪表盘包 ${colors.bold(colors.green(ANALYZE_DASHBOARD_PACKAGE_NAME))},已自动降级关闭 dashboard 能力。`);
2318
2838
  logger_default.info(`如需启用,请执行 ${colors.bold(colors.green(createInstallCommand(options?.packageManagerAgent)))}`);
2319
2839
  }
2320
- function normalizeDashboardRelativePath(value) {
2321
- return value.replaceAll("\\", "/");
2322
- }
2323
- function stripDashboardFileQuery(value) {
2324
- const queryIndex = value.indexOf("?");
2325
- return queryIndex === -1 ? value : value.slice(0, queryIndex);
2326
- }
2327
- function addDashboardAllowedPath(paths, value) {
2328
- if (!value || value.includes("\0")) return;
2329
- const normalizedPath = normalizeDashboardRelativePath(stripDashboardFileQuery(value));
2330
- if (!normalizedPath || path.isAbsolute(normalizedPath)) return;
2331
- paths.add(normalizedPath);
2332
- }
2333
- function createDashboardContentAllowlist(result) {
2334
- const artifactPaths = /* @__PURE__ */ new Set();
2335
- const sourcePaths = /* @__PURE__ */ new Set();
2336
- for (const packageReport of result.packages) for (const file of packageReport.files) {
2337
- addDashboardAllowedPath(artifactPaths, file.file);
2338
- addDashboardAllowedPath(sourcePaths, file.source);
2339
- for (const module of file.modules ?? []) addDashboardAllowedPath(sourcePaths, module.source);
2340
- }
2341
- return {
2342
- artifactPaths,
2343
- sourcePaths
2344
- };
2345
- }
2346
- function resolveDashboardContentPath(root, requestPath, options) {
2347
- if (!root || !requestPath || requestPath.includes("\0")) return;
2348
- const normalizedRequestPath = normalizeDashboardRelativePath(stripDashboardFileQuery(requestPath));
2349
- if (path.isAbsolute(normalizedRequestPath)) return;
2350
- if (!options.allowedPaths.has(normalizedRequestPath)) return;
2351
- const resolvedRoot = path.resolve(root);
2352
- const absolutePath = path.resolve(resolvedRoot, normalizedRequestPath);
2353
- const relativePath = path.relative(resolvedRoot, absolutePath);
2354
- if (!relativePath) return;
2355
- if (!options.allowParent && (relativePath.startsWith("..") || path.isAbsolute(relativePath))) return;
2356
- return {
2357
- absolutePath,
2358
- relativePath: options.allowParent ? normalizedRequestPath : normalizeDashboardRelativePath(relativePath)
2359
- };
2360
- }
2361
- function resolveDashboardFileLanguage(filePath) {
2362
- const extension = path.extname(filePath).toLowerCase();
2363
- if (extension === ".js" || extension === ".mjs" || extension === ".cjs" || extension === ".wxs" || extension === ".sjs") return "javascript";
2364
- if (extension === ".ts" || extension === ".mts" || extension === ".cts") return "typescript";
2365
- if (extension === ".json" || extension === ".map") return "json";
2366
- if (extension === ".css" || extension === ".wxss" || extension === ".scss" || extension === ".sass" || extension === ".less") return "css";
2367
- if (extension === ".vue" || extension === ".wxml" || extension === ".html") return "html";
2368
- return "plaintext";
2369
- }
2370
- function sendDashboardJson(res, statusCode, payload) {
2371
- res.statusCode = statusCode;
2372
- res.setHeader("Content-Type", "application/json; charset=utf-8");
2373
- res.end(JSON.stringify(payload));
2374
- }
2375
- async function sendDashboardFileContent(res, roots, allowlist, kind, requestPath) {
2376
- const resolved = resolveDashboardContentPath(kind === "artifact" ? roots.artifactRoot : kind === "source" ? roots.sourceRoot : void 0, requestPath, {
2377
- allowParent: kind === "source",
2378
- allowedPaths: kind === "artifact" ? allowlist.artifactPaths : allowlist.sourcePaths
2379
- });
2380
- if (!resolved || kind !== "source" && kind !== "artifact") {
2381
- sendDashboardJson(res, 400, {
2382
- error: "invalid_request",
2383
- message: "必须传入合法的 kind 和相对路径。"
2384
- });
2385
- return;
2386
- }
2387
- try {
2388
- const stat = await fs.promises.stat(resolved.absolutePath);
2389
- if (!stat.isFile()) {
2390
- sendDashboardJson(res, 400, {
2391
- error: "not_file",
2392
- message: "目标路径不是文件。"
2393
- });
2394
- return;
2395
- }
2396
- if (stat.size > MAX_FILE_CONTENT_BYTES) {
2397
- sendDashboardJson(res, 413, {
2398
- error: "file_too_large",
2399
- message: `文件超过 ${MAX_FILE_CONTENT_BYTES} 字节,已拒绝读取。`
2400
- });
2401
- return;
2402
- }
2403
- sendDashboardJson(res, 200, {
2404
- kind,
2405
- path: resolved.relativePath,
2406
- language: resolveDashboardFileLanguage(resolved.relativePath),
2407
- size: stat.size,
2408
- content: await fs.promises.readFile(resolved.absolutePath, "utf8")
2409
- });
2410
- } catch (error) {
2411
- const code = typeof error === "object" && error && "code" in error ? String(error.code) : "";
2412
- sendDashboardJson(res, code === "ENOENT" ? 404 : 500, {
2413
- error: code === "ENOENT" ? "not_found" : "read_failed",
2414
- message: code === "ENOENT" ? "文件不存在。" : "读取文件失败。"
2415
- });
2416
- }
2417
- }
2418
- function createAnalyzeHtmlPlugin(state, runtimeEvents, contentRoots, contentAllowlist, onServerInstance, onBroadcastReady) {
2419
- const sseClients = /* @__PURE__ */ new Set();
2420
- const hotBridgeScript = `
2421
- const applyAnalyzePayload = (payload) => {
2422
- const current = payload?.current ?? payload
2423
- const previous = payload?.previous ?? window.${PREVIOUS_ANALYZE_GLOBAL_KEY} ?? null
2424
- window.${ANALYZE_GLOBAL_KEY} = current
2425
- window.${PREVIOUS_ANALYZE_GLOBAL_KEY} = previous
2426
- window.dispatchEvent(new CustomEvent('weapp-analyze:update', { detail: { current, previous } }))
2427
- }
2428
- const applyDashboardEvents = (payload) => {
2429
- const events = Array.isArray(payload) ? payload : [payload]
2430
- const nextEvents = events.filter(Boolean)
2431
- if (nextEvents.length === 0) {
2432
- return
2433
- }
2434
- window.${DASHBOARD_EVENTS_GLOBAL_KEY} = [
2435
- ...(window.${DASHBOARD_EVENTS_GLOBAL_KEY} ?? []),
2436
- ...nextEvents,
2437
- ]
2438
- window.dispatchEvent(new CustomEvent('${DASHBOARD_EVENT_NAME}', { detail: nextEvents }))
2439
- }
2440
- const source = new EventSource('${ANALYZE_SSE_PATH}')
2441
- source.onmessage = (event) => {
2442
- try {
2443
- applyAnalyzePayload(JSON.parse(event.data))
2444
- }
2445
- catch {}
2446
- }
2447
- if (import.meta.hot) {
2448
- import.meta.hot.on('weapp-analyze:update', (payload) => {
2449
- applyAnalyzePayload(payload)
2450
- })
2451
- import.meta.hot.on('${DASHBOARD_EVENT_NAME}', (payload) => {
2452
- applyDashboardEvents(payload)
2453
- })
2454
- }
2455
- `.trim();
2456
- const broadcast = (payload) => {
2457
- const serialized = `data: ${JSON.stringify(payload)}\n\n`;
2458
- for (const client of sseClients) client.write(serialized);
2459
- };
2460
- onBroadcastReady(broadcast);
2461
- return {
2462
- name: "weapp-vite-analyze-html",
2463
- transformIndexHtml(html) {
2464
- return {
2465
- html,
2466
- tags: [
2467
- {
2468
- tag: "script",
2469
- children: `window.${ANALYZE_GLOBAL_KEY} = ${JSON.stringify(state.current)}`,
2470
- injectTo: "head-prepend"
2471
- },
2472
- {
2473
- tag: "script",
2474
- children: `window.${PREVIOUS_ANALYZE_GLOBAL_KEY} = ${JSON.stringify(state.previous)}`,
2475
- injectTo: "head-prepend"
2476
- },
2477
- {
2478
- tag: "script",
2479
- children: `window.${DASHBOARD_EVENTS_GLOBAL_KEY} = ${JSON.stringify(runtimeEvents.current)}`,
2480
- injectTo: "head-prepend"
2481
- },
2482
- {
2483
- tag: "script",
2484
- attrs: {
2485
- type: "module",
2486
- src: "/@vite/client"
2487
- },
2488
- injectTo: "head"
2489
- },
2490
- {
2491
- tag: "script",
2492
- attrs: { type: "module" },
2493
- children: hotBridgeScript,
2494
- injectTo: "body"
2495
- }
2496
- ]
2497
- };
2498
- },
2499
- configureServer(server) {
2500
- onServerInstance(server);
2501
- server.middlewares.use((req, res, next) => {
2502
- const url = new URL(req.url ?? "/", "http://127.0.0.1");
2503
- if (url.pathname === FILE_CONTENT_PATH) {
2504
- sendDashboardFileContent(res, contentRoots, contentAllowlist.current, url.searchParams.get("kind"), url.searchParams.get("path"));
2505
- return;
2506
- }
2507
- if (url.pathname !== ANALYZE_SSE_PATH) {
2508
- next();
2509
- return;
2510
- }
2511
- res.statusCode = 200;
2512
- res.setHeader("Content-Type", "text/event-stream");
2513
- res.setHeader("Cache-Control", "no-cache, no-transform");
2514
- res.setHeader("Connection", "keep-alive");
2515
- res.write(`data: ${JSON.stringify(state)}\n\n`);
2516
- sseClients.add(res);
2517
- req.on("close", () => {
2518
- sseClients.delete(res);
2519
- });
2520
- });
2521
- }
2522
- };
2523
- }
2524
2840
  async function waitForServerExit(server) {
2525
2841
  let resolved = false;
2526
2842
  const cleanup = async () => {
@@ -2553,9 +2869,9 @@ async function startAnalyzeDashboard(result, options) {
2553
2869
  const { root, configFile } = resolved;
2554
2870
  const state = {
2555
2871
  current: result,
2556
- previous: options?.previousResult ?? null
2872
+ previous: options.previousResult ?? null,
2873
+ artifacts: options.artifacts
2557
2874
  };
2558
- const contentAllowlist = { current: createDashboardContentAllowlist(result) };
2559
2875
  const runtimeEvents = { current: [createDashboardRuntimeEvent({
2560
2876
  kind: "command",
2561
2877
  level: "success",
@@ -2563,16 +2879,16 @@ async function startAnalyzeDashboard(result, options) {
2563
2879
  detail: options?.watch ? "weapp-vite UI 已进入实时分析模式,后续 analyze 结果会继续推送到 dashboard。" : "weapp-vite UI 已进入静态分析模式,当前页面展示的是一次性分析结果。",
2564
2880
  tags: options?.watch ? ["watch", "analyze"] : ["static", "analyze"]
2565
2881
  }), ...(options?.initialEvents ?? []).map((event) => createDashboardRuntimeEvent(event))] };
2566
- let serverRef;
2567
- let broadcastAnalyzeResult;
2568
- const plugins = [createAnalyzeHtmlPlugin(state, runtimeEvents, {
2569
- artifactRoot: options?.artifactRoot ?? (options?.cwd ? path.resolve(options.cwd, "dist") : void 0),
2570
- sourceRoot: options?.cwd
2571
- }, contentAllowlist, (server) => {
2572
- serverRef = server;
2573
- }, (broadcast) => {
2574
- broadcastAnalyzeResult = broadcast;
2575
- })];
2882
+ const devframe = createAnalyzeDashboardDevframe({
2883
+ getAnalyzeSnapshot: () => state,
2884
+ getRuntimeEvents: () => runtimeEvents.current,
2885
+ roots: {
2886
+ pluginRoot: options?.pluginRoot,
2887
+ projectRoot: options?.cwd,
2888
+ srcRoot: options?.srcRoot ?? (options?.cwd ? path.resolve(options.cwd, "src") : void 0)
2889
+ }
2890
+ });
2891
+ const plugins = [createAnalyzeDashboardViteBridge(devframe.definition)];
2576
2892
  const serverOptions = {
2577
2893
  root,
2578
2894
  configFile: configFile ?? false,
@@ -2590,42 +2906,31 @@ async function startAnalyzeDashboard(result, options) {
2590
2906
  const server = await createServer(serverOptions);
2591
2907
  const requestedPort = typeof serverOptions.server?.port === "number" ? serverOptions.server.port : void 0;
2592
2908
  await server.listen(requestedPort);
2593
- serverRef ??= server;
2594
- server.printUrls();
2595
2909
  const urls = (() => {
2596
2910
  const resolved = server.resolvedUrls;
2597
2911
  if (!resolved) return [];
2598
2912
  return [...resolved.local ?? [], ...resolved.network ?? []];
2599
2913
  })();
2600
- const waitPromise = waitForServerExit(server);
2601
- if (serverRef?.ws) {
2602
- serverRef.ws.send({
2603
- type: "custom",
2604
- event: "weapp-analyze:update",
2605
- data: state
2606
- });
2607
- serverRef.ws.send({
2608
- type: "custom",
2609
- event: DASHBOARD_EVENT_NAME,
2610
- data: runtimeEvents.current
2611
- });
2612
- }
2613
- broadcastAnalyzeResult?.(state);
2914
+ const authCode = refreshTempAuthCode();
2915
+ const authenticatedUrls = urls.map((url) => buildOtpAuthUrl(url, authCode));
2916
+ let closed = false;
2917
+ const waitPromise = waitForServerExit(server).then(() => {
2918
+ closed = true;
2919
+ state.artifacts = /* @__PURE__ */ new Map();
2920
+ devframe.dispose();
2921
+ });
2614
2922
  const emitRuntimeEvents = (events) => {
2615
2923
  if (events.length === 0) return;
2616
2924
  const nextEvents = events.map((event) => createDashboardRuntimeEvent(event));
2617
2925
  runtimeEvents.current = [...nextEvents, ...runtimeEvents.current].slice(0, 24);
2618
- if (serverRef) serverRef.ws.send({
2619
- type: "custom",
2620
- event: DASHBOARD_EVENT_NAME,
2621
- data: nextEvents
2622
- });
2926
+ devframe.syncRuntimeEvents();
2623
2927
  };
2624
2928
  const handle = {
2625
- async update(nextResult, previousResult) {
2929
+ async update(nextResult, artifacts, previousResult) {
2930
+ if (closed) return;
2626
2931
  state.previous = previousResult ?? state.current;
2627
2932
  state.current = nextResult;
2628
- contentAllowlist.current = createDashboardContentAllowlist(nextResult);
2933
+ state.artifacts = artifacts;
2629
2934
  emitRuntimeEvents([{
2630
2935
  kind: "build",
2631
2936
  level: "info",
@@ -2633,19 +2938,15 @@ async function startAnalyzeDashboard(result, options) {
2633
2938
  detail: `已推送新的 analyze 结果,当前包含 ${nextResult.packages.length} 个包与 ${nextResult.modules.length} 个模块。`,
2634
2939
  tags: ["analyze", "refresh"]
2635
2940
  }]);
2636
- if (serverRef) serverRef.ws.send({
2637
- type: "custom",
2638
- event: "weapp-analyze:update",
2639
- data: state
2640
- });
2641
- broadcastAnalyzeResult?.(state);
2941
+ devframe.notifyAnalyzeUpdate();
2642
2942
  },
2643
2943
  emitRuntimeEvents,
2644
2944
  waitForExit: () => waitPromise,
2645
2945
  close: async () => {
2946
+ closed = true;
2646
2947
  await server.close();
2647
2948
  },
2648
- urls
2949
+ urls: authenticatedUrls
2649
2950
  };
2650
2951
  if (options?.watch) {
2651
2952
  if (!options.silentStartupLog) {
@@ -2962,6 +3263,7 @@ function registerAnalyzeCommand(cli) {
2962
3263
  if (reportType && !outputPrReport) throw new Error(`不支持的 analyze report 类型:${reportType}`);
2963
3264
  const budgetCheck = coerceBooleanOption(options.budgetCheck);
2964
3265
  const glassEaselCheck = coerceBooleanOption(options.glassEaselCheck);
3266
+ const launchDashboard = !outputJson && !outputMarkdown && !outputPrReport && !budgetCheck && !glassEaselCheck;
2965
3267
  const targets = resolveRuntimeTargets(options);
2966
3268
  const inlineConfig = createInlineConfig(targets);
2967
3269
  let ctx;
@@ -3017,7 +3319,8 @@ function registerAnalyzeCommand(cli) {
3017
3319
  }
3018
3320
  const previousResult = await readLatestAnalyzeHistorySnapshot(ctx.configService);
3019
3321
  ctx.runtimeState.glassEasel.silent = Boolean(outputJson || outputMarkdown || outputPrReport || glassEaselCheck);
3020
- const result = await analyzeSubpackages(ctx);
3322
+ const artifactSnapshot = launchDashboard ? createDashboardArtifactSnapshot() : void 0;
3323
+ const result = artifactSnapshot ? await analyzeSubpackages(ctx, { onArtifact: artifactSnapshot.capture }) : await analyzeSubpackages(ctx);
3021
3324
  await writeAnalyzeHistorySnapshot(result, ctx.configService);
3022
3325
  const writtenPath = await writeAnalyzeResult(result, outputOption, ctx.configService, outputPrReport ? "pr" : outputMarkdown ? "markdown" : "json", previousResult);
3023
3326
  if (outputPrReport) {
@@ -3031,12 +3334,14 @@ function registerAnalyzeCommand(cli) {
3031
3334
  const glassEaselFailed = glassEaselCheck && !outputJson ? printGlassEaselCheckSummary(result) : glassEaselCheck && result.glassEasel.summary.errors > 0;
3032
3335
  if (budgetFailed || glassEaselFailed) process.exitCode = 1;
3033
3336
  if (budgetCheck || glassEaselCheck) return;
3034
- if (!outputPrReport && !outputMarkdown && !outputJson) {
3337
+ if (artifactSnapshot) {
3035
3338
  printAnalysisSummary(result);
3036
3339
  await startAnalyzeDashboard(result, {
3037
- artifactRoot: ctx.configService.outDir,
3340
+ artifacts: artifactSnapshot.files,
3038
3341
  cwd: ctx.configService.cwd,
3039
3342
  packageManagerAgent: ctx.configService.packageManager.agent,
3343
+ pluginRoot: ctx.configService.absolutePluginRoot,
3344
+ srcRoot: ctx.configService.absoluteSrcRoot,
3040
3345
  previousResult
3041
3346
  });
3042
3347
  }
@@ -3227,14 +3532,17 @@ function registerBuildCommand(cli) {
3227
3532
  if (enableAnalyze) {
3228
3533
  const analyzeStartedAt = Date.now();
3229
3534
  const previousAnalyzeResult = await readLatestAnalyzeHistorySnapshot(configService);
3230
- const analyzeResult = await analyzeSubpackages(ctx);
3535
+ const artifactSnapshot = createDashboardArtifactSnapshot();
3536
+ const analyzeResult = await analyzeSubpackages(ctx, { onArtifact: artifactSnapshot.capture });
3231
3537
  await writeAnalyzeHistorySnapshot(analyzeResult, configService);
3232
3538
  const analyzeDurationMs = Date.now() - analyzeStartedAt;
3233
3539
  analyzeHandle = await startAnalyzeDashboard(analyzeResult, {
3234
3540
  watch: true,
3235
- artifactRoot: configService.outDir,
3541
+ artifacts: artifactSnapshot.files,
3236
3542
  cwd: configService.cwd,
3237
3543
  packageManagerAgent: configService.packageManager.agent,
3544
+ pluginRoot: configService.absolutePluginRoot,
3545
+ srcRoot: configService.absoluteSrcRoot,
3238
3546
  previousResult: previousAnalyzeResult,
3239
3547
  initialEvents: [{
3240
3548
  kind: "build",
@@ -5062,7 +5370,7 @@ function resolveRunnableHotkeyDefinition(input) {
5062
5370
  }
5063
5371
  //#endregion
5064
5372
  //#region package.json
5065
- var version = "7.1.4";
5373
+ var version = "7.2.1";
5066
5374
  //#endregion
5067
5375
  //#region src/cli/devHotkeys/format.ts
5068
5376
  const FULLWIDTH_ASCII_START = 65281;
@@ -5465,7 +5773,7 @@ function getCompressedSizes(content) {
5465
5773
  brotliSize: brotliCompressSync(content).byteLength
5466
5774
  };
5467
5775
  }
5468
- async function analyzeUiFallback(ctx) {
5776
+ async function analyzeUiFallback(ctx, options) {
5469
5777
  const { configService, scanService } = ctx;
5470
5778
  await scanService.loadAppEntry();
5471
5779
  const subPackageMetas = scanService.loadSubPackages();
@@ -5501,17 +5809,17 @@ async function analyzeUiFallback(ctx) {
5501
5809
  for (const absoluteFile of absoluteFiles) {
5502
5810
  const relativeFile = path.relative(distRoot, absoluteFile).replace(REG_DIST_POSIX_SEP, "/");
5503
5811
  const packageInfo = classifyPackage(relativeFile);
5504
- const stat = await fs$2.stat(absoluteFile);
5505
5812
  const content = await fs$2.readFile(absoluteFile);
5506
5813
  ensurePackage(packageInfo.id, packageInfo.type).files.push({
5507
5814
  file: relativeFile,
5508
5815
  type: relativeFile.endsWith(".js") ? "chunk" : "asset",
5509
5816
  from: packageInfo.type === "independent" ? "independent" : "main",
5510
- size: stat.size,
5817
+ size: content.byteLength,
5511
5818
  ...getCompressedSizes(content),
5512
5819
  isEntry: relativeFile === "app.js" || REG_DIST_PAGE_ENTRY.test(relativeFile),
5513
5820
  source: relativeFile.endsWith(".js") ? void 0 : relativeFile
5514
5821
  });
5822
+ options?.onArtifact?.(relativeFile, content);
5515
5823
  }
5516
5824
  return {
5517
5825
  metadata: createAnalyzeMetadata(configService),
@@ -5537,8 +5845,21 @@ function createAnalyzeController(options) {
5537
5845
  const runAnalyze = async () => {
5538
5846
  const startedAt = Date.now();
5539
5847
  const previousResult = await readLatestAnalyzeHistorySnapshot(configService);
5848
+ const runFallback = async (fallbackReason) => {
5849
+ const artifactSnapshot = createDashboardArtifactSnapshot();
5850
+ const result = await analyzeUiFallback(ctx, { onArtifact: artifactSnapshot.capture });
5851
+ await writeAnalyzeHistorySnapshot(result, configService);
5852
+ return {
5853
+ result,
5854
+ artifacts: artifactSnapshot.files,
5855
+ previousResult,
5856
+ durationMs: Date.now() - startedAt,
5857
+ mode: "fallback",
5858
+ fallbackReason
5859
+ };
5860
+ };
5540
5861
  try {
5541
- const result = await analyzeSubpackages(await createCompilerContext({
5862
+ const analyzeCtx = await createCompilerContext({
5542
5863
  key: `serve-ui-analyze:${process.pid}:${++analyzeRunId}`,
5543
5864
  cwd: configService.cwd,
5544
5865
  mode: configService.mode,
@@ -5548,11 +5869,14 @@ function createAnalyzeController(options) {
5548
5869
  cliPlatform: targets.rawPlatform,
5549
5870
  projectConfigPath: cliOptions.projectConfig,
5550
5871
  syncSupportFiles: false
5551
- }));
5872
+ });
5873
+ const artifactSnapshot = createDashboardArtifactSnapshot();
5874
+ const result = await analyzeSubpackages(analyzeCtx, { onArtifact: artifactSnapshot.capture });
5552
5875
  if (hasAnalyzeData(result)) {
5553
5876
  await writeAnalyzeHistorySnapshot(result, configService);
5554
5877
  return {
5555
5878
  result,
5879
+ artifacts: artifactSnapshot.files,
5556
5880
  previousResult,
5557
5881
  durationMs: Date.now() - startedAt,
5558
5882
  mode: "full"
@@ -5561,25 +5885,9 @@ function createAnalyzeController(options) {
5561
5885
  } catch (error) {
5562
5886
  const message = error instanceof Error ? error.message : String(error);
5563
5887
  logger_default.warn(`[ui] 完整分析失败,已回退到 dist 文件扫描:${message}`);
5564
- const result = await analyzeUiFallback(ctx);
5565
- await writeAnalyzeHistorySnapshot(result, configService);
5566
- return {
5567
- result,
5568
- previousResult,
5569
- durationMs: Date.now() - startedAt,
5570
- mode: "fallback",
5571
- fallbackReason: message
5572
- };
5888
+ return runFallback(message);
5573
5889
  }
5574
- const result = await analyzeUiFallback(ctx);
5575
- await writeAnalyzeHistorySnapshot(result, configService);
5576
- return {
5577
- result,
5578
- previousResult,
5579
- durationMs: Date.now() - startedAt,
5580
- mode: "fallback",
5581
- fallbackReason: "完整分析结果为空,已回退到 dist 文件扫描。"
5582
- };
5890
+ return runFallback("完整分析结果为空,已回退到 dist 文件扫描。");
5583
5891
  };
5584
5892
  const triggerAnalyzeUpdate = async (reason = "watch") => {
5585
5893
  if (!analyzeHandle) return;
@@ -5599,7 +5907,7 @@ function createAnalyzeController(options) {
5599
5907
  durationMs: next.durationMs,
5600
5908
  tags: ["analyze", "fallback"]
5601
5909
  }]);
5602
- await analyzeHandle.update(next.result, next.previousResult);
5910
+ await analyzeHandle.update(next.result, next.artifacts, next.previousResult);
5603
5911
  emitDashboardEvents(analyzeHandle, [{
5604
5912
  kind: next.mode === "fallback" ? "diagnostic" : "build",
5605
5913
  level: next.mode === "fallback" ? "warning" : "success",
@@ -5635,9 +5943,11 @@ function createAnalyzeController(options) {
5635
5943
  const initialAnalyze = await runAnalyze();
5636
5944
  analyzeHandle = await startDashboard(initialAnalyze.result, {
5637
5945
  watch: true,
5638
- artifactRoot: configService.outDir,
5946
+ artifacts: initialAnalyze.artifacts,
5639
5947
  cwd: configService.cwd,
5640
5948
  packageManagerAgent: configService.packageManager.agent,
5949
+ pluginRoot: configService.absolutePluginRoot,
5950
+ srcRoot: configService.absoluteSrcRoot,
5641
5951
  silentStartupLog: true,
5642
5952
  previousResult: initialAnalyze.previousResult,
5643
5953
  initialEvents: [{