From a16a275325e3f5d0e41ac42b96491d7b9f7fbcab Mon Sep 17 00:00:00 2001 From: Alexander Karan Date: Sun, 21 Jun 2026 12:49:07 +0800 Subject: [PATCH 1/2] Re-named Client Side Performance --- .github/frameworks.json | 42 +++++++++-- .github/workflows/generate-stats.yml | 2 +- .github/workflows/validate-stats.yml | 2 +- packages/app-astro/ci-stats.json | 10 +-- packages/app-astro/stats/5.16.15.json | 10 +-- packages/app-next-js/ci-stats.json | 12 ++-- packages/app-next-js/stats/16.1.1.json | 12 ++-- packages/app-nuxt/ci-stats.json | 10 +-- packages/app-nuxt/stats/4.2.2.json | 10 +-- packages/app-react-router/ci-stats.json | 10 +-- packages/app-react-router/stats/7.10.1.json | 10 +-- packages/app-solid-start/ci-stats.json | 10 +-- packages/app-solid-start/stats/1.2.1.json | 10 +-- packages/app-sveltekit/ci-stats.json | 12 ++-- packages/app-sveltekit/stats/2.49.4.json | 12 ++-- .../app-tanstack-start-react/ci-stats.json | 10 +-- .../stats/1.145.3.json | 10 +-- .../components/ClientSideRenderedCharts.astro | 18 +++++ ...astro => ClientSideRenderedFCPChart.astro} | 4 +- ....astro => ClientSideRenderedFPChart.astro} | 4 +- ...astro => ClientSideRenderedINPChart.astro} | 4 +- ...ntSideRenderedStatsMethodologyNotes.astro} | 0 ...tro => ClientSideRenderedStatsTable.astro} | 12 ++-- packages/docs/src/components/SPACharts.astro | 18 ----- packages/docs/src/content.config.ts | 15 ++-- .../src/content/devtime/starter-astro.json | 2 +- .../src/content/devtime/starter-mastro.json | 2 +- .../src/content/devtime/starter-next-js.json | 2 +- .../src/content/devtime/starter-nuxt.json | 2 +- .../content/devtime/starter-react-router.json | 2 +- .../content/devtime/starter-solid-start.json | 2 +- .../content/devtime/starter-sveltekit.json | 2 +- .../devtime/starter-tanstack-start-react.json | 2 +- .../docs/src/content/runtime/app-astro.json | 14 ++-- .../content/runtime/app-baseline-html.json | 2 +- .../docs/src/content/runtime/app-mastro.json | 2 +- .../docs/src/content/runtime/app-next-js.json | 14 ++-- .../docs/src/content/runtime/app-nuxt.json | 12 ++-- .../src/content/runtime/app-react-router.json | 12 ++-- .../src/content/runtime/app-solid-start.json | 12 ++-- .../src/content/runtime/app-sveltekit.json | 14 ++-- .../runtime/app-tanstack-start-react.json | 12 ++-- packages/docs/src/lib/collections.ts | 70 ++++++++++++------- .../docs/src/pages/framework/[slug].astro | 28 ++++---- packages/docs/src/pages/index.astro | 11 +-- packages/docs/src/pages/run-time.astro | 16 +++-- .../src/{spa => clientSideRendered}/index.ts | 0 .../run-benchmark.ts | 10 +-- .../src/{spa => clientSideRendered}/types.ts | 20 +++--- packages/stats-generator/src/get-ci-stats.ts | 3 +- .../stats-generator/src/run-spa-benchmark.ts | 15 ++-- packages/stats-generator/src/save-ci-stats.ts | 20 +++--- packages/stats-generator/src/save-stats.ts | 7 +- packages/stats-generator/src/types.ts | 14 ++-- packages/stats-generator/src/utils.ts | 37 +++++++++- 55 files changed, 389 insertions(+), 241 deletions(-) create mode 100644 packages/docs/src/components/ClientSideRenderedCharts.astro rename packages/docs/src/components/{SPAFCPChart.astro => ClientSideRenderedFCPChart.astro} (67%) rename packages/docs/src/components/{SPAFPChart.astro => ClientSideRenderedFPChart.astro} (67%) rename packages/docs/src/components/{SPAINPChart.astro => ClientSideRenderedINPChart.astro} (67%) rename packages/docs/src/components/{SPAStatsMethodologyNotes.astro => ClientSideRenderedStatsMethodologyNotes.astro} (100%) rename packages/docs/src/components/{SPAStatsTable.astro => ClientSideRenderedStatsTable.astro} (59%) delete mode 100644 packages/docs/src/components/SPACharts.astro rename packages/stats-generator/src/{spa => clientSideRendered}/index.ts (100%) rename packages/stats-generator/src/{spa => clientSideRendered}/run-benchmark.ts (96%) rename packages/stats-generator/src/{spa => clientSideRendered}/types.ts (58%) diff --git a/.github/frameworks.json b/.github/frameworks.json index 69b3ebc2..348d5411 100644 --- a/.github/frameworks.json +++ b/.github/frameworks.json @@ -30,7 +30,11 @@ "package": "app-astro", "buildScript": "build", "buildOutputDir": "dist", - "measurements": [{ "type": "ssr" }, { "type": "spa" }, { "type": "mpa" }] + "measurements": [ + { "type": "ssr" }, + { "type": "clientSideRendered" }, + { "type": "mpa" } + ] } }, { @@ -74,7 +78,11 @@ "package": "app-next-js", "buildScript": "build", "buildOutputDir": ".next", - "measurements": [{ "type": "ssr" }, { "type": "spa" }, { "type": "mpa" }] + "measurements": [ + { "type": "ssr" }, + { "type": "clientSideRendered" }, + { "type": "mpa" } + ] } }, { @@ -96,7 +104,11 @@ "package": "app-nuxt", "buildScript": "build", "buildOutputDir": ".output", - "measurements": [{ "type": "ssr" }, { "type": "spa" }, { "type": "mpa" }] + "measurements": [ + { "type": "ssr" }, + { "type": "clientSideRendered" }, + { "type": "mpa" } + ] } }, { @@ -118,7 +130,11 @@ "package": "app-react-router", "buildScript": "build", "buildOutputDir": "build", - "measurements": [{ "type": "ssr" }, { "type": "spa" }, { "type": "mpa" }] + "measurements": [ + { "type": "ssr" }, + { "type": "clientSideRendered" }, + { "type": "mpa" } + ] } }, { @@ -140,7 +156,11 @@ "package": "app-solid-start", "buildScript": "build", "buildOutputDir": ".output", - "measurements": [{ "type": "ssr" }, { "type": "spa" }, { "type": "mpa" }] + "measurements": [ + { "type": "ssr" }, + { "type": "clientSideRendered" }, + { "type": "mpa" } + ] } }, { @@ -162,7 +182,11 @@ "package": "app-sveltekit", "buildScript": "build", "buildOutputDir": "build", - "measurements": [{ "type": "ssr" }, { "type": "spa" }, { "type": "mpa" }] + "measurements": [ + { "type": "ssr" }, + { "type": "clientSideRendered" }, + { "type": "mpa" } + ] } }, { @@ -184,7 +208,11 @@ "package": "app-tanstack-start-react", "buildScript": "build", "buildOutputDir": ".output", - "measurements": [{ "type": "ssr" }, { "type": "spa" }, { "type": "mpa" }] + "measurements": [ + { "type": "ssr" }, + { "type": "clientSideRendered" }, + { "type": "mpa" } + ] } } ] diff --git a/.github/workflows/generate-stats.yml b/.github/workflows/generate-stats.yml index 155b5bc5..9bb4927d 100644 --- a/.github/workflows/generate-stats.yml +++ b/.github/workflows/generate-stats.yml @@ -40,7 +40,7 @@ jobs: echo "build=$(echo "$FRAMEWORKS" | jq -c '[.[] | select(.starter) | select(.starter.measurements | map(.type) | contains(["build"])) | {name, displayName, package: .starter.package, buildScript: .starter.buildScript, buildOutputDir: .starter.buildOutputDir, measurements: .starter.measurements}]')" >> $GITHUB_OUTPUT echo "ssr=$(echo "$FRAMEWORKS" | jq -c '[.[] | select(.app) | select(.app.measurements | map(.type) | contains(["ssr"])) | {name, displayName, package: .app.package, buildScript: .app.buildScript, buildOutputDir: .app.buildOutputDir, measurements: .app.measurements}]')" >> $GITHUB_OUTPUT echo "deps=$(echo "$FRAMEWORKS" | jq -c '[.[] | select(.starter) | select(.starter.measurements | map(.type) | contains(["dependencies"])) | {name, displayName, package: .starter.package}]')" >> $GITHUB_OUTPUT - echo "spa=$(echo "$FRAMEWORKS" | jq -c '[.[] | select(.app) | select(.app.measurements | map(.type) | contains(["spa"])) | {name, displayName, package: .app.package, buildScript: .app.buildScript, buildOutputDir: .app.buildOutputDir, measurements: .app.measurements}]')" >> $GITHUB_OUTPUT + echo "spa=$(echo "$FRAMEWORKS" | jq -c '[.[] | select(.app) | select(.app.measurements | map(.type) | contains(["clientSideRendered"])) | {name, displayName, package: .app.package, buildScript: .app.buildScript, buildOutputDir: .app.buildOutputDir, measurements: .app.measurements}]')" >> $GITHUB_OUTPUT echo "mpa=$(echo "$FRAMEWORKS" | jq -c '[.[] | select(.app) | select(.app.measurements | map(.type) | contains(["mpa"])) | {name, displayName, package: .app.package, buildScript: .app.buildScript, buildOutputDir: .app.buildOutputDir, measurements: .app.measurements}]')" >> $GITHUB_OUTPUT measure: diff --git a/.github/workflows/validate-stats.yml b/.github/workflows/validate-stats.yml index 1f9ec9d0..1dfb43ed 100644 --- a/.github/workflows/validate-stats.yml +++ b/.github/workflows/validate-stats.yml @@ -75,7 +75,7 @@ jobs: echo "" echo "=== Running SPA benchmarks ===" FRAMEWORKS=$(cat .github/frameworks.json) - echo "$FRAMEWORKS" | jq -c '.[] | select(.app) | select(.app.measurements | map(.type) | contains(["spa"]))' | while read -r framework; do + echo "$FRAMEWORKS" | jq -c '.[] | select(.app) | select(.app.measurements | map(.type) | contains(["clientSideRendered"]))' | while read -r framework; do PKG=$(echo "$framework" | jq -r '.app.package') echo "Building SPA for $PKG..." (cd packages/$PKG && BUILD_MODE=spa pnpm build) diff --git a/packages/app-astro/ci-stats.json b/packages/app-astro/ci-stats.json index d0370e3a..6562a4a0 100644 --- a/packages/app-astro/ci-stats.json +++ b/packages/app-astro/ci-stats.json @@ -12,8 +12,10 @@ "mpaFCPMs": 79.21, "mpaINPMs": 3.59, "mpaRuns": 5, - "spaFirstPaintMs": 95.6, - "spaFCPMs": 95.46, - "spaINPMs": 11.03, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 95.6, + "fcpMs": 95.46, + "inpMs": 11.03, + "runs": 5 + } } diff --git a/packages/app-astro/stats/5.16.15.json b/packages/app-astro/stats/5.16.15.json index d0370e3a..6562a4a0 100644 --- a/packages/app-astro/stats/5.16.15.json +++ b/packages/app-astro/stats/5.16.15.json @@ -12,8 +12,10 @@ "mpaFCPMs": 79.21, "mpaINPMs": 3.59, "mpaRuns": 5, - "spaFirstPaintMs": 95.6, - "spaFCPMs": 95.46, - "spaINPMs": 11.03, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 95.6, + "fcpMs": 95.46, + "inpMs": 11.03, + "runs": 5 + } } diff --git a/packages/app-next-js/ci-stats.json b/packages/app-next-js/ci-stats.json index a3043d43..16e2d1cb 100644 --- a/packages/app-next-js/ci-stats.json +++ b/packages/app-next-js/ci-stats.json @@ -8,12 +8,14 @@ "ssrSamples": 2153, "ssrBodySizeKb": 199.11, "ssrDuplicationFactor": 2, - "spaFirstPaintMs": 347, - "spaFCPMs": 347.14, - "spaINPMs": 16.89, - "spaRuns": 5, "mpaFirstPaintMs": 139.8, "mpaFCPMs": 139.94, "mpaINPMs": 13.99, - "mpaRuns": 5 + "mpaRuns": 5, + "clientSideRenderedTests": { + "firstPaintMs": 347, + "fcpMs": 347.14, + "inpMs": 16.89, + "runs": 5 + } } diff --git a/packages/app-next-js/stats/16.1.1.json b/packages/app-next-js/stats/16.1.1.json index a3043d43..16e2d1cb 100644 --- a/packages/app-next-js/stats/16.1.1.json +++ b/packages/app-next-js/stats/16.1.1.json @@ -8,12 +8,14 @@ "ssrSamples": 2153, "ssrBodySizeKb": 199.11, "ssrDuplicationFactor": 2, - "spaFirstPaintMs": 347, - "spaFCPMs": 347.14, - "spaINPMs": 16.89, - "spaRuns": 5, "mpaFirstPaintMs": 139.8, "mpaFCPMs": 139.94, "mpaINPMs": 13.99, - "mpaRuns": 5 + "mpaRuns": 5, + "clientSideRenderedTests": { + "firstPaintMs": 347, + "fcpMs": 347.14, + "inpMs": 16.89, + "runs": 5 + } } diff --git a/packages/app-nuxt/ci-stats.json b/packages/app-nuxt/ci-stats.json index 65426cff..d618edec 100644 --- a/packages/app-nuxt/ci-stats.json +++ b/packages/app-nuxt/ci-stats.json @@ -12,8 +12,10 @@ "mpaFCPMs": 72.34, "mpaINPMs": 2.94, "mpaRuns": 5, - "spaFirstPaintMs": 95.6, - "spaFCPMs": 95.59, - "spaINPMs": 9.05, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 95.6, + "fcpMs": 95.59, + "inpMs": 9.05, + "runs": 5 + } } diff --git a/packages/app-nuxt/stats/4.2.2.json b/packages/app-nuxt/stats/4.2.2.json index 65426cff..d618edec 100644 --- a/packages/app-nuxt/stats/4.2.2.json +++ b/packages/app-nuxt/stats/4.2.2.json @@ -12,8 +12,10 @@ "mpaFCPMs": 72.34, "mpaINPMs": 2.94, "mpaRuns": 5, - "spaFirstPaintMs": 95.6, - "spaFCPMs": 95.59, - "spaINPMs": 9.05, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 95.6, + "fcpMs": 95.59, + "inpMs": 9.05, + "runs": 5 + } } diff --git a/packages/app-react-router/ci-stats.json b/packages/app-react-router/ci-stats.json index 69feb74a..f1e87e6b 100644 --- a/packages/app-react-router/ci-stats.json +++ b/packages/app-react-router/ci-stats.json @@ -12,8 +12,10 @@ "mpaFCPMs": 143.91, "mpaINPMs": 2.14, "mpaRuns": 5, - "spaFirstPaintMs": 125, - "spaFCPMs": 125.21, - "spaINPMs": 18.41, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 125, + "fcpMs": 125.21, + "inpMs": 18.41, + "runs": 5 + } } diff --git a/packages/app-react-router/stats/7.10.1.json b/packages/app-react-router/stats/7.10.1.json index 69feb74a..f1e87e6b 100644 --- a/packages/app-react-router/stats/7.10.1.json +++ b/packages/app-react-router/stats/7.10.1.json @@ -12,8 +12,10 @@ "mpaFCPMs": 143.91, "mpaINPMs": 2.14, "mpaRuns": 5, - "spaFirstPaintMs": 125, - "spaFCPMs": 125.21, - "spaINPMs": 18.41, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 125, + "fcpMs": 125.21, + "inpMs": 18.41, + "runs": 5 + } } diff --git a/packages/app-solid-start/ci-stats.json b/packages/app-solid-start/ci-stats.json index ab9632fb..46c12490 100644 --- a/packages/app-solid-start/ci-stats.json +++ b/packages/app-solid-start/ci-stats.json @@ -12,8 +12,10 @@ "mpaFCPMs": 77.93, "mpaINPMs": 11.95, "mpaRuns": 5, - "spaFirstPaintMs": 105.6, - "spaFCPMs": 105.6, - "spaINPMs": 16.21, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 105.6, + "fcpMs": 105.6, + "inpMs": 16.21, + "runs": 5 + } } diff --git a/packages/app-solid-start/stats/1.2.1.json b/packages/app-solid-start/stats/1.2.1.json index ab9632fb..46c12490 100644 --- a/packages/app-solid-start/stats/1.2.1.json +++ b/packages/app-solid-start/stats/1.2.1.json @@ -12,8 +12,10 @@ "mpaFCPMs": 77.93, "mpaINPMs": 11.95, "mpaRuns": 5, - "spaFirstPaintMs": 105.6, - "spaFCPMs": 105.6, - "spaINPMs": 16.21, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 105.6, + "fcpMs": 105.6, + "inpMs": 16.21, + "runs": 5 + } } diff --git a/packages/app-sveltekit/ci-stats.json b/packages/app-sveltekit/ci-stats.json index bd2cdf69..1ac78541 100644 --- a/packages/app-sveltekit/ci-stats.json +++ b/packages/app-sveltekit/ci-stats.json @@ -8,12 +8,14 @@ "ssrSamples": 4317, "ssrBodySizeKb": 183.55, "ssrDuplicationFactor": 2, - "spaFirstPaintMs": 107.4, - "spaFCPMs": 107.7, - "spaINPMs": 14.19, - "spaRuns": 5, "mpaFirstPaintMs": 81.6, "mpaFCPMs": 81.81, "mpaINPMs": 0.82, - "mpaRuns": 5 + "mpaRuns": 5, + "clientSideRenderedTests": { + "firstPaintMs": 107.4, + "fcpMs": 107.7, + "inpMs": 14.19, + "runs": 5 + } } diff --git a/packages/app-sveltekit/stats/2.49.4.json b/packages/app-sveltekit/stats/2.49.4.json index bd2cdf69..1ac78541 100644 --- a/packages/app-sveltekit/stats/2.49.4.json +++ b/packages/app-sveltekit/stats/2.49.4.json @@ -8,12 +8,14 @@ "ssrSamples": 4317, "ssrBodySizeKb": 183.55, "ssrDuplicationFactor": 2, - "spaFirstPaintMs": 107.4, - "spaFCPMs": 107.7, - "spaINPMs": 14.19, - "spaRuns": 5, "mpaFirstPaintMs": 81.6, "mpaFCPMs": 81.81, "mpaINPMs": 0.82, - "mpaRuns": 5 + "mpaRuns": 5, + "clientSideRenderedTests": { + "firstPaintMs": 107.4, + "fcpMs": 107.7, + "inpMs": 14.19, + "runs": 5 + } } diff --git a/packages/app-tanstack-start-react/ci-stats.json b/packages/app-tanstack-start-react/ci-stats.json index 89760ccf..321b100f 100644 --- a/packages/app-tanstack-start-react/ci-stats.json +++ b/packages/app-tanstack-start-react/ci-stats.json @@ -12,8 +12,10 @@ "mpaFCPMs": 85.11, "mpaINPMs": 2.25, "mpaRuns": 5, - "spaFirstPaintMs": 142.6, - "spaFCPMs": 142.54, - "spaINPMs": 28.14, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 142.6, + "fcpMs": 142.54, + "inpMs": 28.14, + "runs": 5 + } } diff --git a/packages/app-tanstack-start-react/stats/1.145.3.json b/packages/app-tanstack-start-react/stats/1.145.3.json index 89760ccf..321b100f 100644 --- a/packages/app-tanstack-start-react/stats/1.145.3.json +++ b/packages/app-tanstack-start-react/stats/1.145.3.json @@ -12,8 +12,10 @@ "mpaFCPMs": 85.11, "mpaINPMs": 2.25, "mpaRuns": 5, - "spaFirstPaintMs": 142.6, - "spaFCPMs": 142.54, - "spaINPMs": 28.14, - "spaRuns": 5 + "clientSideRenderedTests": { + "firstPaintMs": 142.6, + "fcpMs": 142.54, + "inpMs": 28.14, + "runs": 5 + } } diff --git a/packages/docs/src/components/ClientSideRenderedCharts.astro b/packages/docs/src/components/ClientSideRenderedCharts.astro new file mode 100644 index 00000000..1f78ae3b --- /dev/null +++ b/packages/docs/src/components/ClientSideRenderedCharts.astro @@ -0,0 +1,18 @@ +--- +import ChartTabs from './ChartTabs.astro' +import ClientSideRenderedFPChart from './ClientSideRenderedFPChart.astro' +import ClientSideRenderedFCPChart from './ClientSideRenderedFCPChart.astro' +import ClientSideRenderedINPChart from './ClientSideRenderedINPChart.astro' +--- + + + + + + diff --git a/packages/docs/src/components/SPAFCPChart.astro b/packages/docs/src/components/ClientSideRenderedFCPChart.astro similarity index 67% rename from packages/docs/src/components/SPAFCPChart.astro rename to packages/docs/src/components/ClientSideRenderedFCPChart.astro index 900fee69..78e989ca 100644 --- a/packages/docs/src/components/SPAFCPChart.astro +++ b/packages/docs/src/components/ClientSideRenderedFCPChart.astro @@ -1,8 +1,8 @@ --- -import { chartSPAFCPData } from '../lib/collections' +import { chartClientSideRenderedFCPData } from '../lib/collections' import ComparisonBarChart from './ComparisonBarChart.astro' -const data = [...chartSPAFCPData].sort( +const data = [...chartClientSideRenderedFCPData].sort( (a, b) => a.value - b.value || a.name.localeCompare(b.name), ) --- diff --git a/packages/docs/src/components/SPAFPChart.astro b/packages/docs/src/components/ClientSideRenderedFPChart.astro similarity index 67% rename from packages/docs/src/components/SPAFPChart.astro rename to packages/docs/src/components/ClientSideRenderedFPChart.astro index adb262e1..e95611d4 100644 --- a/packages/docs/src/components/SPAFPChart.astro +++ b/packages/docs/src/components/ClientSideRenderedFPChart.astro @@ -1,8 +1,8 @@ --- -import { chartSPAFPData } from '../lib/collections' +import { chartClientSideRenderedFPData } from '../lib/collections' import ComparisonBarChart from './ComparisonBarChart.astro' -const data = [...chartSPAFPData].sort( +const data = [...chartClientSideRenderedFPData].sort( (a, b) => a.value - b.value || a.name.localeCompare(b.name), ) --- diff --git a/packages/docs/src/components/SPAINPChart.astro b/packages/docs/src/components/ClientSideRenderedINPChart.astro similarity index 67% rename from packages/docs/src/components/SPAINPChart.astro rename to packages/docs/src/components/ClientSideRenderedINPChart.astro index 8500db44..166fb3d0 100644 --- a/packages/docs/src/components/SPAINPChart.astro +++ b/packages/docs/src/components/ClientSideRenderedINPChart.astro @@ -1,8 +1,8 @@ --- -import { chartSPAINPData } from '../lib/collections' +import { chartClientSideRenderedINPData } from '../lib/collections' import ComparisonBarChart from './ComparisonBarChart.astro' -const data = [...chartSPAINPData].sort( +const data = [...chartClientSideRenderedINPData].sort( (a, b) => a.value - b.value || a.name.localeCompare(b.name), ) --- diff --git a/packages/docs/src/components/SPAStatsMethodologyNotes.astro b/packages/docs/src/components/ClientSideRenderedStatsMethodologyNotes.astro similarity index 100% rename from packages/docs/src/components/SPAStatsMethodologyNotes.astro rename to packages/docs/src/components/ClientSideRenderedStatsMethodologyNotes.astro diff --git a/packages/docs/src/components/SPAStatsTable.astro b/packages/docs/src/components/ClientSideRenderedStatsTable.astro similarity index 59% rename from packages/docs/src/components/SPAStatsTable.astro rename to packages/docs/src/components/ClientSideRenderedStatsTable.astro index 15fe073e..76b2fa79 100644 --- a/packages/docs/src/components/SPAStatsTable.astro +++ b/packages/docs/src/components/ClientSideRenderedStatsTable.astro @@ -1,5 +1,5 @@ --- -import { spaStats } from '../lib/collections' +import { clientSideRenderedStats } from '../lib/collections' import { getFrameworkSlug } from '../lib/utils' import StatsTable from './StatsTable.astro' @@ -13,16 +13,16 @@ const columns = [ ? `/framework/${getFrameworkSlug(row.package as string)}` : null, }, - { key: 'spaFirstPaintMs', header: 'First Paint' }, - { key: 'spaFCPMs', header: 'FCP' }, - { key: 'spaINPMs', header: 'INP' }, + { key: 'firstPaintMs', header: 'First Paint' }, + { key: 'fcpMs', header: 'FCP' }, + { key: 'inpMs', header: 'INP' }, ] -const tableData = spaStats +const tableData = clientSideRenderedStats --- diff --git a/packages/docs/src/components/SPACharts.astro b/packages/docs/src/components/SPACharts.astro deleted file mode 100644 index b3172d3a..00000000 --- a/packages/docs/src/components/SPACharts.astro +++ /dev/null @@ -1,18 +0,0 @@ ---- -import ChartTabs from './ChartTabs.astro' -import SPAFPChart from './SPAFPChart.astro' -import SPAFCPChart from './SPAFCPChart.astro' -import SPAINPChart from './SPAINPChart.astro' ---- - - - - - - diff --git a/packages/docs/src/content.config.ts b/packages/docs/src/content.config.ts index 491223fd..512cd0ed 100644 --- a/packages/docs/src/content.config.ts +++ b/packages/docs/src/content.config.ts @@ -57,11 +57,16 @@ const runtimeCollection = defineCollection({ ssrSamples: z.number(), ssrBodySizeKb: z.number(), ssrDuplicationFactor: z.number(), - // SPA paint + interaction metrics - spaFirstPaintMs: z.number().optional(), - spaFCPMs: z.number().optional(), - spaINPMs: z.number().optional(), - spaRuns: z.number().optional(), + // Client-side rendered paint + interaction metrics + clientSideRenderedTests: z + .object({ + firstPaintMs: z.number(), + fcpMs: z.number(), + inpMs: z.number(), + runs: z.number(), + }) + .optional(), + // MPA paint + interaction metrics mpaFirstPaintMs: z.number().optional(), mpaFCPMs: z.number().optional(), diff --git a/packages/docs/src/content/devtime/starter-astro.json b/packages/docs/src/content/devtime/starter-astro.json index f9b27d73..c40dde07 100644 --- a/packages/docs/src/content/devtime/starter-astro.json +++ b/packages/docs/src/content/devtime/starter-astro.json @@ -124,4 +124,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/devtime/starter-mastro.json b/packages/docs/src/content/devtime/starter-mastro.json index 28511179..ebc316c6 100644 --- a/packages/docs/src/content/devtime/starter-mastro.json +++ b/packages/docs/src/content/devtime/starter-mastro.json @@ -32,4 +32,4 @@ "e18eMessages": [], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/devtime/starter-next-js.json b/packages/docs/src/content/devtime/starter-next-js.json index 5aa08456..7ee52e58 100644 --- a/packages/docs/src/content/devtime/starter-next-js.json +++ b/packages/docs/src/content/devtime/starter-next-js.json @@ -407,4 +407,4 @@ "web.url-search-params.has", "web.url-search-params.size" ] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/devtime/starter-nuxt.json b/packages/docs/src/content/devtime/starter-nuxt.json index 736a1199..b8705e37 100644 --- a/packages/docs/src/content/devtime/starter-nuxt.json +++ b/packages/docs/src/content/devtime/starter-nuxt.json @@ -354,4 +354,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/devtime/starter-react-router.json b/packages/docs/src/content/devtime/starter-react-router.json index a0c195fc..9339b8d3 100644 --- a/packages/docs/src/content/devtime/starter-react-router.json +++ b/packages/docs/src/content/devtime/starter-react-router.json @@ -84,4 +84,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/devtime/starter-solid-start.json b/packages/docs/src/content/devtime/starter-solid-start.json index 194cc34e..65eec37a 100644 --- a/packages/docs/src/content/devtime/starter-solid-start.json +++ b/packages/docs/src/content/devtime/starter-solid-start.json @@ -434,4 +434,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/devtime/starter-sveltekit.json b/packages/docs/src/content/devtime/starter-sveltekit.json index cdb245e7..401fa2f6 100644 --- a/packages/docs/src/content/devtime/starter-sveltekit.json +++ b/packages/docs/src/content/devtime/starter-sveltekit.json @@ -44,4 +44,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/devtime/starter-tanstack-start-react.json b/packages/docs/src/content/devtime/starter-tanstack-start-react.json index de1379ca..a9bdbd1f 100644 --- a/packages/docs/src/content/devtime/starter-tanstack-start-react.json +++ b/packages/docs/src/content/devtime/starter-tanstack-start-react.json @@ -109,4 +109,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-astro.json b/packages/docs/src/content/runtime/app-astro.json index da12c939..8f5ee1ca 100644 --- a/packages/docs/src/content/runtime/app-astro.json +++ b/packages/docs/src/content/runtime/app-astro.json @@ -13,12 +13,14 @@ "runner": "ubuntu-latest", "frameworkVersion": "5.16.15", "order": 1, - "spaFirstPaintMs": 95.6, - "spaFCPMs": 95.46, - "spaINPMs": 11.03, - "spaRuns": 5, "mpaFirstPaintMs": 79.4, "mpaFCPMs": 79.21, "mpaINPMs": 3.59, - "mpaRuns": 5 -} + "mpaRuns": 5, + "clientSideRenderedTests": { + "firstPaintMs": 95.6, + "fcpMs": 95.46, + "inpMs": 11.03, + "runs": 5 + } +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-baseline-html.json b/packages/docs/src/content/runtime/app-baseline-html.json index f48edde2..316aacd1 100644 --- a/packages/docs/src/content/runtime/app-baseline-html.json +++ b/packages/docs/src/content/runtime/app-baseline-html.json @@ -12,4 +12,4 @@ "order": 0, "timingMeasuredAt": "2026-06-20T06:03:29.537Z", "runner": "ubuntu-latest" -} +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-mastro.json b/packages/docs/src/content/runtime/app-mastro.json index e5c3ca65..691c3988 100644 --- a/packages/docs/src/content/runtime/app-mastro.json +++ b/packages/docs/src/content/runtime/app-mastro.json @@ -12,4 +12,4 @@ "ssrBodySizeKb": 181.95, "ssrDuplicationFactor": 1, "order": 2 -} +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-next-js.json b/packages/docs/src/content/runtime/app-next-js.json index c95dab5d..12877c64 100644 --- a/packages/docs/src/content/runtime/app-next-js.json +++ b/packages/docs/src/content/runtime/app-next-js.json @@ -13,12 +13,14 @@ "ssrBodySizeKb": 199.11, "ssrDuplicationFactor": 2, "order": 3, - "spaFirstPaintMs": 347, - "spaFCPMs": 347.14, - "spaINPMs": 16.89, - "spaRuns": 5, "mpaFirstPaintMs": 139.8, "mpaFCPMs": 139.94, "mpaINPMs": 13.99, - "mpaRuns": 5 -} + "mpaRuns": 5, + "clientSideRenderedTests": { + "firstPaintMs": 347, + "fcpMs": 347.14, + "inpMs": 16.89, + "runs": 5 + } +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-nuxt.json b/packages/docs/src/content/runtime/app-nuxt.json index a3cfcdb4..93635363 100644 --- a/packages/docs/src/content/runtime/app-nuxt.json +++ b/packages/docs/src/content/runtime/app-nuxt.json @@ -17,8 +17,10 @@ "mpaFCPMs": 72.34, "mpaINPMs": 2.94, "mpaRuns": 5, - "spaFirstPaintMs": 95.6, - "spaFCPMs": 95.59, - "spaINPMs": 9.05, - "spaRuns": 5 -} + "clientSideRenderedTests": { + "firstPaintMs": 95.6, + "fcpMs": 95.59, + "inpMs": 9.05, + "runs": 5 + } +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-react-router.json b/packages/docs/src/content/runtime/app-react-router.json index eeba78d8..96ee0ac7 100644 --- a/packages/docs/src/content/runtime/app-react-router.json +++ b/packages/docs/src/content/runtime/app-react-router.json @@ -17,8 +17,10 @@ "mpaFCPMs": 143.91, "mpaINPMs": 2.14, "mpaRuns": 5, - "spaFirstPaintMs": 125, - "spaFCPMs": 125.21, - "spaINPMs": 18.41, - "spaRuns": 5 -} + "clientSideRenderedTests": { + "firstPaintMs": 125, + "fcpMs": 125.21, + "inpMs": 18.41, + "runs": 5 + } +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-solid-start.json b/packages/docs/src/content/runtime/app-solid-start.json index 0ef0aefa..101f7671 100644 --- a/packages/docs/src/content/runtime/app-solid-start.json +++ b/packages/docs/src/content/runtime/app-solid-start.json @@ -17,8 +17,10 @@ "mpaFCPMs": 77.93, "mpaINPMs": 11.95, "mpaRuns": 5, - "spaFirstPaintMs": 105.6, - "spaFCPMs": 105.6, - "spaINPMs": 16.21, - "spaRuns": 5 -} + "clientSideRenderedTests": { + "firstPaintMs": 105.6, + "fcpMs": 105.6, + "inpMs": 16.21, + "runs": 5 + } +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-sveltekit.json b/packages/docs/src/content/runtime/app-sveltekit.json index 280cc60b..2ac76345 100644 --- a/packages/docs/src/content/runtime/app-sveltekit.json +++ b/packages/docs/src/content/runtime/app-sveltekit.json @@ -13,12 +13,14 @@ "ssrBodySizeKb": 183.55, "ssrDuplicationFactor": 2, "order": 7, - "spaFirstPaintMs": 107.4, - "spaFCPMs": 107.7, - "spaINPMs": 14.19, - "spaRuns": 5, "mpaFirstPaintMs": 81.6, "mpaFCPMs": 81.81, "mpaINPMs": 0.82, - "mpaRuns": 5 -} + "mpaRuns": 5, + "clientSideRenderedTests": { + "firstPaintMs": 107.4, + "fcpMs": 107.7, + "inpMs": 14.19, + "runs": 5 + } +} \ No newline at end of file diff --git a/packages/docs/src/content/runtime/app-tanstack-start-react.json b/packages/docs/src/content/runtime/app-tanstack-start-react.json index 3648cc7a..54b85105 100644 --- a/packages/docs/src/content/runtime/app-tanstack-start-react.json +++ b/packages/docs/src/content/runtime/app-tanstack-start-react.json @@ -17,8 +17,10 @@ "mpaFCPMs": 85.11, "mpaINPMs": 2.25, "mpaRuns": 5, - "spaFirstPaintMs": 142.6, - "spaFCPMs": 142.54, - "spaINPMs": 28.14, - "spaRuns": 5 -} + "clientSideRenderedTests": { + "firstPaintMs": 142.6, + "fcpMs": 142.54, + "inpMs": 28.14, + "runs": 5 + } +} \ No newline at end of file diff --git a/packages/docs/src/lib/collections.ts b/packages/docs/src/lib/collections.ts index ec51d68d..c1966940 100644 --- a/packages/docs/src/lib/collections.ts +++ b/packages/docs/src/lib/collections.ts @@ -8,11 +8,11 @@ export const starterStats = devtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) -const ssrStats = runtimeEntries +export const ssrStats = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) -const mpaStats = runtimeEntries +export const mpaStats = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) .filter((f) => f?.name != null && Number.isFinite(f.mpaFirstPaintMs)) @@ -25,20 +25,24 @@ const mpaStats = runtimeEntries mpaINPMs: `${f.mpaINPMs}ms`, })) -const spaStats = runtimeEntries +export const clientSideRenderedStats = runtimeEntries .map((entry) => entry.data) + .filter( + (framework) => + framework.clientSideRenderedTests != null && + Number.isFinite(framework.clientSideRenderedTests.firstPaintMs), + ) .sort((a, b) => a.order - b.order) - .filter((f) => f?.name != null && Number.isFinite(f.spaFirstPaintMs)) - .map((f) => ({ - name: f.name, - package: f.package, - isFocused: f.isFocused, - spaFirstPaintMs: `${f.spaFirstPaintMs}ms`, - spaFCPMs: `${f.spaFCPMs}ms`, - spaINPMs: `${f.spaINPMs}ms`, + .map((framework) => ({ + name: framework.name, + package: framework.package, + isFocused: framework.isFocused, + firstPaintMs: `${framework.clientSideRenderedTests!.firstPaintMs}ms`, + fcpMs: `${framework.clientSideRenderedTests!.fcpMs}ms`, + inpMs: `${framework.clientSideRenderedTests!.inpMs}ms`, })) -const depsStats = starterStats.map((f) => ({ +export const depsStats = starterStats.map((f) => ({ name: f.name, package: f.package, isFocused: f.isFocused, @@ -52,7 +56,7 @@ const depsStats = starterStats.map((f) => ({ graph: 'View', })) -const buildInstallData = starterStats.map((f) => ({ +export const buildInstallData = starterStats.map((f) => ({ name: f.name, package: f.package, isFocused: f.isFocused, @@ -98,27 +102,47 @@ export const chartMPAINPData = runtimeEntries .filter((f) => f?.name != null && Number.isFinite(f.mpaINPMs)) .map((f) => ({ name: f.name, value: f.mpaINPMs!, focused: f.isFocused })) -export const chartSPAFPData = runtimeEntries +export const chartClientSideRenderedFPData = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) - .filter((f) => f?.name != null && Number.isFinite(f.spaFirstPaintMs)) + .filter( + (f) => + f.clientSideRenderedTests != null && + Number.isFinite(f.clientSideRenderedTests.firstPaintMs), + ) .map((f) => ({ name: f.name, - value: f.spaFirstPaintMs!, + value: f.clientSideRenderedTests!.firstPaintMs, focused: f.isFocused, })) -export const chartSPAFCPData = runtimeEntries +export const chartClientSideRenderedFCPData = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) - .filter((f) => f?.name != null && Number.isFinite(f.spaFCPMs)) - .map((f) => ({ name: f.name, value: f.spaFCPMs!, focused: f.isFocused })) + .filter( + (f) => + f.clientSideRenderedTests != null && + Number.isFinite(f.clientSideRenderedTests.fcpMs), + ) + .map((f) => ({ + name: f.name, + value: f.clientSideRenderedTests!.fcpMs, + focused: f.isFocused, + })) -export const chartSPAINPData = runtimeEntries +export const chartClientSideRenderedINPData = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) - .filter((f) => f?.name != null && Number.isFinite(f.spaINPMs)) - .map((f) => ({ name: f.name, value: f.spaINPMs!, focused: f.isFocused })) + .filter( + (f) => + f.clientSideRenderedTests != null && + Number.isFinite(f.clientSideRenderedTests.inpMs), + ) + .map((f) => ({ + name: f.name, + value: f.clientSideRenderedTests!.inpMs, + focused: f.isFocused, + })) export const coreJsTableData = starterStats.map((f) => { const hasCorejs = (f.vendoredCoreJsUnnecessaryModules?.length ?? 0) > 0 @@ -134,5 +158,3 @@ export const coreJsTableData = starterStats.map((f) => { : '—', } }) - -export { ssrStats, spaStats, mpaStats, depsStats, buildInstallData } diff --git a/packages/docs/src/pages/framework/[slug].astro b/packages/docs/src/pages/framework/[slug].astro index 379e8e22..9b3e5f3c 100644 --- a/packages/docs/src/pages/framework/[slug].astro +++ b/packages/docs/src/pages/framework/[slug].astro @@ -4,7 +4,7 @@ import BackLink from '../../components/BackLink.astro' import DevTimeChart from '../../components/DevTimeChart.astro' import MPAStatsMethodologyNotes from '../../components/MPAStatsMethodologyNotes.astro' import PageHeader from '../../components/PageHeader.astro' -import SPAStatsMethodologyNotes from '../../components/SPAStatsMethodologyNotes.astro' +import ClientSideRenderedStatsMethodologyNotes from '../../components/ClientSideRenderedStatsMethodologyNotes.astro' import SSRStatsMethodologyNotes from '../../components/SSRStatsMethodologyNotes.astro' import StatsTable from '../../components/StatsTable.astro' import Layout from '../../layouts/Layout.astro' @@ -166,20 +166,20 @@ const ssrData = [ : []), ] -const spaColumns = [ +const clientSideRenderedColumns = [ { key: 'name', header: 'Framework', nameCell: true }, - { key: 'spaFirstPaintMs', header: 'First Paint' }, - { key: 'spaFCPMs', header: 'FCP' }, - { key: 'spaINPMs', header: 'INP' }, + { key: 'firstPaintMs', header: 'First Paint' }, + { key: 'fcpMs', header: 'FCP' }, + { key: 'inpMs', header: 'INP' }, ] -const spaData = runtime +const clientSideRenderedData = runtime?.clientSideRenderedTests ? [ { name: runtime.name, - spaFirstPaintMs: `${runtime.spaFirstPaintMs}ms`, - spaFCPMs: `${runtime.spaFCPMs}ms`, - spaINPMs: `${runtime.spaINPMs}ms`, + firstPaintMs: `${runtime.clientSideRenderedTests.firstPaintMs}ms`, + fcpMs: `${runtime.clientSideRenderedTests.fcpMs}ms`, + inpMs: `${runtime.clientSideRenderedTests.inpMs}ms`, }, ] : [] @@ -332,13 +332,13 @@ const mpaData = runtime data={ssrData} /> -

SPA Performance

+

Client Side Rendered Performance

- +

MPA Performance

SSR Performance -

SPA Performance

- - +

Client Side Performance

+ +

MPA Performance

diff --git a/packages/docs/src/pages/run-time.astro b/packages/docs/src/pages/run-time.astro index 2e9f728a..0c01cad8 100644 --- a/packages/docs/src/pages/run-time.astro +++ b/packages/docs/src/pages/run-time.astro @@ -3,9 +3,9 @@ import MPACharts from '../components/MPACharts.astro' import MPAStatsMethodologyNotes from '../components/MPAStatsMethodologyNotes.astro' import MPAStatsTable from '../components/MPAStatsTable.astro' import PageHeader from '../components/PageHeader.astro' -import SPACharts from '../components/SPACharts.astro' -import SPAStatsMethodologyNotes from '../components/SPAStatsMethodologyNotes.astro' -import SPAStatsTable from '../components/SPAStatsTable.astro' +import ClientSideRenderedCharts from '../components/ClientSideRenderedCharts.astro' +import ClientSideRenderedStatsMethodologyNotes from '../components/ClientSideRenderedStatsMethodologyNotes.astro' +import ClientSideRenderedStatsTable from '../components/ClientSideRenderedStatsTable.astro' import SSRStatsMethodologyNotes from '../components/SSRStatsMethodologyNotes.astro' import SSRStatsTable from '../components/SSRStatsTable.astro' import Layout from '../layouts/Layout.astro' @@ -16,10 +16,12 @@ import Layout from '../layouts/Layout.astro'

SSR Performance

-

SPA Performance

- - - +

+ Client Side Rendered Performance +

+ + +

MPA Performance

diff --git a/packages/stats-generator/src/spa/index.ts b/packages/stats-generator/src/clientSideRendered/index.ts similarity index 100% rename from packages/stats-generator/src/spa/index.ts rename to packages/stats-generator/src/clientSideRendered/index.ts diff --git a/packages/stats-generator/src/spa/run-benchmark.ts b/packages/stats-generator/src/clientSideRendered/run-benchmark.ts similarity index 96% rename from packages/stats-generator/src/spa/run-benchmark.ts rename to packages/stats-generator/src/clientSideRendered/run-benchmark.ts index ab90a4a7..5505f6f2 100644 --- a/packages/stats-generator/src/spa/run-benchmark.ts +++ b/packages/stats-generator/src/clientSideRendered/run-benchmark.ts @@ -121,9 +121,11 @@ export async function runBenchmark( name: packageName, displayName, package: packageName, - spaFirstPaintMs: avg(fp), - spaFCPMs: avg(fcp), - spaINPMs: avg(inp), - spaRuns: results.length, + clientSideRenderedTests: { + firstPaintMs: avg(fp), + fcpMs: avg(fcp), + inpMs: avg(inp), + runs: results.length, + }, } } diff --git a/packages/stats-generator/src/spa/types.ts b/packages/stats-generator/src/clientSideRendered/types.ts similarity index 58% rename from packages/stats-generator/src/spa/types.ts rename to packages/stats-generator/src/clientSideRendered/types.ts index ec7d5f2b..f1a81065 100644 --- a/packages/stats-generator/src/spa/types.ts +++ b/packages/stats-generator/src/clientSideRendered/types.ts @@ -8,18 +8,22 @@ export interface SPABenchmarkResult { name: string displayName: string package: string - spaFirstPaintMs: number - spaFCPMs: number - spaINPMs: number - spaRuns: number + clientSideRenderedTests: { + firstPaintMs: number + fcpMs: number + inpMs: number + runs: number + } } export interface SPAStats { timingMeasuredAt: string runner: string frameworkVersion?: string - spaFirstPaintMs: number - spaFCPMs: number - spaINPMs: number - spaRuns: number + clientSideRenderedTests: { + firstPaintMs: number + fcpMs: number + inpMs: number + runs: number + } } diff --git a/packages/stats-generator/src/get-ci-stats.ts b/packages/stats-generator/src/get-ci-stats.ts index 72b8124d..421b2800 100644 --- a/packages/stats-generator/src/get-ci-stats.ts +++ b/packages/stats-generator/src/get-ci-stats.ts @@ -2,13 +2,14 @@ import { readFile } from 'node:fs/promises' import { join } from 'node:path' import { packagesDir } from './constants.ts' import type { CIStats } from './types.ts' +import { normalizeCIStats } from './utils.ts' export async function getCIStats(pkgDir: string): Promise { const ciStatsPath = join(packagesDir, pkgDir, 'ci-stats.json') try { const content = await readFile(ciStatsPath, 'utf-8') - return JSON.parse(content) as CIStats + return normalizeCIStats(JSON.parse(content) as CIStats) } catch (error) { console.warn(`Warning: Failed to read CI stats for ${pkgDir}:`, error) return null diff --git a/packages/stats-generator/src/run-spa-benchmark.ts b/packages/stats-generator/src/run-spa-benchmark.ts index 65905d2c..d7c933d9 100644 --- a/packages/stats-generator/src/run-spa-benchmark.ts +++ b/packages/stats-generator/src/run-spa-benchmark.ts @@ -1,6 +1,6 @@ import { readFile, writeFile } from 'node:fs/promises' import { join } from 'node:path' -import { runSPABenchmark } from './spa/index.ts' +import { runSPABenchmark } from './clientSideRendered/index.ts' import { packagesDir } from './constants.ts' import { getFrameworkByPackage, readJsonFile } from './utils.ts' import type { CIStats } from './types.ts' @@ -58,10 +58,7 @@ async function main() { timingMeasuredAt: timestamp, runner, frameworkVersion: frameworkVersion ?? existingStats?.frameworkVersion, - spaFirstPaintMs: result.spaFirstPaintMs, - spaFCPMs: result.spaFCPMs, - spaINPMs: result.spaINPMs, - spaRuns: result.spaRuns, + clientSideRenderedTests: result.clientSideRenderedTests, } const outputPath = join(packagesDir, packageName, 'ci-stats.json') @@ -70,9 +67,11 @@ async function main() { console.info( `\n✓ Saved ${result.displayName} v${frameworkVersion ?? 'unknown'} (${packageName})`, ) - console.info(` First Paint: ${result.spaFirstPaintMs}ms`) - console.info(` FCP: ${result.spaFCPMs}ms`) - console.info(` INP: ${result.spaINPMs}ms`) + console.info( + ` First Paint: ${result.clientSideRenderedTests.firstPaintMs}ms`, + ) + console.info(` FCP: ${result.clientSideRenderedTests.fcpMs}ms`) + console.info(` INP: ${result.clientSideRenderedTests.inpMs}ms`) } main().catch(console.error) diff --git a/packages/stats-generator/src/save-ci-stats.ts b/packages/stats-generator/src/save-ci-stats.ts index c57e4fb5..991ee95e 100644 --- a/packages/stats-generator/src/save-ci-stats.ts +++ b/packages/stats-generator/src/save-ci-stats.ts @@ -1,7 +1,7 @@ import { join } from 'node:path' import { getFrameworks } from './get-frameworks.ts' import { packagesDir } from './constants.ts' -import { readJsonFile, writeJsonFile } from './utils.ts' +import { normalizeCIStats, readJsonFile, writeJsonFile } from './utils.ts' import type { CIStats, InstallStats, @@ -34,7 +34,7 @@ async function main() { const existingStats = readJsonFile(existingStatsPath) let stats: CIStats = { - ...existingStats, + ...(existingStats ? normalizeCIStats(existingStats) : {}), timingMeasuredAt: timestamp, runner, } @@ -159,7 +159,7 @@ async function main() { const existingStats = readJsonFile(existingStatsPath) let stats: CIStats = { - ...existingStats, + ...(existingStats ? normalizeCIStats(existingStats) : {}), timingMeasuredAt: timestamp, runner, } @@ -172,7 +172,8 @@ async function main() { `ssr-stats-${name}`, 'ci-stats.json', ) - const ssrStats = readJsonFile(ssrStatsPath) + const rawSsrStats = readJsonFile(ssrStatsPath) + const ssrStats = rawSsrStats ? normalizeCIStats(rawSsrStats) : null if (ssrStats) { console.info(` ✓ Found SSR stats artifact`) @@ -197,16 +198,14 @@ async function main() { `spa-stats-${name}`, 'ci-stats.json', ) - const spaStats = readJsonFile(spaStatsArtifactPath) + const rawSpaStats = readJsonFile(spaStatsArtifactPath) + const spaStats = rawSpaStats ? normalizeCIStats(rawSpaStats) : null if (spaStats) { console.info(` ✓ Found SPA stats artifact`) stats = { ...stats, - spaFirstPaintMs: spaStats.spaFirstPaintMs, - spaFCPMs: spaStats.spaFCPMs, - spaINPMs: spaStats.spaINPMs, - spaRuns: spaStats.spaRuns, + clientSideRenderedTests: spaStats.clientSideRenderedTests, } } else { console.warn(`No SPA stats artifact found at ${spaStatsArtifactPath}`) @@ -218,7 +217,8 @@ async function main() { `mpa-stats-${name}`, 'ci-stats.json', ) - const mpaStats = readJsonFile(mpaStatsArtifactPath) + const rawMpaStats = readJsonFile(mpaStatsArtifactPath) + const mpaStats = rawMpaStats ? normalizeCIStats(rawMpaStats) : null if (mpaStats) { console.info(` ✓ Found MPA stats artifact`) diff --git a/packages/stats-generator/src/save-stats.ts b/packages/stats-generator/src/save-stats.ts index eafa9270..01dead0b 100644 --- a/packages/stats-generator/src/save-stats.ts +++ b/packages/stats-generator/src/save-stats.ts @@ -2,6 +2,7 @@ import { access, readFile, writeFile } from 'node:fs/promises' import { join } from 'node:path' import { packagesDir } from './constants.ts' import type { FrameworkStats } from './types.ts' +import { normalizeCIStats } from './utils.ts' export type StatsCollection = 'devtime' | 'runtime' @@ -23,10 +24,12 @@ export async function saveStats( const fileName = `${packageName}.json` const filePath = join(outputDir, fileName) - let mergedStats = stats + let mergedStats = normalizeCIStats(stats) try { const existingContent = await readFile(filePath, 'utf-8') - const existingStats = JSON.parse(existingContent) as FrameworkStats + const existingStats = normalizeCIStats( + JSON.parse(existingContent) as FrameworkStats, + ) mergedStats = { ...existingStats, ...stats } } catch (error) { if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { diff --git a/packages/stats-generator/src/types.ts b/packages/stats-generator/src/types.ts index 7897380a..2ecaf1fd 100644 --- a/packages/stats-generator/src/types.ts +++ b/packages/stats-generator/src/types.ts @@ -4,7 +4,7 @@ export type MeasurementType = | 'test' | 'dependencies' | 'ssr' - | 'spa' + | 'clientSideRendered' | 'mpa' export interface MeasurementConfig { @@ -48,11 +48,13 @@ export interface CIStats { ssrSamples?: number ssrBodySizeKb?: number ssrDuplicationFactor?: number - // SPA stats (browser paint + interaction timings) - spaFirstPaintMs?: number - spaFCPMs?: number - spaINPMs?: number - spaRuns?: number + // Client-side rendered stats (browser paint + interaction timings) + clientSideRenderedTests?: { + firstPaintMs: number + fcpMs: number + inpMs: number + runs: number + } // MPA stats (browser paint + interaction timings) mpaFirstPaintMs?: number mpaFCPMs?: number diff --git a/packages/stats-generator/src/utils.ts b/packages/stats-generator/src/utils.ts index 96765673..5198aa86 100644 --- a/packages/stats-generator/src/utils.ts +++ b/packages/stats-generator/src/utils.ts @@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process' import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs' import { join, dirname } from 'node:path' import { getFrameworks } from './get-frameworks.ts' -import type { FrameworkConfig, TestConfig } from './types.ts' +import type { CIStats, FrameworkConfig, TestConfig } from './types.ts' /** * Get directory size in bytes using du command. @@ -46,6 +46,41 @@ export function writeJsonFile(filePath: string, data: unknown): void { writeFileSync(filePath, JSON.stringify(data, null, 2)) } +/** + * Keep generated stats on the current nested client-side-rendered shape. + * This also cleans up older flat fields when merging existing stats files. + */ +export function normalizeCIStats(stats: T): T { + const legacyStats = stats as T & { + clientSideRenderedFirstPaintMs?: unknown + clientSideRenderedFCPMs?: unknown + clientSideRenderedINPMs?: unknown + clientSideRenderedRuns?: unknown + } + + if ( + stats.clientSideRenderedTests == null && + typeof legacyStats.clientSideRenderedFirstPaintMs === 'number' && + typeof legacyStats.clientSideRenderedFCPMs === 'number' && + typeof legacyStats.clientSideRenderedINPMs === 'number' && + typeof legacyStats.clientSideRenderedRuns === 'number' + ) { + stats.clientSideRenderedTests = { + firstPaintMs: legacyStats.clientSideRenderedFirstPaintMs, + fcpMs: legacyStats.clientSideRenderedFCPMs, + inpMs: legacyStats.clientSideRenderedINPMs, + runs: legacyStats.clientSideRenderedRuns, + } + } + + delete legacyStats.clientSideRenderedFirstPaintMs + delete legacyStats.clientSideRenderedFCPMs + delete legacyStats.clientSideRenderedINPMs + delete legacyStats.clientSideRenderedRuns + + return stats +} + /** * Find a framework by package name (searching both starter and app sections). * Exits with error if not found. From 7d26fe1140b7fa9204d437b1faf21d7ed61573aa Mon Sep 17 00:00:00 2001 From: Alexander Karan Date: Sun, 21 Jun 2026 13:05:34 +0800 Subject: [PATCH 2/2] Fixed stats --- packages/docs/src/content/devtime/starter-astro.json | 2 +- packages/docs/src/content/devtime/starter-mastro.json | 2 +- packages/docs/src/content/devtime/starter-next-js.json | 2 +- packages/docs/src/content/devtime/starter-nuxt.json | 2 +- packages/docs/src/content/devtime/starter-react-router.json | 2 +- packages/docs/src/content/devtime/starter-solid-start.json | 2 +- packages/docs/src/content/devtime/starter-sveltekit.json | 2 +- .../docs/src/content/devtime/starter-tanstack-start-react.json | 2 +- packages/docs/src/content/runtime/app-astro.json | 2 +- packages/docs/src/content/runtime/app-baseline-html.json | 2 +- packages/docs/src/content/runtime/app-mastro.json | 2 +- packages/docs/src/content/runtime/app-next-js.json | 2 +- packages/docs/src/content/runtime/app-nuxt.json | 2 +- packages/docs/src/content/runtime/app-react-router.json | 2 +- packages/docs/src/content/runtime/app-solid-start.json | 2 +- packages/docs/src/content/runtime/app-sveltekit.json | 2 +- packages/docs/src/content/runtime/app-tanstack-start-react.json | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/docs/src/content/devtime/starter-astro.json b/packages/docs/src/content/devtime/starter-astro.json index 59c7c0f9..43852036 100644 --- a/packages/docs/src/content/devtime/starter-astro.json +++ b/packages/docs/src/content/devtime/starter-astro.json @@ -124,4 +124,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/devtime/starter-mastro.json b/packages/docs/src/content/devtime/starter-mastro.json index 12757cfc..6efb4b45 100644 --- a/packages/docs/src/content/devtime/starter-mastro.json +++ b/packages/docs/src/content/devtime/starter-mastro.json @@ -32,4 +32,4 @@ "e18eMessages": [], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/devtime/starter-next-js.json b/packages/docs/src/content/devtime/starter-next-js.json index d5278d6f..7cf6117f 100644 --- a/packages/docs/src/content/devtime/starter-next-js.json +++ b/packages/docs/src/content/devtime/starter-next-js.json @@ -407,4 +407,4 @@ "web.url-search-params.has", "web.url-search-params.size" ] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/devtime/starter-nuxt.json b/packages/docs/src/content/devtime/starter-nuxt.json index b4010b9c..60c03a0a 100644 --- a/packages/docs/src/content/devtime/starter-nuxt.json +++ b/packages/docs/src/content/devtime/starter-nuxt.json @@ -354,4 +354,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/devtime/starter-react-router.json b/packages/docs/src/content/devtime/starter-react-router.json index f5e310bf..8c833000 100644 --- a/packages/docs/src/content/devtime/starter-react-router.json +++ b/packages/docs/src/content/devtime/starter-react-router.json @@ -84,4 +84,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/devtime/starter-solid-start.json b/packages/docs/src/content/devtime/starter-solid-start.json index 722fb91c..3c78d7dd 100644 --- a/packages/docs/src/content/devtime/starter-solid-start.json +++ b/packages/docs/src/content/devtime/starter-solid-start.json @@ -434,4 +434,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/devtime/starter-sveltekit.json b/packages/docs/src/content/devtime/starter-sveltekit.json index 8cdc5130..7fa380d2 100644 --- a/packages/docs/src/content/devtime/starter-sveltekit.json +++ b/packages/docs/src/content/devtime/starter-sveltekit.json @@ -44,4 +44,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/devtime/starter-tanstack-start-react.json b/packages/docs/src/content/devtime/starter-tanstack-start-react.json index 01177a1f..f844bb2f 100644 --- a/packages/docs/src/content/devtime/starter-tanstack-start-react.json +++ b/packages/docs/src/content/devtime/starter-tanstack-start-react.json @@ -109,4 +109,4 @@ ], "vendoredCoreJsSize": 0, "vendoredCoreJsUnnecessaryModules": [] -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-astro.json b/packages/docs/src/content/runtime/app-astro.json index 8f5ee1ca..e4e6eeb3 100644 --- a/packages/docs/src/content/runtime/app-astro.json +++ b/packages/docs/src/content/runtime/app-astro.json @@ -23,4 +23,4 @@ "inpMs": 11.03, "runs": 5 } -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-baseline-html.json b/packages/docs/src/content/runtime/app-baseline-html.json index 001258f6..cc71add6 100644 --- a/packages/docs/src/content/runtime/app-baseline-html.json +++ b/packages/docs/src/content/runtime/app-baseline-html.json @@ -12,4 +12,4 @@ "order": 0, "timingMeasuredAt": "2026-06-20T23:33:09.957Z", "runner": "ubuntu-latest" -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-mastro.json b/packages/docs/src/content/runtime/app-mastro.json index 87374fc3..16269329 100644 --- a/packages/docs/src/content/runtime/app-mastro.json +++ b/packages/docs/src/content/runtime/app-mastro.json @@ -12,4 +12,4 @@ "ssrBodySizeKb": 181.95, "ssrDuplicationFactor": 1, "order": 2 -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-next-js.json b/packages/docs/src/content/runtime/app-next-js.json index 12877c64..4c4fa58d 100644 --- a/packages/docs/src/content/runtime/app-next-js.json +++ b/packages/docs/src/content/runtime/app-next-js.json @@ -23,4 +23,4 @@ "inpMs": 16.89, "runs": 5 } -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-nuxt.json b/packages/docs/src/content/runtime/app-nuxt.json index 93635363..2d63d6c6 100644 --- a/packages/docs/src/content/runtime/app-nuxt.json +++ b/packages/docs/src/content/runtime/app-nuxt.json @@ -23,4 +23,4 @@ "inpMs": 9.05, "runs": 5 } -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-react-router.json b/packages/docs/src/content/runtime/app-react-router.json index 96ee0ac7..fc60605f 100644 --- a/packages/docs/src/content/runtime/app-react-router.json +++ b/packages/docs/src/content/runtime/app-react-router.json @@ -23,4 +23,4 @@ "inpMs": 18.41, "runs": 5 } -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-solid-start.json b/packages/docs/src/content/runtime/app-solid-start.json index 101f7671..9ce8ca8a 100644 --- a/packages/docs/src/content/runtime/app-solid-start.json +++ b/packages/docs/src/content/runtime/app-solid-start.json @@ -23,4 +23,4 @@ "inpMs": 16.21, "runs": 5 } -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-sveltekit.json b/packages/docs/src/content/runtime/app-sveltekit.json index 2ac76345..b264cb75 100644 --- a/packages/docs/src/content/runtime/app-sveltekit.json +++ b/packages/docs/src/content/runtime/app-sveltekit.json @@ -23,4 +23,4 @@ "inpMs": 14.19, "runs": 5 } -} \ No newline at end of file +} diff --git a/packages/docs/src/content/runtime/app-tanstack-start-react.json b/packages/docs/src/content/runtime/app-tanstack-start-react.json index 54b85105..413cc155 100644 --- a/packages/docs/src/content/runtime/app-tanstack-start-react.json +++ b/packages/docs/src/content/runtime/app-tanstack-start-react.json @@ -23,4 +23,4 @@ "inpMs": 28.14, "runs": 5 } -} \ No newline at end of file +}