+
@@ -23,6 +31,21 @@ defineProps<{
+
+
+
+
+
+ {{ summary.label }}
+
+
+ {{ summary.value }}
+
+
+
diff --git a/packages/rolldown/src/app/components/compare/SingleSideRow.vue b/packages/rolldown/src/app/components/compare/SingleSideRow.vue
new file mode 100644
index 00000000..1580291e
--- /dev/null
+++ b/packages/rolldown/src/app/components/compare/SingleSideRow.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+ {{ sessionLabel }}
+
+
+ {{ title }}
+ ({{ titleMeta }})
+
+
+ {{ subtitle }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/rolldown/src/app/components/compare/SplitRow.vue b/packages/rolldown/src/app/components/compare/SplitRow.vue
new file mode 100644
index 00000000..2cbcfaff
--- /dev/null
+++ b/packages/rolldown/src/app/components/compare/SplitRow.vue
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+ {{ ratioText }}
+
+
+
+
+
+
+
+
+
+
+ Session A
+
+
+ {{ previousTitle }}
+ ({{ previousTitleMeta }})
+
+
+ {{ previousSubtitle }}
+
+
+
+
+
+ Not present
+
+
+
+
+
+
+
+
+ Session B
+
+
+ {{ currentTitle }}
+ ({{ currentTitleMeta }})
+
+
+ {{ currentSubtitle }}
+
+
+
+
+
+ Not present
+
+
+
+
+
diff --git a/packages/rolldown/src/app/components/compare/StatsStrip.vue b/packages/rolldown/src/app/components/compare/StatsStrip.vue
new file mode 100644
index 00000000..d470c47c
--- /dev/null
+++ b/packages/rolldown/src/app/components/compare/StatsStrip.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+ ·
+
+
+ {{ stat.value }}
+
+ {{ stat.label }}
+
+
+
+
diff --git a/packages/rolldown/src/app/components/compare/TabView.vue b/packages/rolldown/src/app/components/compare/TabView.vue
new file mode 100644
index 00000000..b8fbe034
--- /dev/null
+++ b/packages/rolldown/src/app/components/compare/TabView.vue
@@ -0,0 +1,679 @@
+
+
+
+
+
+
+ {{ activeTabMeta?.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ activeDiffEmptyText }}
+
+
+
+
+
+
diff --git a/packages/rolldown/src/app/components/display/ComparisonMetric.vue b/packages/rolldown/src/app/components/display/ComparisonMetric.vue
index e67bd0d7..eb49059d 100644
--- a/packages/rolldown/src/app/components/display/ComparisonMetric.vue
+++ b/packages/rolldown/src/app/components/display/ComparisonMetric.vue
@@ -16,7 +16,7 @@ const normalizedPercent = computed(() => {
return Math.abs((props.current - props.previous) / props.previous * 100).toFixed(2)
})
const trendSymbol = computed(() => isNotChanged.value ? '' : (props.current > props.previous ? '+' : '-'))
-const comparisonColorClass = computed(() => isNotChanged.value ? 'text-gray-500' : (props.current > props.previous ? 'text-green-500' : 'text-red-500'))
+const comparisonColorClass = computed(() => isNotChanged.value ? 'text-gray-500' : (props.current > props.previous ? 'text-red-500' : 'text-green-500'))
diff --git a/packages/rolldown/src/app/pages/compare/[sessions].vue b/packages/rolldown/src/app/pages/compare/[sessions].vue
new file mode 100644
index 00000000..6fb6c58d
--- /dev/null
+++ b/packages/rolldown/src/app/pages/compare/[sessions].vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+ Re-select Session
+
+
+
+
+
+
+
diff --git a/packages/rolldown/src/app/pages/compare/[sessions]/assets.vue b/packages/rolldown/src/app/pages/compare/[sessions]/assets.vue
new file mode 100644
index 00000000..962c389f
--- /dev/null
+++ b/packages/rolldown/src/app/pages/compare/[sessions]/assets.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/packages/rolldown/src/app/pages/compare/[sessions]/chunks.vue b/packages/rolldown/src/app/pages/compare/[sessions]/chunks.vue
new file mode 100644
index 00000000..4963e51a
--- /dev/null
+++ b/packages/rolldown/src/app/pages/compare/[sessions]/chunks.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/packages/rolldown/src/app/pages/compare/[sessions]/index.vue b/packages/rolldown/src/app/pages/compare/[sessions]/index.vue
index 0175ca3a..9a39741e 100644
--- a/packages/rolldown/src/app/pages/compare/[sessions]/index.vue
+++ b/packages/rolldown/src/app/pages/compare/[sessions]/index.vue
@@ -1,140 +1,28 @@
-
-
-
-
-
- Re-select Session
-
-
-
-
- Compare Overview
-
-
-
-
-
-
+
diff --git a/packages/rolldown/src/app/pages/compare/[sessions]/packages.vue b/packages/rolldown/src/app/pages/compare/[sessions]/packages.vue
new file mode 100644
index 00000000..79b56646
--- /dev/null
+++ b/packages/rolldown/src/app/pages/compare/[sessions]/packages.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/packages/rolldown/src/app/pages/compare/[sessions]/plugins.vue b/packages/rolldown/src/app/pages/compare/[sessions]/plugins.vue
new file mode 100644
index 00000000..0da81ed5
--- /dev/null
+++ b/packages/rolldown/src/app/pages/compare/[sessions]/plugins.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/packages/rolldown/src/node/rolldown/events-reader.ts b/packages/rolldown/src/node/rolldown/events-reader.ts
index 151920e6..66a41ea4 100644
--- a/packages/rolldown/src/node/rolldown/events-reader.ts
+++ b/packages/rolldown/src/node/rolldown/events-reader.ts
@@ -1,6 +1,6 @@
import type { Event, HookLoadCallEnd, HookTransformCallEnd, HookTransformCallStart, SessionMeta } from '@rolldown/debug'
import type { ModuleBuildMetrics, PluginBuildMetrics, RolldownModuleLoadInfo, RolldownModuleTransformInfo, RolldownResolveInfo } from '../../shared/types'
-import type { IndexedHookCall, LineLocation, ModuleEventIndex, PendingHookCallStart, RolldownLogCacheState } from './log-cache'
+import type { IndexedHookCall, LineLocation, ModuleEventIndex, PendingHookCallStart, PluginBuildMetricsSummary, RolldownLogCacheState } from './log-cache'
import { Buffer } from 'node:buffer'
import { createHash } from 'node:crypto'
import fs from 'node:fs'
@@ -172,6 +172,26 @@ function estimateContentByteSizeFromLine(preview: Buffer, location: LineLocation
return Math.max(0, location.length - contentStart - 128)
}
+function summarizePluginCalls(calls: PluginBuildMetrics['calls']): Pick {
+ const summary: Pick = {
+ total: { count: 0, duration: 0 },
+ resolve: { count: 0, duration: 0 },
+ load: { count: 0, duration: 0 },
+ transform: { count: 0, duration: 0 },
+ }
+
+ for (const call of calls) {
+ const metric = summary[call.type]
+
+ summary.total.count += 1
+ summary.total.duration += call.duration
+ metric.count += 1
+ metric.duration += call.duration
+ }
+
+ return summary
+}
+
function pruneReaders() {
if (readers.size <= MAX_READERS)
return
@@ -532,6 +552,27 @@ export class RolldownEventsReader {
return this.pendingPackageSummaryRead
}
+ async readPluginBuildMetricsSummary(): Promise