fix(ui): split control ui runtime chunks

This commit is contained in:
Vincent Koc
2026-05-24 22:20:27 +02:00
parent fe34141a3d
commit 8bf4f7d4a8
4 changed files with 92 additions and 1 deletions
+1
View File
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Control UI: split large build-time runtime dependencies into stable chunks so Linux/Docker install and package builds stay below the app chunk warning threshold.
- Scripts: run the optional Discord native opus installer through the shared pnpm launcher and Windows CI coverage so native Windows installs avoid shell-mode package-manager shims.
- Agents/MCP: bound bundled MCP `tools/list` catalog discovery so hung MCP servers do not block session tool materialization. (#85063) Thanks @nxmxbbd.
- Scripts: run generated-module formatting through the shared pnpm launcher and Windows CI coverage so native Windows generator checks avoid shell-mode package-manager shims.
+49
View File
@@ -0,0 +1,49 @@
export function normalizeModuleId(id: string): string {
return id.replace(/\\/g, "/");
}
export function moduleIdIncludesPackage(id: string, packageName: string): boolean {
const normalized = normalizeModuleId(id);
return (
normalized.includes(`/node_modules/${packageName}/`) ||
normalized.includes(`/openclaw-pnpm-node-modules/${packageName}/`)
);
}
export function controlUiManualChunk(id: string): string | undefined {
if (
moduleIdIncludesPackage(id, "lit") ||
moduleIdIncludesPackage(id, "lit-html") ||
moduleIdIncludesPackage(id, "@lit/reactive-element")
) {
return "lit-runtime";
}
if (
moduleIdIncludesPackage(id, "highlight.js") ||
moduleIdIncludesPackage(id, "markdown-it") ||
moduleIdIncludesPackage(id, "markdown-it-task-lists") ||
moduleIdIncludesPackage(id, "dompurify") ||
moduleIdIncludesPackage(id, "entities") ||
moduleIdIncludesPackage(id, "linkify-it") ||
moduleIdIncludesPackage(id, "mdurl") ||
moduleIdIncludesPackage(id, "punycode.js") ||
moduleIdIncludesPackage(id, "uc.micro")
) {
return "markdown-runtime";
}
if (moduleIdIncludesPackage(id, "zod") || moduleIdIncludesPackage(id, "json5")) {
return "config-runtime";
}
if (
moduleIdIncludesPackage(id, "@noble/ed25519") ||
moduleIdIncludesPackage(id, "@noble/hashes") ||
moduleIdIncludesPackage(id, "ipaddr.js")
) {
return "gateway-runtime";
}
return undefined;
}
+35
View File
@@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import { controlUiManualChunk, normalizeModuleId } from "../../build/chunking.ts";
describe("Control UI build chunking", () => {
it("groups stable runtime dependencies into bounded chunks", () => {
expect(controlUiManualChunk("/repo/ui/node_modules/lit/index.js")).toBe("lit-runtime");
expect(controlUiManualChunk("/repo/ui/node_modules/lit-html/directives/repeat.js")).toBe(
"lit-runtime",
);
expect(controlUiManualChunk("/repo/ui/node_modules/highlight.js/lib/core.js")).toBe(
"markdown-runtime",
);
expect(
controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/dompurify/dist/purify.es.mjs"),
).toBe("markdown-runtime");
expect(controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/zod/v4/core/schemas.js")).toBe(
"config-runtime",
);
expect(controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/json5/dist/index.js")).toBe(
"config-runtime",
);
expect(controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/@noble/ed25519/index.js")).toBe(
"gateway-runtime",
);
expect(controlUiManualChunk("/repo/ui/src/ui/app-render.ts")).toBeUndefined();
});
it("normalizes Windows module paths before package matching", () => {
expect(normalizeModuleId(String.raw`C:\repo\ui\node_modules\highlight.js\lib\core.js`)).toBe(
"C:/repo/ui/node_modules/highlight.js/lib/core.js",
);
expect(controlUiManualChunk(String.raw`C:\repo\ui\node_modules\highlight.js\lib\core.js`))
.toBe("markdown-runtime");
});
});
+7 -1
View File
@@ -3,6 +3,7 @@ import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig, type Plugin } from "vite";
import { controlUiManualChunk } from "./build/chunking.ts";
const here = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(here, "..");
@@ -98,7 +99,12 @@ export default defineConfig(() => {
outDir,
emptyOutDir: true,
sourcemap: true,
// Keep CI/onboard logs clean; current control UI chunking is intentionally above 500 kB.
rollupOptions: {
output: {
manualChunks: controlUiManualChunk,
},
},
// Keep CI/onboard logs clean; the app chunk is split into stable runtime buckets above.
chunkSizeWarningLimit: 1024,
},
server: {