forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmeasure-copilotchat.mjs
More file actions
113 lines (109 loc) · 4.02 KB
/
Copy pathmeasure-copilotchat.mjs
File metadata and controls
113 lines (109 loc) · 4.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Measures a relative bundle-regression signal for `CopilotChat`: the total
// gzipped JS that bundling `{ CopilotChat }` from `@copilotkit/react-core/v2`
// produces (entry + all code-split chunks).
//
// IMPORTANT — this is a relative regression signal, NOT a production figure.
// It uses esbuild; a real consumer (Vite/Next/webpack) splits eager-vs-lazy
// differently and reports different absolute numbers. Its value is consistency
// across PRs: the same script every PR, so a change that grows CopilotChat's
// JS shows up.
//
// Why a custom esbuild script instead of size-limit: CopilotChat's graph pulls
// `katex/dist/katex.min.css`, whose url() font refs crash size-limit's esbuild
// preset (which exposes no loader/config hook). Driving esbuild directly lets
// us stub CSS/fonts to "empty" (we measure JS, not CSS). react/react-dom are
// external — a host React app already ships them.
//
// Run: `node scripts/measure-copilotchat.mjs` (after `nx run react-core:build`).
import { build } from "esbuild";
import { gzipSync } from "node:zlib";
import { fileURLToPath } from "node:url";
import path from "node:path";
const DEFAULT_EXTERNAL = [
"react",
"react-dom",
"react/jsx-runtime",
"react/jsx-dev-runtime",
"react-dom/client",
"react-dom/server",
];
const EMPTY_LOADERS = {
".css": "empty",
".woff": "empty",
".woff2": "empty",
".ttf": "empty",
".eot": "empty",
".svg": "empty",
};
/**
* Bundle `export { CopilotChat } from <entryModule>` with esbuild (minified,
* code-split, react/react-dom external, CSS/fonts stubbed empty) and return
* the summed gzip byte count across all output chunks.
*
* @param {object} options
* @param {string} options.entryModule - Absolute path to the module that exports CopilotChat.
* @param {string} options.pkgRoot - Working directory for esbuild resolution and the (in-memory) outdir.
* @param {string[]} [options.external] - External specifiers; defaults to react/react-dom subpaths.
* @param {Record<string, string>} [options.loader] - esbuild loader map; defaults to stubbing CSS/fonts.
* @returns {Promise<{ totalBytes: number, outputCount: number }>}
*/
export async function measureBundle({
entryModule,
pkgRoot,
external = DEFAULT_EXTERNAL,
loader = EMPTY_LOADERS,
}) {
const result = await build({
stdin: {
contents: `export { CopilotChat } from ${JSON.stringify(entryModule)};`,
resolveDir: pkgRoot,
loader: "js",
},
bundle: true,
splitting: true,
format: "esm",
platform: "browser",
target: "es2022",
minify: true,
outdir: path.join(pkgRoot, ".size-headline-tmp"),
write: false,
external,
loader,
logLevel: "silent",
});
let totalBytes = 0;
for (const file of result.outputFiles) {
totalBytes += gzipSync(Buffer.from(file.contents)).length;
}
return { totalBytes, outputCount: result.outputFiles.length };
}
// CLI entry — only runs when invoked directly, so importing this module from
// tests doesn't perform a real build at module-load time.
const isMain =
import.meta.url === `file://${process.argv[1]}` ||
import.meta.url === `file://${path.resolve(process.argv[1] ?? "")}`;
if (isMain) {
const pkgRoot = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"..",
);
const entryModule = path.join(pkgRoot, "dist/v2/index.mjs");
const { totalBytes } = await measureBundle({ entryModule, pkgRoot });
if (totalBytes === 0) {
console.error("measure-copilotchat: esbuild produced no output");
process.exit(1);
}
const mb = (totalBytes / 1024 / 1024).toFixed(2);
const caption =
"esbuild relative regression signal — not a production Vite/Next figure";
console.log(`CopilotChat total bundled JS: ${mb} MB gzip (${caption})`);
if (process.env.GITHUB_STEP_SUMMARY) {
const { appendFileSync } = await import("node:fs");
appendFileSync(
process.env.GITHUB_STEP_SUMMARY,
`### Bundle regression signal — react-core CopilotChat\n\n` +
`- **CopilotChat total bundled JS: ${mb} MB gzip**\n` +
`- _${caption}_\n`,
);
}
}