@0x1f320.sh/why-did-you-render-mcp 1.0.0-dev.14 → 1.0.0-dev.16

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.
@@ -228,6 +228,40 @@ var RenderStore = class {
228
228
  for (const f of this.jsonlFiles()) unlinkSync(join(this.dir, f));
229
229
  }
230
230
  }
231
+ clearRendersByComponent(component, projectId) {
232
+ this.flush(projectId);
233
+ const files = projectId ? this.projectFiles(projectId) : this.jsonlFiles();
234
+ let removed = 0;
235
+ for (const f of files) {
236
+ const filePath = join(this.dir, f);
237
+ const renders = readJsonl(filePath);
238
+ const before = renders.length;
239
+ const remaining = renders.filter((r) => r.displayName !== component);
240
+ removed += before - remaining.length;
241
+ if (remaining.length === 0) {
242
+ unlinkSync(filePath);
243
+ this.clearBuffersForFile(f);
244
+ } else if (remaining.length < before) {
245
+ this.rewriteFile(filePath, remaining);
246
+ this.clearBuffersForFile(f);
247
+ }
248
+ }
249
+ return removed;
250
+ }
251
+ clearRendersByCommit(beforeCommit, projectId) {
252
+ this.flush(projectId);
253
+ const files = projectId ? this.projectFiles(projectId) : this.jsonlFiles();
254
+ let deleted = 0;
255
+ for (const f of files) {
256
+ const parsed = this.parseFilename(f);
257
+ if (parsed?.commitId != null && parsed.commitId < beforeCommit) {
258
+ unlinkSync(join(this.dir, f));
259
+ this.clearBuffersForFile(f);
260
+ deleted++;
261
+ }
262
+ }
263
+ return deleted;
264
+ }
231
265
  getProjects() {
232
266
  this.flush();
233
267
  const projects = /* @__PURE__ */ new Set();
@@ -273,7 +307,19 @@ var RenderStore = class {
273
307
  for (const r of renders) {
274
308
  summary[r.project] ??= {};
275
309
  const project = summary[r.project];
276
- project[r.displayName] = (project[r.displayName] ?? 0) + 1;
310
+ project[r.displayName] ??= {
311
+ count: 0,
312
+ reasons: {
313
+ props: 0,
314
+ state: 0,
315
+ hooks: 0
316
+ }
317
+ };
318
+ const entry = project[r.displayName];
319
+ entry.count++;
320
+ if (Array.isArray(r.reason.propsDifferences)) entry.reasons.props++;
321
+ if (Array.isArray(r.reason.stateDifferences)) entry.reasons.state++;
322
+ if (Array.isArray(r.reason.hookDifferences)) entry.reasons.hooks++;
277
323
  }
278
324
  return summary;
279
325
  }
@@ -285,7 +331,19 @@ var RenderStore = class {
285
331
  summary[r.project] ??= {};
286
332
  summary[r.project][r.commitId] ??= {};
287
333
  const commit = summary[r.project][r.commitId];
288
- commit[r.displayName] = (commit[r.displayName] ?? 0) + 1;
334
+ commit[r.displayName] ??= {
335
+ count: 0,
336
+ reasons: {
337
+ props: 0,
338
+ state: 0,
339
+ hooks: 0
340
+ }
341
+ };
342
+ const entry = commit[r.displayName];
343
+ entry.count++;
344
+ if (Array.isArray(r.reason.propsDifferences)) entry.reasons.props++;
345
+ if (Array.isArray(r.reason.stateDifferences)) entry.reasons.state++;
346
+ if (Array.isArray(r.reason.hookDifferences)) entry.reasons.hooks++;
289
347
  }
290
348
  return summary;
291
349
  }
@@ -304,6 +362,25 @@ var RenderStore = class {
304
362
  }
305
363
  return result;
306
364
  }
365
+ rewriteFile(filePath, renders) {
366
+ writeFileSync(filePath, `${renders.map((r) => JSON.stringify(r)).join("\n")}\n`);
367
+ }
368
+ clearBuffersForFile(filename) {
369
+ const parsed = this.parseFilename(filename);
370
+ if (!parsed) return;
371
+ for (const [bk, meta] of this.bufferMeta) {
372
+ if (sanitizeProjectId(meta.projectId) !== parsed.projectSanitized) continue;
373
+ if (!(parsed.commitId != null ? meta.commitId === parsed.commitId : meta.commitId == null)) continue;
374
+ this.buffers.delete(bk);
375
+ this.dicts.delete(bk);
376
+ this.bufferMeta.delete(bk);
377
+ const timer = this.timers.get(bk);
378
+ if (timer) {
379
+ clearTimeout(timer);
380
+ this.timers.delete(bk);
381
+ }
382
+ }
383
+ }
307
384
  bufferKey(projectId, commitId) {
308
385
  return `${projectId}\0${commitId ?? NOCOMMIT}`;
309
386
  }
@@ -374,11 +451,17 @@ function textResult(text) {
374
451
  function register$6(server) {
375
452
  server.registerTool("clear_renders", {
376
453
  title: "Clear Renders",
377
- description: "Clears collected render data. If multiple projects are active and no project is specified, the tool will ask you to disambiguate.",
378
- inputSchema: { project: z.string().optional().describe("Project identifier (the browser's origin URL, e.g. http://localhost:3000). Omit to auto-detect.") }
379
- }, async ({ project }) => {
454
+ description: "Clears collected render data. Supports filtering by component name or by commit ID threshold. When no filter is given, clears all data. If multiple projects are active and no project is specified, the tool will ask you to disambiguate.",
455
+ inputSchema: {
456
+ project: z.string().optional().describe("Project identifier (the browser's origin URL, e.g. http://localhost:3000). Omit to auto-detect."),
457
+ component: z.string().optional().describe("Clear only renders for this component (by displayName)."),
458
+ beforeCommit: z.number().optional().describe("Clear all renders from commits with an ID strictly less than this value.")
459
+ }
460
+ }, async ({ project, component, beforeCommit }) => {
380
461
  const resolved = resolveProject(project);
381
462
  if (resolved.error) return textResult(resolved.error);
463
+ if (component) return textResult(`Cleared ${store.clearRendersByComponent(component, resolved.projectId)} render(s) for component "${component}".`);
464
+ if (beforeCommit != null) return textResult(`Cleared renders from ${store.clearRendersByCommit(beforeCommit, resolved.projectId)} commit file(s) before commit #${beforeCommit}.`);
382
465
  store.clearRenders(resolved.projectId);
383
466
  return textResult(resolved.projectId ? `Render data cleared for ${resolved.projectId}.` : "All render data cleared.");
384
467
  });
@@ -428,13 +511,20 @@ function register$3(server) {
428
511
  return aggregateSummary(resolved.projectId);
429
512
  });
430
513
  }
514
+ function formatReasons(reasons) {
515
+ const parts = [];
516
+ if (reasons.props > 0) parts.push(`props: ${reasons.props}`);
517
+ if (reasons.state > 0) parts.push(`state: ${reasons.state}`);
518
+ if (reasons.hooks > 0) parts.push(`hooks: ${reasons.hooks}`);
519
+ return parts.length > 0 ? ` — ${parts.join(", ")}` : "";
520
+ }
431
521
  function aggregateSummary(projectId) {
432
522
  const summary = store.getSummary(projectId);
433
523
  if (Object.keys(summary).length === 0) return textResult("No renders recorded yet.");
434
524
  const lines = [];
435
525
  for (const [proj, components] of Object.entries(summary)) {
436
526
  lines.push(`[${proj}]`);
437
- for (const [name, count] of Object.entries(components)) lines.push(` ${name}: ${count} re-render(s)`);
527
+ for (const [name, { count, reasons }] of Object.entries(components)) lines.push(` ${name}: ${count} re-render(s)${formatReasons(reasons)}`);
438
528
  }
439
529
  return textResult(`Re-render summary:\n\n${lines.join("\n")}`);
440
530
  }
@@ -447,9 +537,9 @@ function commitSummary(projectId) {
447
537
  const sortedCommitIds = Object.keys(commits).map(Number).sort((a, b) => a - b);
448
538
  for (const commitId of sortedCommitIds) {
449
539
  const components = commits[commitId];
450
- const total = Object.values(components).reduce((s, c) => s + c, 0);
540
+ const total = Object.values(components).reduce((s, c) => s + c.count, 0);
451
541
  lines.push(` Commit #${commitId} (${total} re-render(s)):`);
452
- for (const [name, count] of Object.entries(components)) lines.push(` ${name}: ${count}`);
542
+ for (const [name, { count, reasons }] of Object.entries(components)) lines.push(` ${name}: ${count}${formatReasons(reasons)}`);
453
543
  }
454
544
  }
455
545
  return textResult(`Re-render summary (by commit):\n\n${lines.join("\n")}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0x1f320.sh/why-did-you-render-mcp",
3
- "version": "1.0.0-dev.14",
3
+ "version": "1.0.0-dev.16",
4
4
  "type": "module",
5
5
  "description": "MCP server that collects why-did-you-render data from browser and exposes it to coding agents",
6
6
  "license": "MIT",