From 8ee16999013810f43e7661d755df4ec7718fda20 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Wed, 28 Oct 2026 09:37:13 +0100 Subject: [PATCH 1/2] feat(graphs): add changes over time graphs --- packages/docs/package.json | 2 + .../docs/src/components/BuildSizeChart.astro | 15 +- .../src/components/BuildSizeProdChart.astro | 15 +- .../docs/src/components/CodeComparison.astro | 4 +- .../src/components/ComparisonBarChart.astro | 10 +- .../components/ComparisonComposition.astro | 120 +++++ .../src/components/ComparisonLineChart.astro | 465 ++++++++++++++++++ .../CorePackageDependencyCharts.astro | 50 +- packages/docs/src/components/DepsChart.astro | 18 +- .../src/components/DevServerStartChart.astro | 18 +- .../components/DuplicateDependencyChart.astro | 18 +- .../FirstPartyDependencyCharts.astro | 69 ++- .../components/InstallTimeMedianChart.astro | 15 +- ...LighthouseClientSideRenderedFCPChart.astro | 18 +- .../LighthouseClientSideRenderedFPChart.astro | 18 +- ...LighthouseServerSideRenderedFCPChart.astro | 18 +- .../LighthouseServerSideRenderedFPChart.astro | 18 +- .../src/components/NodeModulesSizeChart.astro | 18 +- .../docs/src/components/ProdDepsChart.astro | 18 +- .../SSRLoadWorker100P75LatencyChart.astro | 17 +- .../SSRLoadWorker100P90LatencyChart.astro | 17 +- .../SSRLoadWorker100P99LatencyChart.astro | 17 +- .../SSRLoadWorker25P75LatencyChart.astro | 17 +- .../SSRLoadWorker25P90LatencyChart.astro | 17 +- .../SSRLoadWorker25P99LatencyChart.astro | 17 +- .../SSRLoadWorker50P75LatencyChart.astro | 17 +- .../SSRLoadWorker50P90LatencyChart.astro | 17 +- .../SSRLoadWorker50P99LatencyChart.astro | 17 +- .../SSRRequestThroughputBodySizeChart.astro | 18 +- .../SSRRequestThroughputLatencyChart.astro | 18 +- .../SSRRequestThroughputOpsChart.astro | 18 +- packages/docs/src/lib/chart-measurements.ts | 234 +++++++++ packages/docs/src/lib/chart.ts | 38 +- packages/docs/src/lib/framework-cards.ts | 4 +- packages/docs/src/lib/framework-logos.ts | 103 +++- packages/docs/src/lib/monthly-history.test.ts | 106 ++++ packages/docs/src/lib/monthly-history.ts | 104 ++++ packages/docs/src/lib/prepare-chart.ts | 4 +- packages/docs/src/lib/types.ts | 28 ++ pnpm-lock.yaml | 14 +- 40 files changed, 1555 insertions(+), 214 deletions(-) create mode 100644 packages/docs/src/components/ComparisonComposition.astro create mode 100644 packages/docs/src/components/ComparisonLineChart.astro create mode 100644 packages/docs/src/lib/monthly-history.test.ts create mode 100644 packages/docs/src/lib/monthly-history.ts diff --git a/packages/docs/package.json b/packages/docs/package.json index c1fe28bd0..62cdc7c85 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -18,10 +18,12 @@ "@tanstack/charts": "^1.1.0", "astro": "^7.4.0", "chart.js": "^4.5.1", + "d3-shape": "3.2.0", "@framework-tracker/results": "workspace:*" }, "devDependencies": { "@astrojs/check": "^0.9.10", + "@types/d3-shape": "3.2.0", "@typescript-eslint/parser": "8.62.1", "eslint": "10.6.0", "eslint-plugin-astro": "^2.1.1" diff --git a/packages/docs/src/components/BuildSizeChart.astro b/packages/docs/src/components/BuildSizeChart.astro index f5db0d2ad..a5537862c 100644 --- a/packages/docs/src/components/BuildSizeChart.astro +++ b/packages/docs/src/components/BuildSizeChart.astro @@ -1,9 +1,14 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareColdBuildTimeComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareColdBuildTimeComparison, + prepareColdBuildTimeMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareColdBuildTimeComparison(starterStats) +const bar = prepareColdBuildTimeComparison(starterStats) +const line = prepareColdBuildTimeMonthlyComparison(starterHistory, starterStats) --- - + diff --git a/packages/docs/src/components/BuildSizeProdChart.astro b/packages/docs/src/components/BuildSizeProdChart.astro index 933cdb1fa..fa8465e48 100644 --- a/packages/docs/src/components/BuildSizeProdChart.astro +++ b/packages/docs/src/components/BuildSizeProdChart.astro @@ -1,9 +1,14 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareWarmBuildTimeComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareWarmBuildTimeComparison, + prepareWarmBuildTimeMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareWarmBuildTimeComparison(starterStats) +const bar = prepareWarmBuildTimeComparison(starterStats) +const line = prepareWarmBuildTimeMonthlyComparison(starterHistory, starterStats) --- - + diff --git a/packages/docs/src/components/CodeComparison.astro b/packages/docs/src/components/CodeComparison.astro index 72c68ed83..137da26f0 100644 --- a/packages/docs/src/components/CodeComparison.astro +++ b/packages/docs/src/components/CodeComparison.astro @@ -1,7 +1,7 @@ --- import { render } from 'astro:content' import { getCodeComparison } from '../lib/prepare-table' -import { frameworkLogos } from '../lib/framework-logos' +import { getBrandByPackage } from '../lib/framework-logos' type Props = { example: string @@ -21,7 +21,7 @@ const tabs = await Promise.all( frameworks.map(async ({ name, slug, package: packageName, entry }) => ({ name, slug, - logo: frameworkLogos[packageName], + logo: getBrandByPackage(packageName)?.logo, docs: entry?.data.docs, Content: entry == null ? null : (await render(entry)).Content, tabId: `${example}-tab-${slug}`, diff --git a/packages/docs/src/components/ComparisonBarChart.astro b/packages/docs/src/components/ComparisonBarChart.astro index 9a7bdcdc5..3d628f898 100644 --- a/packages/docs/src/components/ComparisonBarChart.astro +++ b/packages/docs/src/components/ComparisonBarChart.astro @@ -1,5 +1,5 @@ --- -import { formatChartValue, getComparisonChartHeight } from '../lib/chart' +import { COMPARISON_CHART_HEIGHT, formatChartValue } from '../lib/chart' import type { ChartDatum, ChartValueFormat } from '../lib/types' interface Props { @@ -19,7 +19,6 @@ const chartData = data.map((d) => ({ focused: d.focused, version: d.version, })) -const chartHeight = getComparisonChartHeight(chartData.length) const chartPayload = JSON.stringify({ id, @@ -36,7 +35,7 @@ const chartPayload = JSON.stringify({
@@ -152,8 +151,8 @@ const chartPayload = JSON.stringify({ import { decorative } from '@tanstack/charts/mark/decorative' import { tooltip } from '@tanstack/charts/tooltip' import { + COMPARISON_CHART_HEIGHT, formatChartValue, - getComparisonChartHeight, getFrameworkLogoSource, } from '../lib/chart' import type { ChartDatum, ComparisonChartPayload } from '../lib/types' @@ -293,8 +292,7 @@ const chartPayload = JSON.stringify({ const values = rows.map((d) => d.value) const max = Math.max(...values) const median = getMedian(values) - const height = getComparisonChartHeight(rows.length) - wrapper.style.setProperty('--comparison-chart-height', `${height}px`) + const height = COMPARISON_CHART_HEIGHT const barColor = getCssVar('--ft-chart-bar', BAR_COLOR) const barHoverColor = getCssVar('--ft-chart-bar-hover', BAR_HOVER_COLOR) diff --git a/packages/docs/src/components/ComparisonComposition.astro b/packages/docs/src/components/ComparisonComposition.astro new file mode 100644 index 000000000..56de12243 --- /dev/null +++ b/packages/docs/src/components/ComparisonComposition.astro @@ -0,0 +1,120 @@ +--- +import type { ComponentProps } from 'astro/types' +import type { MonthlyComparisonChartPayload } from '../lib/types' +import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonLineChart from './ComparisonLineChart.astro' + +interface Props { + bar: ComponentProps + line: MonthlyComparisonChartPayload +} + +const { bar, line } = Astro.props +--- + +
+
+ + +
+ +
+ +
+ + +
+ + + + diff --git a/packages/docs/src/components/ComparisonLineChart.astro b/packages/docs/src/components/ComparisonLineChart.astro new file mode 100644 index 000000000..f12915417 --- /dev/null +++ b/packages/docs/src/components/ComparisonLineChart.astro @@ -0,0 +1,465 @@ +--- +import { COMPARISON_CHART_HEIGHT, formatChartValue } from '../lib/chart' +import { LATEST_POINT } from '../lib/monthly-history' +import type { MonthlyComparisonChartPayload } from '../lib/types' + +type Props = MonthlyComparisonChartPayload + +const { title, description, months, series, valueFormat } = Astro.props +const chartPayload = JSON.stringify(Astro.props) + +function formatMonth(month: string) { + if (month === LATEST_POINT) return 'Latest' + return new Date(`${month}-01T00:00:00Z`).toLocaleDateString('en-US', { + month: 'short', + year: 'numeric', + timeZone: 'UTC', + }) +} + +function seriesColorVars( + color: MonthlyComparisonChartPayload['series'][number]['color'], +) { + return color == null + ? '' + : `--series-dark: ${color.dark}; --series-light: ${color.light ?? color.dark}` +} +--- + +
+
{title}
+ {description ?

{description}

: null} + + +
+
+ +
+ + + + + + {months.map((month) => )} + + + + { + series.map((s) => ( + + + {s.points.map((point) => ( + + ))} + + )) + } + +
+ {title} by month, using the version current on the first of each month, + then the latest results +
Framework{formatMonth(month)}
{s.name} + {point.value == null + ? '—' + : `${formatChartValue(point.value, valueFormat)} (v${point.version})`} +
+
+
+
+ + + + diff --git a/packages/docs/src/components/CorePackageDependencyCharts.astro b/packages/docs/src/components/CorePackageDependencyCharts.astro index 076e86e19..f40538416 100644 --- a/packages/docs/src/components/CorePackageDependencyCharts.astro +++ b/packages/docs/src/components/CorePackageDependencyCharts.astro @@ -1,6 +1,8 @@ --- -import { starterStats } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { starterHistory, starterStats } from '../lib/prepare-chart' +import { monthlyComparison } from '../lib/monthly-history' +import type { MetricDefinition } from '../lib/types' +import ComparisonComposition from './ComparisonComposition.astro' type DependencyMetric = 'allDependencies' | 'devDependencies' | 'prodDependencies' @@ -22,23 +24,37 @@ function getChartData(metric: DependencyMetric) { }) .sort((a, b) => a.value - b.value || a.name.localeCompare(b.name)) } + +function getCharts(metric: DependencyMetric, title: string) { + const definition: MetricDefinition = { + id: `frameworkDependencies.${metric}`, + title, + valueFormat: 'count', + yAxisLabel: 'Dependency count', + } + return { + bar: { ...definition, data: getChartData(metric) }, + line: monthlyComparison( + definition, + starterHistory, + starterStats, + (record) => { + const value = record.frameworkDependencies?.[metric] + return typeof value === 'number' && Number.isFinite(value) + ? value + : null + }, + ), + } +} --- - - - diff --git a/packages/docs/src/components/DepsChart.astro b/packages/docs/src/components/DepsChart.astro index ddad303c1..c552e82ed 100644 --- a/packages/docs/src/components/DepsChart.astro +++ b/packages/docs/src/components/DepsChart.astro @@ -1,9 +1,17 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareDevDependenciesComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareDevDependenciesComparison, + prepareDevDependenciesMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareDevDependenciesComparison(starterStats) +const bar = prepareDevDependenciesComparison(starterStats) +const line = prepareDevDependenciesMonthlyComparison( + starterHistory, + starterStats, +) --- - + diff --git a/packages/docs/src/components/DevServerStartChart.astro b/packages/docs/src/components/DevServerStartChart.astro index e43d3de82..e55494d50 100644 --- a/packages/docs/src/components/DevServerStartChart.astro +++ b/packages/docs/src/components/DevServerStartChart.astro @@ -1,9 +1,17 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareDevServerStartTimeComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareDevServerStartTimeComparison, + prepareDevServerStartTimeMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareDevServerStartTimeComparison(starterStats) +const bar = prepareDevServerStartTimeComparison(starterStats) +const line = prepareDevServerStartTimeMonthlyComparison( + starterHistory, + starterStats, +) --- - + diff --git a/packages/docs/src/components/DuplicateDependencyChart.astro b/packages/docs/src/components/DuplicateDependencyChart.astro index d270f77d0..b17042936 100644 --- a/packages/docs/src/components/DuplicateDependencyChart.astro +++ b/packages/docs/src/components/DuplicateDependencyChart.astro @@ -1,9 +1,17 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareDuplicateDependenciesComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareDuplicateDependenciesComparison, + prepareDuplicateDependenciesMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareDuplicateDependenciesComparison(starterStats) +const bar = prepareDuplicateDependenciesComparison(starterStats) +const line = prepareDuplicateDependenciesMonthlyComparison( + starterHistory, + starterStats, +) --- - + diff --git a/packages/docs/src/components/FirstPartyDependencyCharts.astro b/packages/docs/src/components/FirstPartyDependencyCharts.astro index 4a4bd69cb..197014f31 100644 --- a/packages/docs/src/components/FirstPartyDependencyCharts.astro +++ b/packages/docs/src/components/FirstPartyDependencyCharts.astro @@ -1,6 +1,8 @@ --- -import { starterStats } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { starterHistory, starterStats } from '../lib/prepare-chart' +import { monthlyComparison } from '../lib/monthly-history' +import type { MetricDefinition } from '../lib/types' +import ComparisonComposition from './ComparisonComposition.astro' type DependencyMetric = | 'allDependencies' @@ -25,29 +27,52 @@ function getDependencyChartData(metric: DependencyMetric) { }) .sort((a, b) => a.value - b.value || a.name.localeCompare(b.name)) } + +function getDependencyCharts(metric: DependencyMetric, title: string) { + const definition: MetricDefinition = { + id: `firstPartyDependencies.${metric}`, + title, + valueFormat: 'count', + yAxisLabel: 'Dependency count', + } + return { + bar: { ...definition, data: getDependencyChartData(metric) }, + line: monthlyComparison( + definition, + starterHistory, + starterStats, + (record) => { + const value = record.firstPartyDependencies?.[metric] + return typeof value === 'number' && Number.isFinite(value) + ? value + : null + }, + ), + } +} --- - - - - diff --git a/packages/docs/src/components/InstallTimeMedianChart.astro b/packages/docs/src/components/InstallTimeMedianChart.astro index b7a42c477..cc6436163 100644 --- a/packages/docs/src/components/InstallTimeMedianChart.astro +++ b/packages/docs/src/components/InstallTimeMedianChart.astro @@ -1,9 +1,14 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareInstallTimeComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareInstallTimeComparison, + prepareInstallTimeMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareInstallTimeComparison(starterStats) +const bar = prepareInstallTimeComparison(starterStats) +const line = prepareInstallTimeMonthlyComparison(starterHistory, starterStats) --- - + diff --git a/packages/docs/src/components/LighthouseClientSideRenderedFCPChart.astro b/packages/docs/src/components/LighthouseClientSideRenderedFCPChart.astro index e8b68fc1b..0ba2d32d7 100644 --- a/packages/docs/src/components/LighthouseClientSideRenderedFCPChart.astro +++ b/packages/docs/src/components/LighthouseClientSideRenderedFCPChart.astro @@ -1,11 +1,19 @@ --- -import { ssrRequestThroughputStats } from '../lib/prepare-chart' -import { prepareLighthouseClientSideRenderedTestsFcpMsComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareLighthouseClientSideRenderedTestsFcpMsComparison, + prepareLighthouseClientSideRenderedTestsFcpMsMonthlyComparison, + runtimeHistory, + ssrRequestThroughputStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareLighthouseClientSideRenderedTestsFcpMsComparison( +const bar = prepareLighthouseClientSideRenderedTestsFcpMsComparison( + ssrRequestThroughputStats, +) +const line = prepareLighthouseClientSideRenderedTestsFcpMsMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, ) --- - + diff --git a/packages/docs/src/components/LighthouseClientSideRenderedFPChart.astro b/packages/docs/src/components/LighthouseClientSideRenderedFPChart.astro index f0232051c..516a09432 100644 --- a/packages/docs/src/components/LighthouseClientSideRenderedFPChart.astro +++ b/packages/docs/src/components/LighthouseClientSideRenderedFPChart.astro @@ -1,11 +1,19 @@ --- -import { ssrRequestThroughputStats } from '../lib/prepare-chart' -import { prepareLighthouseClientSideRenderedTestsFirstPaintMsComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareLighthouseClientSideRenderedTestsFirstPaintMsComparison, + prepareLighthouseClientSideRenderedTestsFirstPaintMsMonthlyComparison, + runtimeHistory, + ssrRequestThroughputStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareLighthouseClientSideRenderedTestsFirstPaintMsComparison( +const bar = prepareLighthouseClientSideRenderedTestsFirstPaintMsComparison( + ssrRequestThroughputStats, +) +const line = prepareLighthouseClientSideRenderedTestsFirstPaintMsMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, ) --- - + diff --git a/packages/docs/src/components/LighthouseServerSideRenderedFCPChart.astro b/packages/docs/src/components/LighthouseServerSideRenderedFCPChart.astro index afcd6132d..93f98e9c2 100644 --- a/packages/docs/src/components/LighthouseServerSideRenderedFCPChart.astro +++ b/packages/docs/src/components/LighthouseServerSideRenderedFCPChart.astro @@ -1,11 +1,19 @@ --- -import { ssrRequestThroughputStats } from '../lib/prepare-chart' -import { prepareLighthouseServerSideRenderedTestsFcpMsComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareLighthouseServerSideRenderedTestsFcpMsComparison, + prepareLighthouseServerSideRenderedTestsFcpMsMonthlyComparison, + runtimeHistory, + ssrRequestThroughputStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareLighthouseServerSideRenderedTestsFcpMsComparison( +const bar = prepareLighthouseServerSideRenderedTestsFcpMsComparison( + ssrRequestThroughputStats, +) +const line = prepareLighthouseServerSideRenderedTestsFcpMsMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, ) --- - + diff --git a/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro b/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro index 760447abb..94c38db97 100644 --- a/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro +++ b/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro @@ -1,11 +1,19 @@ --- -import { ssrRequestThroughputStats } from '../lib/prepare-chart' -import { prepareLighthouseServerSideRenderedTestsFirstPaintMsComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareLighthouseServerSideRenderedTestsFirstPaintMsComparison, + prepareLighthouseServerSideRenderedTestsFirstPaintMsMonthlyComparison, + runtimeHistory, + ssrRequestThroughputStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareLighthouseServerSideRenderedTestsFirstPaintMsComparison( +const bar = prepareLighthouseServerSideRenderedTestsFirstPaintMsComparison( + ssrRequestThroughputStats, +) +const line = prepareLighthouseServerSideRenderedTestsFirstPaintMsMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, ) --- - + diff --git a/packages/docs/src/components/NodeModulesSizeChart.astro b/packages/docs/src/components/NodeModulesSizeChart.astro index 56f405e76..7df67d56e 100644 --- a/packages/docs/src/components/NodeModulesSizeChart.astro +++ b/packages/docs/src/components/NodeModulesSizeChart.astro @@ -1,9 +1,17 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareNodeModulesSizeComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareNodeModulesSizeComparison, + prepareNodeModulesSizeMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareNodeModulesSizeComparison(starterStats) +const bar = prepareNodeModulesSizeComparison(starterStats) +const line = prepareNodeModulesSizeMonthlyComparison( + starterHistory, + starterStats, +) --- - + diff --git a/packages/docs/src/components/ProdDepsChart.astro b/packages/docs/src/components/ProdDepsChart.astro index 9983d9a8f..1e3f7e2b2 100644 --- a/packages/docs/src/components/ProdDepsChart.astro +++ b/packages/docs/src/components/ProdDepsChart.astro @@ -1,9 +1,17 @@ --- -import { starterStats } from '../lib/prepare-chart' -import { prepareProdDependenciesComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareProdDependenciesComparison, + prepareProdDependenciesMonthlyComparison, + starterHistory, + starterStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareProdDependenciesComparison(starterStats) +const bar = prepareProdDependenciesComparison(starterStats) +const line = prepareProdDependenciesMonthlyComparison( + starterHistory, + starterStats, +) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker100P75LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker100P75LatencyChart.astro index 95400da07..fbd33d5a4 100644 --- a/packages/docs/src/components/SSRLoadWorker100P75LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker100P75LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 100, + 'p75LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 100, 'p75LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker100P90LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker100P90LatencyChart.astro index ea2e900ed..23f0eb9c0 100644 --- a/packages/docs/src/components/SSRLoadWorker100P90LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker100P90LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 100, + 'p90LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 100, 'p90LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker100P99LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker100P99LatencyChart.astro index f9c7f9971..36e241eea 100644 --- a/packages/docs/src/components/SSRLoadWorker100P99LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker100P99LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 100, + 'p99LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 100, 'p99LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker25P75LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker25P75LatencyChart.astro index 4e641c8f5..9bc9f88e3 100644 --- a/packages/docs/src/components/SSRLoadWorker25P75LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker25P75LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 25, + 'p75LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 25, 'p75LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker25P90LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker25P90LatencyChart.astro index debfce1de..1101d354c 100644 --- a/packages/docs/src/components/SSRLoadWorker25P90LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker25P90LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 25, + 'p90LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 25, 'p90LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker25P99LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker25P99LatencyChart.astro index 7249fde45..16aa65195 100644 --- a/packages/docs/src/components/SSRLoadWorker25P99LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker25P99LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 25, + 'p99LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 25, 'p99LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker50P75LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker50P75LatencyChart.astro index 734c2ca24..ce5cdfb99 100644 --- a/packages/docs/src/components/SSRLoadWorker50P75LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker50P75LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 50, + 'p75LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 50, 'p75LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker50P90LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker50P90LatencyChart.astro index b765878ab..7991612e0 100644 --- a/packages/docs/src/components/SSRLoadWorker50P90LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker50P90LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 50, + 'p90LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 50, 'p90LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRLoadWorker50P99LatencyChart.astro b/packages/docs/src/components/SSRLoadWorker50P99LatencyChart.astro index d629d7c63..027d741ef 100644 --- a/packages/docs/src/components/SSRLoadWorker50P99LatencyChart.astro +++ b/packages/docs/src/components/SSRLoadWorker50P99LatencyChart.astro @@ -1,16 +1,25 @@ --- import { + prepareSSRLatencyComparison, + prepareSSRLatencyMonthlyComparison, + runtimeHistory, ssrRequestThroughputStats, type SSRLoadKey, } from '../lib/prepare-chart' -import { prepareSSRLatencyComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import ComparisonComposition from './ComparisonComposition.astro' interface Props { testsKey?: SSRLoadKey } const { testsKey = 'ssrLoadTests' } = Astro.props -const chart = prepareSSRLatencyComparison( +const bar = prepareSSRLatencyComparison( + ssrRequestThroughputStats, + 50, + 'p99LatencyMs', + testsKey, +) +const line = prepareSSRLatencyMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, 50, 'p99LatencyMs', @@ -18,4 +27,4 @@ const chart = prepareSSRLatencyComparison( ) --- - + diff --git a/packages/docs/src/components/SSRRequestThroughputBodySizeChart.astro b/packages/docs/src/components/SSRRequestThroughputBodySizeChart.astro index 4c7f80ac1..5cde5657b 100644 --- a/packages/docs/src/components/SSRRequestThroughputBodySizeChart.astro +++ b/packages/docs/src/components/SSRRequestThroughputBodySizeChart.astro @@ -1,11 +1,19 @@ --- -import { ssrRequestThroughputStats } from '../lib/prepare-chart' -import { prepareSsrRequestThroughputTestsBodySizeKbComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareSsrRequestThroughputTestsBodySizeKbComparison, + prepareSsrRequestThroughputTestsBodySizeKbMonthlyComparison, + runtimeHistory, + ssrRequestThroughputStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareSsrRequestThroughputTestsBodySizeKbComparison( +const bar = prepareSsrRequestThroughputTestsBodySizeKbComparison( + ssrRequestThroughputStats, +) +const line = prepareSsrRequestThroughputTestsBodySizeKbMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, ) --- - + diff --git a/packages/docs/src/components/SSRRequestThroughputLatencyChart.astro b/packages/docs/src/components/SSRRequestThroughputLatencyChart.astro index 3ccebc2db..31daca573 100644 --- a/packages/docs/src/components/SSRRequestThroughputLatencyChart.astro +++ b/packages/docs/src/components/SSRRequestThroughputLatencyChart.astro @@ -1,11 +1,19 @@ --- -import { ssrRequestThroughputStats } from '../lib/prepare-chart' -import { prepareSsrRequestThroughputTestsLatencyMsComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareSsrRequestThroughputTestsLatencyMsComparison, + prepareSsrRequestThroughputTestsLatencyMsMonthlyComparison, + runtimeHistory, + ssrRequestThroughputStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareSsrRequestThroughputTestsLatencyMsComparison( +const bar = prepareSsrRequestThroughputTestsLatencyMsComparison( + ssrRequestThroughputStats, +) +const line = prepareSsrRequestThroughputTestsLatencyMsMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, ) --- - + diff --git a/packages/docs/src/components/SSRRequestThroughputOpsChart.astro b/packages/docs/src/components/SSRRequestThroughputOpsChart.astro index 0a7c88784..b122c6edc 100644 --- a/packages/docs/src/components/SSRRequestThroughputOpsChart.astro +++ b/packages/docs/src/components/SSRRequestThroughputOpsChart.astro @@ -1,11 +1,19 @@ --- -import { ssrRequestThroughputStats } from '../lib/prepare-chart' -import { prepareSsrRequestThroughputTestsOpsPerSecComparison } from '../lib/prepare-chart' -import ComparisonBarChart from './ComparisonBarChart.astro' +import { + prepareSsrRequestThroughputTestsOpsPerSecComparison, + prepareSsrRequestThroughputTestsOpsPerSecMonthlyComparison, + runtimeHistory, + ssrRequestThroughputStats, +} from '../lib/prepare-chart' +import ComparisonComposition from './ComparisonComposition.astro' -const chart = prepareSsrRequestThroughputTestsOpsPerSecComparison( +const bar = prepareSsrRequestThroughputTestsOpsPerSecComparison( + ssrRequestThroughputStats, +) +const line = prepareSsrRequestThroughputTestsOpsPerSecMonthlyComparison( + runtimeHistory, ssrRequestThroughputStats, ) --- - + diff --git a/packages/docs/src/lib/chart-measurements.ts b/packages/docs/src/lib/chart-measurements.ts index 575835d39..cf7896abf 100644 --- a/packages/docs/src/lib/chart-measurements.ts +++ b/packages/docs/src/lib/chart-measurements.ts @@ -1,11 +1,13 @@ import { INTERACTION_PHASES } from './interaction-phases.ts' import { metricCatalogue } from './metrics.ts' +import { monthlyComparison } from './monthly-history.ts' import type { ChartMeasurement, InteractionHistoryDatum, InteractionTimings, InstallChartMeasurement, MetricDefinition, + MonthlyComparisonChartPayload, PreparedComparisonChart, VersionLineChartPayload, } from './types' @@ -88,6 +90,20 @@ export function prepareDevServerStartTimeComparison( ) } +export function prepareDevServerStartTimeMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['devServerStartTime.medianMs'], + history, + latest, + (record) => finiteValue(record.devServerStartTime?.medianMs, 1000), + now, + ) +} + export function prepareFrameworkDependenciesProdDependenciesHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -148,6 +164,20 @@ export function prepareProdDependenciesComparison( ) } +export function prepareProdDependenciesMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['prodDependencies'], + history, + latest, + (record) => finiteValue(record.prodDependencies), + now, + ) +} + export function prepareDevDependenciesHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -172,6 +202,20 @@ export function prepareDevDependenciesComparison( ) } +export function prepareDevDependenciesMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['devDependencies'], + history, + latest, + (record) => finiteValue(record.devDependencies), + now, + ) +} + export function prepareAllDependenciesHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -208,6 +252,20 @@ export function prepareDuplicateDependenciesComparison( ) } +export function prepareDuplicateDependenciesMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['duplicateDependencies'], + history, + latest, + (record) => finiteValue(record.duplicateDependencies), + now, + ) +} + export function prepareLighthouseClientSideRenderedTestsFirstPaintMsHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -232,6 +290,20 @@ export function prepareLighthouseClientSideRenderedTestsFirstPaintMsComparison( ) } +export function prepareLighthouseClientSideRenderedTestsFirstPaintMsMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['clientSideRenderedTests.firstPaintMs'], + history, + latest, + (record) => finiteValue(record.clientSideRenderedTests?.firstPaintMs), + now, + ) +} + export function prepareLighthouseClientSideRenderedTestsFcpMsHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -314,6 +386,20 @@ export function prepareLighthouseServerSideRenderedTestsInteractionHistory( return interactionHistory(records, 'serverSideRenderedTests') } +export function prepareLighthouseClientSideRenderedTestsFcpMsMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['clientSideRenderedTests.fcpMs'], + history, + latest, + (record) => finiteValue(record.clientSideRenderedTests?.fcpMs), + now, + ) +} + export function prepareInstallTimeHistory( records: readonly InstallChartMeasurement[], ): VersionLineChartPayload { @@ -338,6 +424,20 @@ export function prepareInstallTimeComparison( ) } +export function prepareInstallTimeMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['installTime.medianMs'], + history, + latest, + (record) => finiteValue(record.install?.installTimeMs?.mean, 1000), + now, + ) +} + export function prepareColdBuildTimeHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -362,6 +462,20 @@ export function prepareColdBuildTimeComparison( ) } +export function prepareColdBuildTimeMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['coldBuildTime.medianMs'], + history, + latest, + (record) => finiteValue(record.coldBuildTime?.medianMs, 1000), + now, + ) +} + export function prepareWarmBuildTimeHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -386,6 +500,20 @@ export function prepareWarmBuildTimeComparison( ) } +export function prepareWarmBuildTimeMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['warmBuildTime.medianMs'], + history, + latest, + (record) => finiteValue(record.warmBuildTime?.medianMs, 1000), + now, + ) +} + export function prepareCoreJsSizeHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -422,6 +550,20 @@ export function prepareLighthouseServerSideRenderedTestsFirstPaintMsComparison( ) } +export function prepareLighthouseServerSideRenderedTestsFirstPaintMsMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['serverSideRenderedTests.firstPaintMs'], + history, + latest, + (record) => finiteValue(record.serverSideRenderedTests?.firstPaintMs), + now, + ) +} + export function prepareLighthouseServerSideRenderedTestsFcpMsHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -446,6 +588,20 @@ export function prepareLighthouseServerSideRenderedTestsFcpMsComparison( ) } +export function prepareLighthouseServerSideRenderedTestsFcpMsMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['serverSideRenderedTests.fcpMs'], + history, + latest, + (record) => finiteValue(record.serverSideRenderedTests?.fcpMs), + now, + ) +} + export function prepareSsrRequestThroughputTestsOpsPerSecHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -471,6 +627,20 @@ export function prepareSsrRequestThroughputTestsOpsPerSecComparison( ) } +export function prepareSsrRequestThroughputTestsOpsPerSecMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['ssrRequestThroughputTests.opsPerSec'], + history, + latest, + (record) => finiteValue(record.ssrRequestThroughputTests?.opsPerSec), + now, + ) +} + export function prepareSsrRequestThroughputTestsBodySizeKbHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -495,6 +665,20 @@ export function prepareSsrRequestThroughputTestsBodySizeKbComparison( ) } +export function prepareSsrRequestThroughputTestsBodySizeKbMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['ssrRequestThroughputTests.bodySizeKb'], + history, + latest, + (record) => finiteValue(record.ssrRequestThroughputTests?.bodySizeKb), + now, + ) +} + export function prepareSsrRequestThroughputTestsLatencyMsHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -519,6 +703,20 @@ export function prepareSsrRequestThroughputTestsLatencyMsComparison( ) } +export function prepareSsrRequestThroughputTestsLatencyMsMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['ssrRequestThroughputTests.latencyMs'], + history, + latest, + (record) => finiteValue(record.ssrRequestThroughputTests?.latencyMs), + now, + ) +} + export function prepareNodeModulesSizeHistory( records: readonly InstallChartMeasurement[], ): VersionLineChartPayload { @@ -543,6 +741,20 @@ export function prepareNodeModulesSizeComparison( ) } +export function prepareNodeModulesSizeMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue['nodeModulesSize'], + history, + latest, + (record) => finiteValue(record.install?.nodeModulesSizeBytes, 1048576), + now, + ) +} + export function prepareDependencyInstallSizeHistory( records: readonly ChartMeasurement[], ): VersionLineChartPayload { @@ -609,3 +821,25 @@ export function prepareSSRLatencyComparison( export type SSRLoadKey = 'ssrLoadTests' | 'ssrRouterLinkLoadTests' export type LatencyPercentile = 'p75LatencyMs' | 'p90LatencyMs' | 'p99LatencyMs' export type ConnectionCount = 25 | 50 | 100 + +export function prepareSSRLatencyMonthlyComparison( + history: readonly ChartMeasurement[], + latest: readonly ChartMeasurement[], + workers: ConnectionCount, + percentile: LatencyPercentile, + testsKey: SSRLoadKey = 'ssrLoadTests', + now?: Date, +): MonthlyComparisonChartPayload { + return monthlyComparison( + metricCatalogue[`${testsKey}.latency.${workers}.${percentile}`], + history, + latest, + (record) => + finiteValue( + record[testsKey]?.stages?.find((stage) => stage.workers === workers)?.[ + percentile + ], + ), + now, + ) +} diff --git a/packages/docs/src/lib/chart.ts b/packages/docs/src/lib/chart.ts index ec217f059..8ff069faa 100644 --- a/packages/docs/src/lib/chart.ts +++ b/packages/docs/src/lib/chart.ts @@ -1,3 +1,4 @@ +import { getBrandByDisplayName, resolveThemedValue } from './framework-logos' import type { ChartValueFormat } from './types' export function formatChartValue( @@ -25,36 +26,13 @@ export function formatChartValue( return prefixPositive && value > 0 ? `+${formatted}` : formatted } -export function getComparisonChartHeight(itemCount: number) { - return Math.max(240, itemCount * 56 + 24) -} - -const FRAMEWORK_LOGO_SOURCES: Record = { - Astro: '/framework-logos/astro-gradient.svg', - 'Baseline HTML': '/framework-logos/html5.svg', - Gatsby: '/framework-logos/gatsby.svg', - 'Next.js': '/framework-logos/nextdotjs.svg', - Nuxt: '/framework-logos/nuxt.svg', - 'Nuxt.js': '/framework-logos/nuxt.svg', - Pracht: '/framework-logos/pracht.svg', - 'React Router': '/framework-logos/reactrouter.svg', - SolidStart: '/framework-logos/solid-start.svg', - SvelteKit: '/framework-logos/svelte.svg', - 'TanStack Start': '/framework-logos/tanstack.svg', - WordPress: '/framework-logos/wordpress.svg', -} +/** Every comparison chart shares one height so they line up on the page. */ +export const COMPARISON_CHART_HEIGHT = 480 +/** Browser-only: resolves the logo for the page's current theme. */ export function getFrameworkLogoSource(name: string) { - if (name === 'Astro' && document.documentElement.dataset.theme === 'light') { - return '/framework-logos/astro-dark.svg' - } - - if ( - name === 'Next.js' && - document.documentElement.dataset.theme !== 'light' - ) { - return '/framework-logos/nextdotjs-light.svg' - } - - return FRAMEWORK_LOGO_SOURCES[name] + const brand = getBrandByDisplayName(name) + return brand == null + ? undefined + : resolveThemedValue(brand.logo, document.documentElement.dataset.theme) } diff --git a/packages/docs/src/lib/framework-cards.ts b/packages/docs/src/lib/framework-cards.ts index 9f363114d..3e5edf0e6 100644 --- a/packages/docs/src/lib/framework-cards.ts +++ b/packages/docs/src/lib/framework-cards.ts @@ -1,9 +1,9 @@ import { starterStats } from './prepare-chart' -import { frameworkLogos } from './framework-logos' +import { getBrandByPackage } from './framework-logos' import { getFrameworkSlug } from './utils' function getFrameworkTitle(name: string, packageName: string) { - const logo = frameworkLogos[packageName] + const logo = getBrandByPackage(packageName)?.logo let visual: string if (logo == null) { visual = `` diff --git a/packages/docs/src/lib/framework-logos.ts b/packages/docs/src/lib/framework-logos.ts index 3ce99c2ea..4a0eab2e6 100644 --- a/packages/docs/src/lib/framework-logos.ts +++ b/packages/docs/src/lib/framework-logos.ts @@ -1,19 +1,86 @@ -export type FrameworkLogo = { dark: string; light?: string } - -/** Keyed by devtime starter package name. */ -export const frameworkLogos: Record = { - 'starter-astro': { - dark: '/framework-logos/astro-gradient.svg', - light: '/framework-logos/astro-dark.svg', - }, - 'starter-next-js': { - dark: '/framework-logos/nextdotjs-light.svg', - light: '/framework-logos/nextdotjs.svg', - }, - 'starter-nuxt': { dark: '/framework-logos/nuxt.svg' }, - 'starter-pracht': { dark: '/framework-logos/pracht.svg' }, - 'starter-react-router': { dark: '/framework-logos/reactrouter.svg' }, - 'starter-solid-start': { dark: '/framework-logos/solid-start.svg' }, - 'starter-sveltekit': { dark: '/framework-logos/svelte.svg' }, - 'starter-tanstack-start-react': { dark: '/framework-logos/tanstack.svg' }, +import { frameworks } from '@framework-tracker/results/frameworks' + +export type ThemedValue = { dark: string; light?: string } +export type FrameworkLogo = ThemedValue + +export interface FrameworkBrand { + logo: FrameworkLogo + color?: ThemedValue +} + +export const frameworkBrands: Record = { + 'baseline-html': { logo: { dark: '/framework-logos/html5.svg' } }, + astro: { + logo: { + dark: '/framework-logos/astro-gradient.svg', + light: '/framework-logos/astro-dark.svg', + }, + color: { dark: '#BC52EE' }, + }, + next: { + logo: { + dark: '/framework-logos/nextdotjs-light.svg', + light: '/framework-logos/nextdotjs.svg', + }, + color: { dark: '#FFFFFF', light: '#000000' }, + }, + nuxt: { + logo: { dark: '/framework-logos/nuxt.svg' }, + color: { dark: '#00DC82' }, + }, + pracht: { + logo: { dark: '/framework-logos/pracht.svg' }, + color: { dark: '#FBBF24' }, + }, + 'react-router': { + logo: { dark: '/framework-logos/reactrouter.svg' }, + color: { dark: '#CA4245' }, + }, + 'solid-start': { + logo: { dark: '/framework-logos/solid-start.svg' }, + color: { dark: '#15ABFF', light: '#0084CE' }, + }, + sveltekit: { + logo: { dark: '/framework-logos/svelte.svg' }, + color: { dark: '#FF3E00' }, + }, + tanstack: { + logo: { dark: '/framework-logos/tanstack.svg' }, + color: { dark: '#FFA05C' }, + }, + // Not benchmarked, but shown in the Core Web Vitals charts. + gatsby: { logo: { dark: '/framework-logos/gatsby.svg' } }, + wordpress: { logo: { dark: '/framework-logos/wordpress.svg' } }, +} + +const brandKeyByDisplayName = new Map([ + ...frameworks.map((f) => [f.displayName, f.name] as const), + ['Nuxt.js', 'nuxt'], + ['Gatsby', 'gatsby'], + ['WordPress', 'wordpress'], +]) + +const brandKeyByPackage = new Map( + frameworks.flatMap((f) => + [f.starter?.package, f.app?.package].flatMap((packageName) => + packageName ? [[packageName, f.name] as const] : [], + ), + ), +) + +export function getBrandByDisplayName(name: string) { + const key = brandKeyByDisplayName.get(name) + return key == null ? undefined : frameworkBrands[key] +} + +export function getBrandByPackage(packageName: string) { + const key = brandKeyByPackage.get(packageName) + return key == null ? undefined : frameworkBrands[key] +} + +export function resolveThemedValue( + value: ThemedValue, + theme: string | undefined, +) { + return theme === 'light' ? (value.light ?? value.dark) : value.dark } diff --git a/packages/docs/src/lib/monthly-history.test.ts b/packages/docs/src/lib/monthly-history.test.ts new file mode 100644 index 000000000..bb047c31c --- /dev/null +++ b/packages/docs/src/lib/monthly-history.test.ts @@ -0,0 +1,106 @@ +import assert from 'node:assert/strict' +import { test } from 'node:test' +import { + getMonthStarts, + LATEST_POINT, + selectVersionAt, +} from './monthly-history.ts' +import { + prepareProdDependenciesComparison, + prepareProdDependenciesMonthlyComparison, +} from './chart-measurements.ts' + +const now = new Date('2026-10-27T12:00:00Z') + +test('month starts cover the year ending with the current month', () => { + const months = getMonthStarts(now) + assert.equal(months.length, 12) + assert.equal(months[0]!.toISOString(), '2025-11-01T00:00:00.000Z') + assert.equal(months.at(-1)!.toISOString(), '2026-10-01T00:00:00.000Z') +}) + +test('selects the latest version released on or before the date', () => { + const versions = [ + { v: '1', releasedAt: '2026-08-31T19:00:00Z' }, + { v: '2', releasedAt: '2026-10-01T00:00:00Z' }, + { v: '3', releasedAt: '2026-10-02T00:00:00Z' }, + { v: 'undated' }, + ] + const date = new Date('2026-10-01T00:00:00Z') + assert.equal(selectVersionAt(versions, date)?.v, '2') + assert.equal( + selectVersionAt(versions, new Date('2026-08-01T00:00:00Z')), + undefined, + ) +}) + +const astroVersions = [ + { + package: 'starter-astro', + name: 'Astro', + isFocused: true, + frameworkVersion: '7.1.6', + releasedAt: '2026-07-29T14:21:53Z', + prodDependencies: 10, + }, + { + package: 'starter-astro', + name: 'Astro', + isFocused: true, + frameworkVersion: '7.2.10', + releasedAt: '2026-08-31T19:39:50Z', + prodDependencies: 12, + }, +] +const astroLatest = { + package: 'starter-astro', + name: 'Astro', + isFocused: true, + frameworkVersion: '7.4.0', + releasedAt: '2026-10-15T14:49:24Z', + prodDependencies: 14, +} + +test('leaves gaps before a framework’s first tracked release', () => { + const chart = prepareProdDependenciesMonthlyComparison( + astroVersions, + [astroLatest], + now, + ) + + assert.equal(chart.id, 'prodDependencies-monthly') + const [astro] = chart.series + assert.deepEqual(astro!.color, { dark: '#BC52EE' }) + assert.deepEqual( + astro!.points.slice(-5, -1).map((p) => [p.month, p.value, p.version]), + [ + ['2026-07', null, null], + ['2026-08', 10, '7.1.6'], + ['2026-09', 12, '7.2.10'], + ['2026-10', 12, '7.2.10'], + ], + ) +}) + +test('the Latest point uses the same latest result as the bar chart', () => { + const line = prepareProdDependenciesMonthlyComparison( + astroVersions, + [astroLatest], + now, + ) + const bar = prepareProdDependenciesComparison([astroLatest]) + + assert.equal(line.months.at(-1), LATEST_POINT) + const nowPoint = line.series[0]!.points.at(-1)! + assert.deepEqual( + [nowPoint.month, nowPoint.value, nowPoint.version], + [LATEST_POINT, bar.data[0]!.value, bar.data[0]!.version], + ) +}) + +test('a framework with only latest results still gets a Latest point', () => { + const line = prepareProdDependenciesMonthlyComparison([], [astroLatest], now) + const points = line.series[0]!.points + assert.ok(points.slice(0, -1).every((p) => p.value == null)) + assert.equal(points.at(-1)!.value, 14) +}) diff --git a/packages/docs/src/lib/monthly-history.ts b/packages/docs/src/lib/monthly-history.ts new file mode 100644 index 000000000..9bfdd8199 --- /dev/null +++ b/packages/docs/src/lib/monthly-history.ts @@ -0,0 +1,104 @@ +import { getBrandByPackage } from './framework-logos.ts' +import type { + ChartMeasurement, + MetricDefinition, + MonthlyComparisonChartPayload, + MonthlyComparisonSeries, +} from './types' + +const MONTH_COUNT = 12 +const BASELINE_PACKAGE = 'app-baseline-html' + +function toMonthKey(date: Date) { + return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}` +} + +export function getMonthStarts(now: Date, count = MONTH_COUNT) { + return Array.from( + { length: count }, + (_, index) => + new Date( + Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - (count - 1 - index)), + ), + ) +} + +export function selectVersionAt( + records: readonly T[], + date: Date, +): T | undefined { + let selected: T | undefined + let selectedTime = -Infinity + for (const record of records) { + const time = record.releasedAt ? Date.parse(record.releasedAt) : NaN + if (time <= date.getTime() && time > selectedTime) { + selected = record + selectedTime = time + } + } + return selected +} + +export const LATEST_POINT = 'latest' + +export function monthlyComparison( + metric: MetricDefinition, + history: readonly T[], + latest: readonly T[], + getValue: (record: T) => number | null, + now = new Date(), +): MonthlyComparisonChartPayload { + const monthStarts = getMonthStarts(now) + const byPackage = new Map() + for (const record of history) { + if (record.package == null) continue + byPackage.set(record.package, [ + ...(byPackage.get(record.package) ?? []), + record, + ]) + } + const latestByPackage = new Map( + latest.flatMap((record) => + record.package == null ? [] : [[record.package, record] as const], + ), + ) + const packages = new Set([...byPackage.keys(), ...latestByPackage.keys()]) + + const series = [...packages].map((packageName): MonthlyComparisonSeries => { + const versions = byPackage.get(packageName) ?? [] + const current = latestByPackage.get(packageName) + const identity = current ?? versions[0]! + const baseline = packageName === BASELINE_PACKAGE + return { + id: packageName, + name: identity.name ?? packageName, + focused: baseline || identity.isFocused !== false, + baseline, + color: baseline ? null : (getBrandByPackage(packageName)?.color ?? null), + points: [ + ...monthStarts.map((date) => { + const record = selectVersionAt(versions, date) + return { + month: toMonthKey(date), + value: record ? getValue(record) : null, + version: record?.frameworkVersion ?? null, + } + }), + { + month: LATEST_POINT, + value: current ? getValue(current) : null, + version: current?.frameworkVersion ?? null, + }, + ], + } + }) + + return { + ...metric, + id: `${metric.id}-monthly`, + months: [...monthStarts.map(toMonthKey), LATEST_POINT], + series: series + .filter((s) => s.points.some((point) => point.value != null)) + .sort((a, b) => a.name.localeCompare(b.name)), + } +} diff --git a/packages/docs/src/lib/prepare-chart.ts b/packages/docs/src/lib/prepare-chart.ts index cd08dc442..7569f0a27 100644 --- a/packages/docs/src/lib/prepare-chart.ts +++ b/packages/docs/src/lib/prepare-chart.ts @@ -25,10 +25,10 @@ export const starterStats = latest export const ssrRequestThroughputStats = latest .filter((result) => result.projects.app) .map(prepareRuntimeResult) -const starterHistory = history +export const starterHistory = history .filter((result) => result.projects.starter) .map(prepareStarterResult) -const runtimeHistory = history +export const runtimeHistory = history .filter((result) => result.projects.app) .map(prepareRuntimeResult) type DevtimeVersionData = (typeof starterHistory)[number] diff --git a/packages/docs/src/lib/types.ts b/packages/docs/src/lib/types.ts index 58549f0de..baf39ed8e 100644 --- a/packages/docs/src/lib/types.ts +++ b/packages/docs/src/lib/types.ts @@ -24,6 +24,29 @@ export interface VersionLineChartPayload { yAxisLabel: string } +export interface MonthlyComparisonPoint { + /** First day of the month as `YYYY-MM`, or `latest` for the latest results. */ + month: string + value: number | null + version: string | null +} + +export interface MonthlyComparisonSeries { + id: string + name: string + focused: boolean + /** Reference series (Baseline HTML) drawn as a neutral dashed line. */ + baseline: boolean + /** Brand colour from `framework-logos.ts`; null draws a neutral line. */ + color: { dark: string; light?: string } | null + points: MonthlyComparisonPoint[] +} + +export interface MonthlyComparisonChartPayload extends MetricDefinition { + months: string[] + series: MonthlyComparisonSeries[] +} + export interface MetricDefinition { id: string title: string @@ -60,10 +83,15 @@ export interface ChartMeasurement { package?: string name?: string frameworkVersion?: string + releasedAt?: string isFocused?: boolean devServerStartTime?: { medianMs?: number } + install?: { + installTimeMs?: { mean?: number } + nodeModulesSizeBytes?: number + } frameworkDependencies?: { prodDependencies?: number devDependencies?: number diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c16217c8..ca63b0cfe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,10 +72,16 @@ importers: chart.js: specifier: ^4.5.1 version: 4.5.1 + d3-shape: + specifier: 3.2.0 + version: 3.2.0 devDependencies: '@astrojs/check': specifier: ^0.9.10 version: 0.9.10(prettier-plugin-astro@0.14.1)(prettier@3.9.6)(typescript@6.0.3) + '@types/d3-shape': + specifier: 3.2.0 + version: 3.2.0 '@typescript-eslint/parser': specifier: 8.62.1 version: 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) @@ -3027,8 +3033,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - legacy-javascript@0.0.3: - resolution: {integrity: sha512-1r6MT4dVaJyXt4G07Cm2cQ0AUaLJdefiEp4SmIAylA++gnEQ82YvmzE58RkM5EP0fk5hROLCslUPGnwIYdZQVA==} + legacy-javascript@0.0.4: + resolution: {integrity: sha512-UTmeKBVxnfJcpmpOvzzNnLsQT99BXHpPlc/ZKddIRmWmrR0fw6335I3lq4+sJoP60UTbBNaYsGYVPc65YB1x3A==} levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} @@ -5589,7 +5595,7 @@ snapshots: '@paulirish/trace_engine@0.0.65': dependencies: - legacy-javascript: 0.0.3 + legacy-javascript: 0.0.4 third-party-web: 0.30.0 '@pkgr/core@0.3.6': {} @@ -7340,7 +7346,7 @@ snapshots: yaml: 2.9.0 zod: 4.6.7 - legacy-javascript@0.0.3: {} + legacy-javascript@0.0.4: {} levn@0.4.1: dependencies: From 729b56acc136edede1f0d539e3c301f493bc6854 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Wed, 28 Oct 2026 12:41:18 +0100 Subject: [PATCH 2/2] fix: linters --- .../docs/src/components/FirstPartyDependencyCharts.astro | 5 +---- .../components/LighthouseClientSideRenderedFPChart.astro | 9 +++++---- .../components/LighthouseServerSideRenderedFPChart.astro | 9 +++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/docs/src/components/FirstPartyDependencyCharts.astro b/packages/docs/src/components/FirstPartyDependencyCharts.astro index 197014f31..08f45dde0 100644 --- a/packages/docs/src/components/FirstPartyDependencyCharts.astro +++ b/packages/docs/src/components/FirstPartyDependencyCharts.astro @@ -53,10 +53,7 @@ function getDependencyCharts(metric: DependencyMetric, title: string) { --- diff --git a/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro b/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro index 94c38db97..a930751e1 100644 --- a/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro +++ b/packages/docs/src/components/LighthouseServerSideRenderedFPChart.astro @@ -10,10 +10,11 @@ import ComparisonComposition from './ComparisonComposition.astro' const bar = prepareLighthouseServerSideRenderedTestsFirstPaintMsComparison( ssrRequestThroughputStats, ) -const line = prepareLighthouseServerSideRenderedTestsFirstPaintMsMonthlyComparison( - runtimeHistory, - ssrRequestThroughputStats, -) +const line = + prepareLighthouseServerSideRenderedTestsFirstPaintMsMonthlyComparison( + runtimeHistory, + ssrRequestThroughputStats, + ) ---