From fdc2f79855bcc5d48ec39cedd8558cf5494f895c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 24 Jun 2026 21:11:19 +0900
Subject: [PATCH] chore(dev): tweak backend-memory-report
---
.github/scripts/backend-memory-report.mjs | 87 ++++++++++++++++++++++-
1 file changed, 86 insertions(+), 1 deletion(-)
diff --git a/.github/scripts/backend-memory-report.mjs b/.github/scripts/backend-memory-report.mjs
index 0d2ba26cba..88a352f30c 100644
--- a/.github/scripts/backend-memory-report.mjs
+++ b/.github/scripts/backend-memory-report.mjs
@@ -37,6 +37,28 @@ const heapSnapshotCategories = [
'Other non-JS objects',
];
+const heapSnapshotCategoriesColors = {
+ 'Total': 'gray',
+ 'Code': 'orange',
+ 'Strings': 'red',
+ 'JS arrays': 'cyan',
+ 'Typed arrays': 'green',
+ 'System objects': 'yellow',
+ 'Other JS objects': 'violet',
+ 'Other non-JS objects': 'pink',
+};
+
+const heapSnapshotCategoriesColorsHex = {
+ 'Total': '#888888',
+ 'Code': '#f28e2c',
+ 'Strings': '#e15759',
+ 'JS arrays': '#76b7b2',
+ 'Typed arrays': '#59a14f',
+ 'System objects': '#edc949',
+ 'Other JS objects': '#af7aa1',
+ 'Other non-JS objects': '#ff9da7',
+};
+
function formatNumber(value) {
return numberFormatter.format(value);
}
@@ -265,6 +287,60 @@ function getHeapSnapshotCategoryValue(report, phase, category) {
return Number.isFinite(value) ? value : null;
}
+function escapeCsvValue(value) {
+ return `"${String(value).replaceAll('"', '""')}"`;
+}
+
+function renderHeapSnapshotSankey(report, phase, title) {
+ const total = getHeapSnapshotCategoryValue(report, phase, 'Total');
+ if (total == null || total <= 0) return null;
+
+ const categories = heapSnapshotCategories
+ .filter(category => category !== 'Total')
+ .map(category => {
+ const value = getHeapSnapshotCategoryValue(report, phase, category);
+ if (value == null || value <= 0) return null;
+ return {
+ category,
+ value,
+ };
+ })
+ .filter(value => value != null);
+
+ if (categories.length === 0) return null;
+
+ const nodeColors = {
+ [title]: heapSnapshotCategoriesColorsHex.Total,
+ };
+ for (const { category } of categories) {
+ nodeColors[category] = heapSnapshotCategoriesColorsHex[category];
+ }
+
+ const lines = [
+ `${title} heap snapshot composition
`,
+ '',
+ '```mermaid',
+ `%%{init: ${JSON.stringify({
+ sankey: {
+ linkColor: 'target',
+ nodeAlignment: 'left',
+ nodeColors,
+ },
+ })}}%%`,
+ 'sankey',
+ ];
+
+ for (const { category, value } of categories) {
+ lines.push(`${escapeCsvValue(title)},${escapeCsvValue(category)},${value}`);
+ }
+
+ lines.push('```');
+ lines.push('');
+ lines.push(' ');
+
+ return lines.join('\n');
+}
+
function formatHeapSnapshotCategoryLabel(category, baseValue, headValue, baseTotal, headTotal) {
if (category === 'Total' || baseTotal == null || headTotal == null || baseTotal <= 0 || headTotal <= 0) return `**${category}**`;
@@ -347,7 +423,7 @@ function renderHeapSnapshotTable(base, head, phase) {
const deltaMedian = summary == null ? '-' : `${formatDeltaBytes(summary.median)}
${formatDeltaPercent(summary.median, baseValue)}`;
const categoryLabel = formatHeapSnapshotCategoryLabel(category, baseValue, headValue, baseTotal, headTotal);
- lines.push(`| ${categoryLabel} | ${formatBytes(baseValue)}
± ${baseSpread == null ? '-' : formatBytes(baseSpread)} | ${formatBytes(headValue)}
± ${headSpread == null ? '-' : formatBytes(headSpread)} | ${deltaMedian} | ${summary?.mad == null ? '-' : formatBytes(summary.mad)} | ${summary == null ? '-' : formatDeltaBytes(summary.min)} | ${summary == null ? '-' : formatDeltaBytes(summary.max)} |`);
+ lines.push(`| $\\color{${heapSnapshotCategoriesColors[category]}}{\\rule{8pt}{8pt}}$ ${categoryLabel} | ${formatBytes(baseValue)}
± ${baseSpread == null ? '-' : formatBytes(baseSpread)} | ${formatBytes(headValue)}
± ${headSpread == null ? '-' : formatBytes(headSpread)} | ${deltaMedian} | ${summary?.mad == null ? '-' : formatBytes(summary.mad)} | ${summary == null ? '-' : formatDeltaBytes(summary.min)} | ${summary == null ? '-' : formatDeltaBytes(summary.max)} |`);
if (category === 'Total') {
lines.push('| | | | | | | |');
}
@@ -368,6 +444,15 @@ function renderHeapSnapshotSection(base, head) {
'',
];
+ for (const graph of [
+ renderHeapSnapshotSankey(base, 'afterRequest', 'Base'),
+ renderHeapSnapshotSankey(head, 'afterRequest', 'Head'),
+ ]) {
+ if (graph == null) continue;
+ lines.push(graph);
+ lines.push('');
+ }
+
return lines.join('\n');
}