niceeval 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/view/index.ts CHANGED
@@ -2,9 +2,10 @@
2
2
  // 读取/版本判定在 loader.ts,聚合在 aggregate.ts,HTTP 与 HTML 烘焙在 server.ts,
3
3
  // server/前端共用的折叠口径、格式化与数据形状在 shared/。
4
4
 
5
- import { mkdir, writeFile } from "node:fs/promises";
6
- import { dirname, resolve } from "node:path";
7
- import { loadSummaries } from "./loader.ts";
5
+ import { copyFile, mkdir, writeFile } from "node:fs/promises";
6
+ import { existsSync } from "node:fs";
7
+ import { dirname, join, resolve } from "node:path";
8
+ import { loadSummaries, type ScanResult } from "./loader.ts";
8
9
  import { renderHtml, type ViewOptions } from "./server.ts";
9
10
 
10
11
  export { startViewServer, type ViewOptions, type ViewServer } from "./server.ts";
@@ -19,11 +20,45 @@ export {
19
20
  type SkippedRun,
20
21
  } from "./loader.ts";
21
22
 
22
- /** 导出静态 HTML 报告(--out):一次性把当前结果烘焙进单个可分享文件。 */
23
+ /** --out 目标是单文件(*.html)还是目录式静态导出;CLI 的提示文案也按这个口径分。 */
24
+ export function isSingleFileOut(out: string): boolean {
25
+ return /\.html?$/i.test(out);
26
+ }
27
+
28
+ /**
29
+ * 导出静态报告(--out)。两种形态:
30
+ * - `--out report.html`:单个可分享 HTML。榜单/断言/检查自包含;代码视图、transcript、
31
+ * trace 依赖工件文件,单文件带不动(diff/trace 可达上百 MB),静态托管时优雅降级。
32
+ * - `--out <dir>`:目录式静态导出。写 <dir>/index.html,并把前端会 fetch 的工件
33
+ * (sources.json / events.json / trace.json)复制到 <dir>/artifact/<base>/——与本地
34
+ * server 的 /artifact/<rel> 路由同一布局,整个目录扔给任何静态托管即是完整体验。
35
+ */
23
36
  export async function buildView(opts: ViewOptions = {}): Promise<string> {
24
37
  const scan = await loadSummaries(opts.input);
25
38
  const out = resolve(opts.out ?? ".niceeval/report.html");
26
- await mkdir(dirname(out), { recursive: true });
27
- await writeFile(out, await renderHtml(scan), "utf-8");
39
+ if (isSingleFileOut(out)) {
40
+ await mkdir(dirname(out), { recursive: true });
41
+ await writeFile(out, await renderHtml(scan), "utf-8");
42
+ return out;
43
+ }
44
+ await mkdir(out, { recursive: true });
45
+ await writeFile(join(out, "index.html"), await renderHtml(scan), "utf-8");
46
+ await copyFetchedArtifacts(scan, join(out, "artifact"));
28
47
  return out;
29
48
  }
49
+
50
+ // 只复制前端会 fetch 的三类工件。diff.json / o11y.json 是运行侧产物,查看器从不读取,
51
+ // 且 diff 可达上百 MB,带进静态导出只会拖垮部署体积。
52
+ const FETCHED_ARTIFACTS = ["sources.json", "events.json", "trace.json"];
53
+
54
+ async function copyFetchedArtifacts(scan: ScanResult, artifactRoot: string): Promise<void> {
55
+ for (const [base, srcDir] of scan.artifactDirs) {
56
+ const destDir = join(artifactRoot, base);
57
+ // 输入本身已经是导出布局(比如对着上次导出的目录重新生成 index.html)时不自拷。
58
+ if (resolve(srcDir) === resolve(destDir)) continue;
59
+ const files = FETCHED_ARTIFACTS.filter((name) => existsSync(join(srcDir, name)));
60
+ if (!files.length) continue;
61
+ await mkdir(destDir, { recursive: true });
62
+ await Promise.all(files.map((name) => copyFile(join(srcDir, name), join(destDir, name))));
63
+ }
64
+ }
@@ -37,6 +37,13 @@ export async function startViewServer(opts: ViewOptions = {}): Promise<ViewServe
37
37
  return;
38
38
  }
39
39
  // 按需提供拆分工件(trace.json / events.json / …),前端展开时 fetch。
40
+ // 路径式 /artifact/<rel> 与目录式静态导出的文件布局一致(见 view/index.ts 的 buildView),
41
+ // 同一份前端产物在本地 server 和静态托管上用同一个相对 URL。
42
+ if (url.pathname.startsWith("/artifact/")) {
43
+ await serveArtifact(root, decodeURIComponent(url.pathname.slice("/artifact/".length)), res);
44
+ return;
45
+ }
46
+ // 兼容旧的 query 形式(0.2.x 前端烘焙的 HTML 可能还开着)。
40
47
  if (url.pathname === "/artifact") {
41
48
  await serveArtifact(root, url.searchParams.get("p") ?? "", res);
42
49
  return;