From 137fbf875e9fb997db35e65e4b4ed8df215fed1e Mon Sep 17 00:00:00 2001 From: chengyongru <2755839590@qq.com> Date: Fri, 10 Jul 2026 23:10:01 +0800 Subject: [PATCH] fix(webui): keep syntax highlighting chunks acyclic --- ...diff-syntax-highlight.integration.test.tsx | 6 +- webui/src/tests/vite-config.test.ts | 21 +++++++ webui/vite.config.ts | 57 ++++++++++--------- 3 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 webui/src/tests/vite-config.test.ts diff --git a/webui/src/tests/diff-syntax-highlight.integration.test.tsx b/webui/src/tests/diff-syntax-highlight.integration.test.tsx index a5ee47de..3a721650 100644 --- a/webui/src/tests/diff-syntax-highlight.integration.test.tsx +++ b/webui/src/tests/diff-syntax-highlight.integration.test.tsx @@ -43,9 +43,11 @@ describe("DiffSyntaxHighlight with Prism", () => { const highlighted = await screen.findByTestId("syntax-highlighted-diff-hunk"); await waitFor( () => { - expect(highlighted.querySelectorAll('td:last-child span[style*="color"]')).not.toHaveLength( - 0, + const tokens = highlighted.querySelectorAll( + 'td:last-child span[style*="color"]', ); + expect(tokens).not.toHaveLength(0); + expect(new Set([...tokens].map((token) => token.style.color)).size).toBeGreaterThan(1); }, { timeout: 10_000 }, ); diff --git a/webui/src/tests/vite-config.test.ts b/webui/src/tests/vite-config.test.ts new file mode 100644 index 00000000..a3174837 --- /dev/null +++ b/webui/src/tests/vite-config.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "vitest"; + +import { webuiManualChunk } from "../../vite.config"; + +describe("webuiManualChunk", () => { + it("keeps Refractor's selector parser in the syntax highlighting chunk", () => { + expect( + webuiManualChunk("/repo/node_modules/hast-util-parse-selector/index.js"), + ).toBe("syntax-highlight"); + }); + + it("keeps markdown-only hast utilities in the markdown chunk", () => { + expect( + webuiManualChunk("/repo/node_modules/hast-util-to-jsx-runtime/lib/index.js"), + ).toBe("markdown-vendor"); + }); + + it("leaves language grammars as independently loaded chunks", () => { + expect(webuiManualChunk("/repo/node_modules/refractor/lang/python.js")).toBeUndefined(); + }); +}); diff --git a/webui/vite.config.ts b/webui/vite.config.ts index 28729732..4ffc48e4 100644 --- a/webui/vite.config.ts +++ b/webui/vite.config.ts @@ -2,6 +2,36 @@ import { defineConfig, loadEnv } from "vite"; import react from "@vitejs/plugin-react"; import path from "node:path"; +export function webuiManualChunk(id: string): string | undefined { + if (id.includes("node_modules/refractor/lang/")) { + return; + } + // Refractor reaches this HAST helper through hastscript. Keeping it with + // Refractor prevents syntax-highlight <-> markdown-vendor circular chunks. + if ( + id.includes("node_modules/react-syntax-highlighter") + || id.includes("node_modules/refractor/core") + || id.includes("node_modules/hast-util-parse-selector") + ) { + return "syntax-highlight"; + } + if ( + id.includes("node_modules/react-markdown") + || id.includes("node_modules/remark-") + || id.includes("node_modules/rehype-") + || id.includes("node_modules/unified") + || id.includes("node_modules/mdast-") + || id.includes("node_modules/hast-") + || id.includes("node_modules/micromark") + || id.includes("node_modules/unist-") + ) { + return "markdown-vendor"; + } + if (id.includes("node_modules/katex")) { + return "katex"; + } +} + export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ""); const target = env.NANOBOT_API_URL ?? "http://127.0.0.1:8765"; @@ -32,32 +62,7 @@ export default defineConfig(({ mode }) => { sourcemap: false, rollupOptions: { output: { - manualChunks(id) { - if (id.includes("node_modules/refractor/lang/")) { - return; - } - if ( - id.includes("node_modules/react-syntax-highlighter") - || id.includes("node_modules/refractor/core") - ) { - return "syntax-highlight"; - } - if ( - id.includes("node_modules/react-markdown") - || id.includes("node_modules/remark-") - || id.includes("node_modules/rehype-") - || id.includes("node_modules/unified") - || id.includes("node_modules/mdast-") - || id.includes("node_modules/hast-") - || id.includes("node_modules/micromark") - || id.includes("node_modules/unist-") - ) { - return "markdown-vendor"; - } - if (id.includes("node_modules/katex")) { - return "katex"; - } - }, + manualChunks: webuiManualChunk, }, }, },