Skip to content

Commit 8bf4f7d

Browse files
committed
fix(ui): split control ui runtime chunks
1 parent fe34141 commit 8bf4f7d

4 files changed

Lines changed: 92 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
88

99
### Fixes
1010

11+
- 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.
1112
- 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.
1213
- Agents/MCP: bound bundled MCP `tools/list` catalog discovery so hung MCP servers do not block session tool materialization. (#85063) Thanks @nxmxbbd.
1314
- 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.

ui/build/chunking.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
export function normalizeModuleId(id: string): string {
2+
return id.replace(/\\/g, "/");
3+
}
4+
5+
export function moduleIdIncludesPackage(id: string, packageName: string): boolean {
6+
const normalized = normalizeModuleId(id);
7+
return (
8+
normalized.includes(`/node_modules/${packageName}/`) ||
9+
normalized.includes(`/openclaw-pnpm-node-modules/${packageName}/`)
10+
);
11+
}
12+
13+
export function controlUiManualChunk(id: string): string | undefined {
14+
if (
15+
moduleIdIncludesPackage(id, "lit") ||
16+
moduleIdIncludesPackage(id, "lit-html") ||
17+
moduleIdIncludesPackage(id, "@lit/reactive-element")
18+
) {
19+
return "lit-runtime";
20+
}
21+
22+
if (
23+
moduleIdIncludesPackage(id, "highlight.js") ||
24+
moduleIdIncludesPackage(id, "markdown-it") ||
25+
moduleIdIncludesPackage(id, "markdown-it-task-lists") ||
26+
moduleIdIncludesPackage(id, "dompurify") ||
27+
moduleIdIncludesPackage(id, "entities") ||
28+
moduleIdIncludesPackage(id, "linkify-it") ||
29+
moduleIdIncludesPackage(id, "mdurl") ||
30+
moduleIdIncludesPackage(id, "punycode.js") ||
31+
moduleIdIncludesPackage(id, "uc.micro")
32+
) {
33+
return "markdown-runtime";
34+
}
35+
36+
if (moduleIdIncludesPackage(id, "zod") || moduleIdIncludesPackage(id, "json5")) {
37+
return "config-runtime";
38+
}
39+
40+
if (
41+
moduleIdIncludesPackage(id, "@noble/ed25519") ||
42+
moduleIdIncludesPackage(id, "@noble/hashes") ||
43+
moduleIdIncludesPackage(id, "ipaddr.js")
44+
) {
45+
return "gateway-runtime";
46+
}
47+
48+
return undefined;
49+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, expect, it } from "vitest";
2+
import { controlUiManualChunk, normalizeModuleId } from "../../build/chunking.ts";
3+
4+
describe("Control UI build chunking", () => {
5+
it("groups stable runtime dependencies into bounded chunks", () => {
6+
expect(controlUiManualChunk("/repo/ui/node_modules/lit/index.js")).toBe("lit-runtime");
7+
expect(controlUiManualChunk("/repo/ui/node_modules/lit-html/directives/repeat.js")).toBe(
8+
"lit-runtime",
9+
);
10+
expect(controlUiManualChunk("/repo/ui/node_modules/highlight.js/lib/core.js")).toBe(
11+
"markdown-runtime",
12+
);
13+
expect(
14+
controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/dompurify/dist/purify.es.mjs"),
15+
).toBe("markdown-runtime");
16+
expect(controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/zod/v4/core/schemas.js")).toBe(
17+
"config-runtime",
18+
);
19+
expect(controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/json5/dist/index.js")).toBe(
20+
"config-runtime",
21+
);
22+
expect(controlUiManualChunk("/tmp/openclaw-pnpm-node-modules/@noble/ed25519/index.js")).toBe(
23+
"gateway-runtime",
24+
);
25+
expect(controlUiManualChunk("/repo/ui/src/ui/app-render.ts")).toBeUndefined();
26+
});
27+
28+
it("normalizes Windows module paths before package matching", () => {
29+
expect(normalizeModuleId(String.raw`C:\repo\ui\node_modules\highlight.js\lib\core.js`)).toBe(
30+
"C:/repo/ui/node_modules/highlight.js/lib/core.js",
31+
);
32+
expect(controlUiManualChunk(String.raw`C:\repo\ui\node_modules\highlight.js\lib\core.js`))
33+
.toBe("markdown-runtime");
34+
});
35+
});

ui/vite.config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import fs from "node:fs";
33
import path from "node:path";
44
import { fileURLToPath } from "node:url";
55
import { defineConfig, type Plugin } from "vite";
6+
import { controlUiManualChunk } from "./build/chunking.ts";
67

78
const here = path.dirname(fileURLToPath(import.meta.url));
89
const repoRoot = path.resolve(here, "..");
@@ -98,7 +99,12 @@ export default defineConfig(() => {
9899
outDir,
99100
emptyOutDir: true,
100101
sourcemap: true,
101-
// Keep CI/onboard logs clean; current control UI chunking is intentionally above 500 kB.
102+
rollupOptions: {
103+
output: {
104+
manualChunks: controlUiManualChunk,
105+
},
106+
},
107+
// Keep CI/onboard logs clean; the app chunk is split into stable runtime buckets above.
102108
chunkSizeWarningLimit: 1024,
103109
},
104110
server: {

0 commit comments

Comments
 (0)