@perfonext/build-mcp 0.1.0 → 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/README.md +34 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/parser/analysis.d.ts +8 -1
- package/dist/parser/analysis.d.ts.map +1 -1
- package/dist/parser/analysis.js +238 -8
- package/dist/parser/analysis.js.map +1 -1
- package/dist/parser/types.d.ts +86 -0
- package/dist/parser/types.d.ts.map +1 -1
- package/dist/parser/webpack-stats.d.ts +15 -0
- package/dist/parser/webpack-stats.d.ts.map +1 -0
- package/dist/parser/webpack-stats.js +195 -0
- package/dist/parser/webpack-stats.js.map +1 -0
- package/dist/store.d.ts +3 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +7 -0
- package/dist/store.js.map +1 -1
- package/dist/tools/explain-growth.d.ts +3 -0
- package/dist/tools/explain-growth.d.ts.map +1 -0
- package/dist/tools/explain-growth.js +97 -0
- package/dist/tools/explain-growth.js.map +1 -0
- package/dist/tools/how-to-collect-stats.d.ts +3 -0
- package/dist/tools/how-to-collect-stats.d.ts.map +1 -0
- package/dist/tools/how-to-collect-stats.js +137 -0
- package/dist/tools/how-to-collect-stats.js.map +1 -0
- package/dist/tools/load-webpack-stats.d.ts +3 -0
- package/dist/tools/load-webpack-stats.d.ts.map +1 -0
- package/dist/tools/load-webpack-stats.js +49 -0
- package/dist/tools/load-webpack-stats.js.map +1 -0
- package/dist/tools/trace-import.d.ts +3 -0
- package/dist/tools/trace-import.d.ts.map +1 -0
- package/dist/tools/trace-import.js +41 -0
- package/dist/tools/trace-import.js.map +1 -0
- package/dist/tools/webpack-shared.d.ts +21 -0
- package/dist/tools/webpack-shared.d.ts.map +1 -0
- package/dist/tools/webpack-shared.js +33 -0
- package/dist/tools/webpack-shared.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
const NODE_MODULES_MARKER = 'node_modules/';
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return value !== null && typeof value === 'object';
|
|
6
|
+
}
|
|
7
|
+
function toFiniteNumber(value) {
|
|
8
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
9
|
+
}
|
|
10
|
+
function toStringArray(value) {
|
|
11
|
+
if (!Array.isArray(value)) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return value.filter((item) => typeof item === 'string');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the npm package an emitted module belongs to. Uses the LAST `node_modules/` segment so
|
|
18
|
+
* nested installs (`a/node_modules/b`) attribute to the inner package, and preserves scoped names
|
|
19
|
+
* (`@org/pkg`). Returns null for first-party application code.
|
|
20
|
+
*/
|
|
21
|
+
export function extractPackageName(moduleName) {
|
|
22
|
+
// Webpack `name` uses POSIX separators, but the `identifier` fallback can carry
|
|
23
|
+
// Windows backslashes — normalize so attribution works on every platform.
|
|
24
|
+
const normalized = moduleName.replace(/\\/g, '/');
|
|
25
|
+
const markerIndex = normalized.lastIndexOf(NODE_MODULES_MARKER);
|
|
26
|
+
if (markerIndex === -1) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const rest = normalized.slice(markerIndex + NODE_MODULES_MARKER.length);
|
|
30
|
+
const parts = rest.split('/').filter(Boolean);
|
|
31
|
+
if (parts.length === 0) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (parts[0].startsWith('@') && parts.length >= 2) {
|
|
35
|
+
return `${parts[0]}/${parts[1]}`;
|
|
36
|
+
}
|
|
37
|
+
return parts[0];
|
|
38
|
+
}
|
|
39
|
+
function normalizeReason(raw) {
|
|
40
|
+
if (!isRecord(raw)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const moduleName = typeof raw.moduleName === 'string' ? raw.moduleName : null;
|
|
44
|
+
const userRequest = typeof raw.userRequest === 'string' ? raw.userRequest : null;
|
|
45
|
+
if (moduleName === null && userRequest === null) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return { moduleName, userRequest };
|
|
49
|
+
}
|
|
50
|
+
function normalizeModule(raw) {
|
|
51
|
+
const name = typeof raw.name === 'string'
|
|
52
|
+
? raw.name
|
|
53
|
+
: typeof raw.identifier === 'string'
|
|
54
|
+
? raw.identifier
|
|
55
|
+
: null;
|
|
56
|
+
if (!name) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const chunkIds = Array.isArray(raw.chunks)
|
|
60
|
+
? raw.chunks.filter((id) => typeof id === 'string' || typeof id === 'number')
|
|
61
|
+
: [];
|
|
62
|
+
const reasons = Array.isArray(raw.reasons)
|
|
63
|
+
? raw.reasons
|
|
64
|
+
.map(normalizeReason)
|
|
65
|
+
.filter((reason) => reason !== null)
|
|
66
|
+
: [];
|
|
67
|
+
return {
|
|
68
|
+
name,
|
|
69
|
+
packageName: extractPackageName(name),
|
|
70
|
+
sizeBytes: toFiniteNumber(raw.size),
|
|
71
|
+
chunkIds,
|
|
72
|
+
reasons,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function normalizeChunk(raw) {
|
|
76
|
+
if (raw.id === undefined || (typeof raw.id !== 'string' && typeof raw.id !== 'number')) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
id: raw.id,
|
|
81
|
+
names: toStringArray(raw.names),
|
|
82
|
+
files: toStringArray(raw.files),
|
|
83
|
+
sizeBytes: toFiniteNumber(raw.size),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Parse a webpack module-stats JSON (`.next/stats.json`) into a compact, analysis-ready shape.
|
|
88
|
+
* Only the fields the attribution tools need are retained — raw source, asset maps, and other
|
|
89
|
+
* webpack noise are dropped so a multi-MB stats file stays small in memory.
|
|
90
|
+
*/
|
|
91
|
+
export async function parseWebpackStats(buildDirPath, buildId) {
|
|
92
|
+
const buildDir = resolve(buildDirPath);
|
|
93
|
+
const statsPath = join(buildDir, 'stats.json');
|
|
94
|
+
let content;
|
|
95
|
+
try {
|
|
96
|
+
content = await readFile(statsPath, 'utf-8');
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error.code === 'ENOENT') {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
let raw;
|
|
105
|
+
try {
|
|
106
|
+
raw = JSON.parse(content);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
throw new Error(`Failed to parse webpack stats JSON at ${statsPath} (build "${buildId}"): ${error.message}`);
|
|
110
|
+
}
|
|
111
|
+
const rawModules = Array.isArray(raw.modules) ? raw.modules : [];
|
|
112
|
+
const rawChunks = Array.isArray(raw.chunks) ? raw.chunks : [];
|
|
113
|
+
const modules = rawModules
|
|
114
|
+
.map(module => normalizeModule(module))
|
|
115
|
+
.filter((module) => module !== null);
|
|
116
|
+
const chunks = rawChunks
|
|
117
|
+
.map(chunk => normalizeChunk(chunk))
|
|
118
|
+
.filter((chunk) => chunk !== null);
|
|
119
|
+
return {
|
|
120
|
+
buildId,
|
|
121
|
+
statsPath,
|
|
122
|
+
modules,
|
|
123
|
+
chunks,
|
|
124
|
+
moduleCount: rawModules.length,
|
|
125
|
+
parsedModuleCount: modules.length,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function buildModuleIndex(stats) {
|
|
129
|
+
const index = new Map();
|
|
130
|
+
for (const module of stats.modules) {
|
|
131
|
+
index.set(module.name, module);
|
|
132
|
+
}
|
|
133
|
+
return index;
|
|
134
|
+
}
|
|
135
|
+
function chunkFilesForModule(module, chunkById) {
|
|
136
|
+
const files = new Set();
|
|
137
|
+
for (const chunkId of module.chunkIds) {
|
|
138
|
+
const chunk = chunkById.get(chunkId);
|
|
139
|
+
for (const file of chunk?.files ?? []) {
|
|
140
|
+
files.add(file);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return Array.from(files).sort();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Walk a module's `reasons` upward to the nearest entry to explain why it is bundled. Picks the
|
|
147
|
+
* first resolvable parent at each step, guards against cycles, and caps depth so a pathological
|
|
148
|
+
* graph cannot loop. Returns the chain ordered from entry → target.
|
|
149
|
+
*/
|
|
150
|
+
function buildImportChain(target, moduleIndex) {
|
|
151
|
+
const chain = [
|
|
152
|
+
{ moduleName: target.name, packageName: target.packageName },
|
|
153
|
+
];
|
|
154
|
+
const visited = new Set([target.name]);
|
|
155
|
+
let current = target;
|
|
156
|
+
const maxDepth = 25;
|
|
157
|
+
for (let depth = 0; depth < maxDepth; depth += 1) {
|
|
158
|
+
const parentReason = current.reasons.find(reason => reason.moduleName !== null && !visited.has(reason.moduleName));
|
|
159
|
+
if (!parentReason || parentReason.moduleName === null) {
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
const parent = moduleIndex.get(parentReason.moduleName);
|
|
163
|
+
visited.add(parentReason.moduleName);
|
|
164
|
+
chain.push({
|
|
165
|
+
moduleName: parentReason.moduleName,
|
|
166
|
+
packageName: parent?.packageName ?? extractPackageName(parentReason.moduleName),
|
|
167
|
+
});
|
|
168
|
+
if (!parent || parent.reasons.length === 0) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
current = parent;
|
|
172
|
+
}
|
|
173
|
+
return chain.reverse();
|
|
174
|
+
}
|
|
175
|
+
export function traceImport(stats, moduleName, limit = 10) {
|
|
176
|
+
const query = moduleName.toLowerCase();
|
|
177
|
+
const moduleIndex = buildModuleIndex(stats);
|
|
178
|
+
const chunkById = new Map(stats.chunks.map(chunk => [chunk.id, chunk]));
|
|
179
|
+
const matches = stats.modules
|
|
180
|
+
.filter(module => module.name.toLowerCase().includes(query))
|
|
181
|
+
.sort((left, right) => right.sizeBytes - left.sizeBytes);
|
|
182
|
+
const traces = matches.slice(0, limit).map(module => ({
|
|
183
|
+
moduleName: module.name,
|
|
184
|
+
packageName: module.packageName,
|
|
185
|
+
sizeBytes: module.sizeBytes,
|
|
186
|
+
chunkFiles: chunkFilesForModule(module, chunkById),
|
|
187
|
+
importChain: buildImportChain(module, moduleIndex),
|
|
188
|
+
}));
|
|
189
|
+
return {
|
|
190
|
+
query: moduleName,
|
|
191
|
+
matchCount: matches.length,
|
|
192
|
+
traces,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=webpack-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webpack-stats.js","sourceRoot":"","sources":["../../src/parser/webpack-stats.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { ParsedBuildStats } from './parser/types.js';
|
|
1
|
+
import type { ParsedBuildStats, ParsedWebpackStats } from './parser/types.js';
|
|
2
2
|
export declare function storeBuildStats(build: ParsedBuildStats): void;
|
|
3
3
|
export declare function getBuildStats(id: string): ParsedBuildStats | undefined;
|
|
4
|
+
export declare function storeWebpackStats(stats: ParsedWebpackStats): void;
|
|
5
|
+
export declare function getWebpackStats(buildId: string): ParsedWebpackStats | undefined;
|
|
4
6
|
export declare function listBuildStats(): Array<{
|
|
5
7
|
id: string;
|
|
6
8
|
buildDir: string;
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAK9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAE7D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEtE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAEjE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE/E;AAED,wBAAgB,cAAc,IAAI,KAAK,CAAC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC,CASD"}
|
package/dist/store.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
const builds = new Map();
|
|
2
|
+
const webpackStats = new Map();
|
|
2
3
|
export function storeBuildStats(build) {
|
|
3
4
|
builds.set(build.id, build);
|
|
4
5
|
}
|
|
5
6
|
export function getBuildStats(id) {
|
|
6
7
|
return builds.get(id);
|
|
7
8
|
}
|
|
9
|
+
export function storeWebpackStats(stats) {
|
|
10
|
+
webpackStats.set(stats.buildId, stats);
|
|
11
|
+
}
|
|
12
|
+
export function getWebpackStats(buildId) {
|
|
13
|
+
return webpackStats.get(buildId);
|
|
14
|
+
}
|
|
8
15
|
export function listBuildStats() {
|
|
9
16
|
return Array.from(builds.values()).map(build => ({
|
|
10
17
|
id: build.id,
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;AACnD,MAAM,YAAY,GAAG,IAAI,GAAG,EAA8B,CAAC;AAE3D,MAAM,UAAU,eAAe,CAAC,KAAuB;IACrD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAyB;IACzD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,cAAc;IAQ5B,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/C,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;QAC/B,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;QAC/B,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain-growth.d.ts","sourceRoot":"","sources":["../../src/tools/explain-growth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+F7D"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatBytes, formatPct } from '../format.js';
|
|
3
|
+
import { explainGrowth as explainBuildGrowth } from '../parser/analysis.js';
|
|
4
|
+
import { getBuildStats, listBuildStats } from '../store.js';
|
|
5
|
+
function formatNullableRatio(value) {
|
|
6
|
+
return value === null ? null : formatPct(value);
|
|
7
|
+
}
|
|
8
|
+
export function registerExplainGrowth(server) {
|
|
9
|
+
server.registerTool('explain_growth', {
|
|
10
|
+
title: 'Explain Growth',
|
|
11
|
+
description: 'Identify which routes and chunks are responsible for bundle size growth between two loaded builds. ' +
|
|
12
|
+
'Returns severity-ranked route findings, the top growing chunks, and an overall regression summary.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
baselineBuildId: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Baseline build ID from load_build_stats. Omit to list loaded builds.'),
|
|
18
|
+
currentBuildId: z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Current build ID from load_build_stats. Omit to list loaded builds.'),
|
|
22
|
+
limit: z
|
|
23
|
+
.number()
|
|
24
|
+
.int()
|
|
25
|
+
.positive()
|
|
26
|
+
.max(25)
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Max route findings and top growing chunks to return. Defaults to 10.'),
|
|
29
|
+
},
|
|
30
|
+
}, async ({ baselineBuildId, currentBuildId, limit }) => {
|
|
31
|
+
if (!baselineBuildId || !currentBuildId) {
|
|
32
|
+
return {
|
|
33
|
+
content: [{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify({
|
|
36
|
+
builds: listBuildStats().map(build => ({
|
|
37
|
+
id: build.id,
|
|
38
|
+
buildDir: build.buildDir,
|
|
39
|
+
routeCount: build.routeCount,
|
|
40
|
+
totalChunkBytes: build.totalChunkBytes,
|
|
41
|
+
totalChunkBytesText: formatBytes(build.totalChunkBytes),
|
|
42
|
+
})),
|
|
43
|
+
}, null, 2),
|
|
44
|
+
}],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const baseline = getBuildStats(baselineBuildId);
|
|
48
|
+
if (!baseline) {
|
|
49
|
+
throw new Error(`Build "${baselineBuildId}" not found. Call explain_growth without IDs to list loaded builds.`);
|
|
50
|
+
}
|
|
51
|
+
const current = getBuildStats(currentBuildId);
|
|
52
|
+
if (!current) {
|
|
53
|
+
throw new Error(`Build "${currentBuildId}" not found. Call explain_growth without IDs to list loaded builds.`);
|
|
54
|
+
}
|
|
55
|
+
const explanation = explainBuildGrowth(baseline, current, limit ?? 10);
|
|
56
|
+
const hasGrowth = explanation.routeFindings.length > 0 || explanation.topGrowingChunks.length > 0;
|
|
57
|
+
const nextStep = hasGrowth
|
|
58
|
+
? `Inspect the largest contributors with get_shared_chunks and get_largest_routes on build '${current.id}', then apply the recommended actions.`
|
|
59
|
+
: `No bundle growth detected between '${baseline.id}' and '${current.id}'.`;
|
|
60
|
+
return {
|
|
61
|
+
content: [{
|
|
62
|
+
type: 'text',
|
|
63
|
+
text: JSON.stringify({
|
|
64
|
+
...explanation,
|
|
65
|
+
overall: {
|
|
66
|
+
...explanation.overall,
|
|
67
|
+
totalDeltaBytesText: formatBytes(Math.abs(explanation.overall.totalDeltaBytes)),
|
|
68
|
+
totalDeltaDirection: explanation.overall.totalDeltaBytes >= 0 ? 'growth' : 'shrink',
|
|
69
|
+
totalDeltaRatioText: formatNullableRatio(explanation.overall.totalDeltaRatio),
|
|
70
|
+
},
|
|
71
|
+
routeFindings: explanation.routeFindings.map(finding => ({
|
|
72
|
+
...finding,
|
|
73
|
+
deltaBytesText: formatBytes(finding.deltaBytes),
|
|
74
|
+
deltaRatioText: formatNullableRatio(finding.deltaRatio),
|
|
75
|
+
topContributingChunks: finding.topContributingChunks.map(chunk => ({
|
|
76
|
+
...chunk,
|
|
77
|
+
deltaBytesText: formatBytes(chunk.deltaBytes),
|
|
78
|
+
deltaRatioText: formatNullableRatio(chunk.deltaRatio),
|
|
79
|
+
})),
|
|
80
|
+
})),
|
|
81
|
+
topGrowingChunks: explanation.topGrowingChunks.map(chunk => ({
|
|
82
|
+
...chunk,
|
|
83
|
+
deltaBytesText: formatBytes(chunk.deltaBytes),
|
|
84
|
+
deltaRatioText: formatNullableRatio(chunk.deltaRatio),
|
|
85
|
+
})),
|
|
86
|
+
suggestions: explanation.suggestions.map(suggestion => ({
|
|
87
|
+
...suggestion,
|
|
88
|
+
deltaBytesText: formatBytes(suggestion.deltaBytes),
|
|
89
|
+
deltaRatioText: formatNullableRatio(suggestion.deltaRatio),
|
|
90
|
+
})),
|
|
91
|
+
nextStep,
|
|
92
|
+
}, null, 2),
|
|
93
|
+
}],
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=explain-growth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain-growth.js","sourceRoot":"","sources":["../../src/tools/explain-growth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"how-to-collect-stats.d.ts","sourceRoot":"","sources":["../../src/tools/how-to-collect-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4HpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8BjE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const NEXT_CONFIG_WEBPACK_SNIPPET = `// next.config.ts — write .next/stats.json only when ANALYZE=true
|
|
3
|
+
import type { NextConfig } from "next";
|
|
4
|
+
import { StatsWriterPlugin } from "webpack-stats-plugin";
|
|
5
|
+
|
|
6
|
+
const nextConfig: NextConfig = {
|
|
7
|
+
webpack(config) {
|
|
8
|
+
if (process.env.ANALYZE === "true") {
|
|
9
|
+
config.plugins.push(
|
|
10
|
+
new StatsWriterPlugin({
|
|
11
|
+
filename: "stats.json", // -> .next/stats.json
|
|
12
|
+
// ids + large modulesSpace are required: without them webpack drops
|
|
13
|
+
// chunk ids and collapses the module list, yielding empty attribution.
|
|
14
|
+
stats: {
|
|
15
|
+
all: false,
|
|
16
|
+
modules: true,
|
|
17
|
+
chunks: true,
|
|
18
|
+
chunkModules: true,
|
|
19
|
+
reasons: true,
|
|
20
|
+
ids: true,
|
|
21
|
+
nestedModules: true,
|
|
22
|
+
modulesSpace: Infinity,
|
|
23
|
+
chunkModulesSpace: Infinity,
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return config;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default nextConfig;`;
|
|
33
|
+
function buildManualResponse(scenario) {
|
|
34
|
+
if (scenario === 'turbopack') {
|
|
35
|
+
return turbopackResponse();
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
method: 'manual',
|
|
39
|
+
steps: [
|
|
40
|
+
{
|
|
41
|
+
step: 1,
|
|
42
|
+
title: 'Add the dev dependency',
|
|
43
|
+
command: 'npm install --save-dev webpack-stats-plugin',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
step: 2,
|
|
47
|
+
title: 'Emit stats behind an ANALYZE flag in next.config',
|
|
48
|
+
snippet: NEXT_CONFIG_WEBPACK_SNIPPET,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
step: 3,
|
|
52
|
+
title: 'Build with the flag set',
|
|
53
|
+
command: 'ANALYZE=true next build',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
producesFile: '.next/stats.json',
|
|
57
|
+
nextStep: 'Once .next/stats.json exists, call load_webpack_stats with the same buildDir and the buildId from load_build_stats.',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function buildAutomaticResponse(scenario) {
|
|
61
|
+
if (scenario === 'turbopack') {
|
|
62
|
+
return turbopackResponse();
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
method: 'automatic',
|
|
66
|
+
actions: [
|
|
67
|
+
{
|
|
68
|
+
action: 'add-dev-dependency',
|
|
69
|
+
run: 'npm install --save-dev webpack-stats-plugin',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
action: 'edit-next-config',
|
|
73
|
+
description: 'Add a webpack hook gated behind ANALYZE=true that writes .next/stats.json.',
|
|
74
|
+
snippet: NEXT_CONFIG_WEBPACK_SNIPPET,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
action: 'add-package-script',
|
|
78
|
+
script: { analyze: 'ANALYZE=true next build' },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
action: 'run-build',
|
|
82
|
+
run: 'npm run analyze',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
action: 'verify-output',
|
|
86
|
+
description: 'Confirm .next/stats.json exists before loading it.',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
producesFile: '.next/stats.json',
|
|
90
|
+
nextStep: 'After .next/stats.json exists, call load_webpack_stats with the same buildDir and the buildId from load_build_stats.',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function turbopackResponse() {
|
|
94
|
+
return {
|
|
95
|
+
method: 'unavailable',
|
|
96
|
+
scenario: 'turbopack',
|
|
97
|
+
summary: 'Turbopack has no webpack module graph, so .next/stats.json cannot be produced and trace_import cannot run.',
|
|
98
|
+
guidance: 'Run a one-off webpack build (omit --turbopack) with the stats hook to use trace_import, or stay on the manifest-only tools.',
|
|
99
|
+
manifestOnlyTools: [
|
|
100
|
+
'load_build_stats',
|
|
101
|
+
'get_largest_routes',
|
|
102
|
+
'get_shared_chunks',
|
|
103
|
+
'compare_builds',
|
|
104
|
+
'explain_growth',
|
|
105
|
+
],
|
|
106
|
+
nextStep: 'Rebuild with webpack to produce .next/stats.json, or continue with get_largest_routes / get_shared_chunks on the build you already loaded.',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export function registerHowToCollectStats(server) {
|
|
110
|
+
server.registerTool('how_to_collect_stats', {
|
|
111
|
+
title: 'How To Collect Webpack Stats',
|
|
112
|
+
description: 'Explain how to generate the webpack stats file (.next/stats.json) required by the bundle attribution ' +
|
|
113
|
+
'tools. Choose manual (a recipe you apply yourself) or automatic (an action plan Copilot executes).',
|
|
114
|
+
inputSchema: {
|
|
115
|
+
method: z
|
|
116
|
+
.enum(['manual', 'automatic'])
|
|
117
|
+
.describe('manual: return a recipe to apply yourself. automatic: return an action plan for Copilot to execute.'),
|
|
118
|
+
scenario: z
|
|
119
|
+
.enum(['webpack', 'turbopack'])
|
|
120
|
+
.optional()
|
|
121
|
+
.describe('Collection context. Defaults to webpack. Use turbopack if the app builds with --turbopack.'),
|
|
122
|
+
},
|
|
123
|
+
}, async ({ method, scenario }) => {
|
|
124
|
+
const resolvedScenario = scenario ?? 'webpack';
|
|
125
|
+
const resolvedMethod = method;
|
|
126
|
+
const payload = resolvedMethod === 'manual'
|
|
127
|
+
? buildManualResponse(resolvedScenario)
|
|
128
|
+
: buildAutomaticResponse(resolvedScenario);
|
|
129
|
+
return {
|
|
130
|
+
content: [{
|
|
131
|
+
type: 'text',
|
|
132
|
+
text: JSON.stringify(payload, null, 2),
|
|
133
|
+
}],
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=how-to-collect-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"how-to-collect-stats.js","sourceRoot":"","sources":["../../src/tools/how-to-collect-stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-webpack-stats.d.ts","sourceRoot":"","sources":["../../src/tools/load-webpack-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOpE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+ChE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { parseWebpackStats } from '../parser/webpack-stats.js';
|
|
3
|
+
import { getBuildStats, storeWebpackStats } from '../store.js';
|
|
4
|
+
import { statsTextResult } from './webpack-shared.js';
|
|
5
|
+
export function registerLoadWebpackStats(server) {
|
|
6
|
+
server.registerTool('load_webpack_stats', {
|
|
7
|
+
title: 'Load Webpack Stats',
|
|
8
|
+
description: 'Parse the webpack module stats file (.next/stats.json) and link it to a build loaded with ' +
|
|
9
|
+
'load_build_stats. Required before trace_import.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
buildId: z.string().describe('Build ID returned by load_build_stats; the stats.json is read from that build directory'),
|
|
12
|
+
},
|
|
13
|
+
}, async ({ buildId }) => {
|
|
14
|
+
const build = getBuildStats(buildId);
|
|
15
|
+
if (!build) {
|
|
16
|
+
throw new Error(`Build "${buildId}" not found. Call load_build_stats first and pass the buildId it returns.`);
|
|
17
|
+
}
|
|
18
|
+
const stats = await parseWebpackStats(build.buildDir, buildId);
|
|
19
|
+
if (!stats) {
|
|
20
|
+
return statsTextResult({
|
|
21
|
+
buildId,
|
|
22
|
+
webpackStatsLoaded: false,
|
|
23
|
+
message: `No stats.json found in ${build.buildDir}. A stock next build does not emit one.`,
|
|
24
|
+
nextStep: 'Call how_to_collect_stats to generate .next/stats.json, then load_webpack_stats again.',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
storeWebpackStats(stats);
|
|
28
|
+
const looksCollapsed = stats.parsedModuleCount === 0 || stats.chunks.length === 0;
|
|
29
|
+
return statsTextResult({
|
|
30
|
+
buildId,
|
|
31
|
+
webpackStatsLoaded: true,
|
|
32
|
+
statsPath: stats.statsPath,
|
|
33
|
+
moduleCount: stats.moduleCount,
|
|
34
|
+
parsedModuleCount: stats.parsedModuleCount,
|
|
35
|
+
chunkCount: stats.chunks.length,
|
|
36
|
+
...(looksCollapsed
|
|
37
|
+
? {
|
|
38
|
+
warning: 'The stats file parsed but contains no usable modules/chunks. This usually means the stats ' +
|
|
39
|
+
'config collapsed the module graph (webpack groups modules once `modulesSpace` is exceeded and ' +
|
|
40
|
+
'omits chunk ids unless `ids: true`). Re-run how_to_collect_stats for the corrected config and rebuild.',
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
43
|
+
nextStep: looksCollapsed
|
|
44
|
+
? 'Call how_to_collect_stats again, apply the corrected stats config, rebuild, then load_webpack_stats.'
|
|
45
|
+
: 'Now call trace_import with a module or package name (e.g. a heavy dependency) to see why it is bundled.',
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=load-webpack-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-webpack-stats.js","sourceRoot":"","sources":["../../src/tools/load-webpack-stats.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CAAC,oBAAoB,EAAE;QACxC,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,4FAA4F;YAC5F,iDAAiD;QACnD,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yFAAyF,CAAC;SACxH;KACF,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,UAAU,OAAO,2EAA2E,CAAC,CAAC;QAChH,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,eAAe,CAAC;gBACrB,OAAO;gBACP,kBAAkB,EAAE,KAAK;gBACzB,OAAO,EAAE,0BAA0B,KAAK,CAAC,QAAQ,yCAAyC;gBAC1F,QAAQ,EAAE,wFAAwF;aACnG,CAAC,CAAC;QACL,CAAC;QAED,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACzB,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;QAClF,OAAO,eAAe,CAAC;YACrB,OAAO;YACP,kBAAkB,EAAE,IAAI;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;YAC1C,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;YAC/B,GAAG,CAAC,cAAc;gBAChB,CAAC,CAAC;oBACE,OAAO,EACL,4FAA4F;wBAC5F,gGAAgG;wBAChG,wGAAwG;iBAC3G;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,QAAQ,EAAE,cAAc;gBACtB,CAAC,CAAC,sGAAsG;gBACxG,CAAC,CAAC,yGAAyG;SAC9G,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-import.d.ts","sourceRoot":"","sources":["../../src/tools/trace-import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAqC3D"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatBytes } from '../format.js';
|
|
3
|
+
import { traceImport } from '../parser/webpack-stats.js';
|
|
4
|
+
import { getBuildStats } from '../store.js';
|
|
5
|
+
import { resolveWebpackStats, statsTextResult } from './webpack-shared.js';
|
|
6
|
+
export function registerTraceImport(server) {
|
|
7
|
+
server.registerTool('trace_import', {
|
|
8
|
+
title: 'Trace Import',
|
|
9
|
+
description: 'Explain why a module is bundled by tracing its import chain from an entry point to the module. ' +
|
|
10
|
+
'Requires load_webpack_stats first.',
|
|
11
|
+
inputSchema: {
|
|
12
|
+
buildId: z.string().describe('Build ID returned by load_build_stats'),
|
|
13
|
+
moduleName: z.string().describe('Module or package name to search for (case-insensitive substring), e.g. "lodash"'),
|
|
14
|
+
limit: z.number().int().positive().max(25).optional().describe('Maximum matching modules to trace. Defaults to 10.'),
|
|
15
|
+
},
|
|
16
|
+
}, async ({ buildId, moduleName, limit }) => {
|
|
17
|
+
const build = getBuildStats(buildId);
|
|
18
|
+
if (!build) {
|
|
19
|
+
throw new Error(`Build "${buildId}" not found. Call load_build_stats first.`);
|
|
20
|
+
}
|
|
21
|
+
const resolved = resolveWebpackStats(buildId);
|
|
22
|
+
if ('breadcrumb' in resolved) {
|
|
23
|
+
return statsTextResult(resolved.breadcrumb);
|
|
24
|
+
}
|
|
25
|
+
const result = traceImport(resolved.stats, moduleName, limit ?? 10);
|
|
26
|
+
return statsTextResult({
|
|
27
|
+
buildId,
|
|
28
|
+
query: result.query,
|
|
29
|
+
matchCount: result.matchCount,
|
|
30
|
+
traces: result.traces.map(trace => ({
|
|
31
|
+
moduleName: trace.moduleName,
|
|
32
|
+
packageName: trace.packageName,
|
|
33
|
+
sizeBytes: trace.sizeBytes,
|
|
34
|
+
sizeBytesText: formatBytes(trace.sizeBytes),
|
|
35
|
+
chunkFiles: trace.chunkFiles,
|
|
36
|
+
importChain: trace.importChain,
|
|
37
|
+
})),
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=trace-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-import.js","sourceRoot":"","sources":["../../src/tools/trace-import.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3E,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,CAAC,YAAY,CAAC,cAAc,EAAE;QAClC,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,iGAAiG;YACjG,oCAAoC;QACtC,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;YACrE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kFAAkF,CAAC;YACnH,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC;SACrH;KACF,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE;QAC1C,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,UAAU,OAAO,2CAA2C,CAAC,CAAC;QAChF,CAAC;QAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;YAC7B,OAAO,eAAe,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC9C,CAAC;QAED,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QACpE,OAAO,eAAe,CAAC;YACrB,OAAO;YACP,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAClC,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,aAAa,EAAE,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC3C,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC,CAAC;SACJ,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|