Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 82 additions & 53 deletions .github/workflows/generate-stats.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,88 @@ jobs:
with:
node-version: '24'

- name: Measure ${{ matrix.framework.displayName }} install time
- name: Measure ${{ matrix.framework.displayName }} install time and sizes
run: |
cp -r packages/${{ matrix.framework.package }} /tmp/framework-test
cd /tmp/framework-test

# Measure install time (full install with dev dependencies)
START=$(date +%s%N)
pnpm install
END=$(date +%s%N)
ELAPSED=$((($END - $START) / 1000000))
echo "${{ matrix.framework.displayName }} install time: ${ELAPSED}ms"
echo "$ELAPSED" > install-time.txt
INSTALL_TIME=$((($END - $START) / 1000000))

# Measure node_modules size (in bytes)
SIZE=$(du -sb node_modules | cut -f1)

# Clean and do production-only install
rm -rf node_modules
pnpm install --prod

# Measure node_modules size production only (in bytes)
SIZE_PROD_ONLY=$(du -sb node_modules | cut -f1)

- name: Upload install time result
echo "${{ matrix.framework.displayName }} - Install: ${INSTALL_TIME}ms, Size: ${SIZE} bytes, Prod only: ${SIZE_PROD_ONLY} bytes"
echo "{\"installTimeMs\": $INSTALL_TIME, \"nodeModulesSize\": $SIZE, \"nodeModulesSizeProdOnly\": $SIZE_PROD_ONLY}" > install-stats.json

- name: Upload install stats result
uses: actions/upload-artifact@v4
with:
name: install-time-${{ matrix.framework.name }}
path: /tmp/framework-test/install-time.txt
name: install-stats-${{ matrix.framework.name }}
path: /tmp/framework-test/install-stats.json
retention-days: 1

# Build and other measurements + generate stats
# Build time measurements - parallel on fresh runners for fair comparison
measure-build:
needs: setup
if: needs.setup.outputs.build-matrix != '[]'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
framework: ${{ fromJson(needs.setup.outputs.build-matrix) }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Measure ${{ matrix.framework.displayName }} build time
run: |
cp -r packages/${{ matrix.framework.package }} /tmp/framework-test
cd /tmp/framework-test
pnpm install

START=$(date +%s%N)
pnpm build
END=$(date +%s%N)
COLD=$((($END - $START) / 1000000))

START=$(date +%s%N)
pnpm build
END=$(date +%s%N)
WARM=$((($END - $START) / 1000000))

echo "${{ matrix.framework.displayName }} - Cold: ${COLD}ms, Warm: ${WARM}ms"
echo "{\"cold\": $COLD, \"warm\": $WARM}" > build-time.json

- name: Upload build time result
uses: actions/upload-artifact@v4
with:
name: build-time-${{ matrix.framework.name }}
path: /tmp/framework-test/build-time.json
retention-days: 1

# Collect measurements + generate stats
generate-stats:
needs: [setup, measure-install]
if: always() && needs.setup.result == 'success'
needs: [setup, measure-install, measure-build]
if: always() && needs.setup.result == 'success' && (needs.measure-install.result == 'success' || needs.measure-build.result == 'success')
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -94,67 +154,36 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Download install time artifacts
if: needs.setup.outputs.install-matrix != '[]' && needs.measure-install.result == 'success'
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: install-time-*

- name: Measure build times
if: needs.setup.outputs.build-matrix != '[]'
run: |
BUILD_FRAMEWORKS=$(cat .github/frameworks.json | jq -c '[.[] | select(.measurements | contains(["build"]))]')

echo "$BUILD_FRAMEWORKS" | jq -c '.[]' | while read -r framework; do
NAME=$(echo "$framework" | jq -r '.name')
DISPLAY_NAME=$(echo "$framework" | jq -r '.displayName')
BUILD_SCRIPT=$(echo "$framework" | jq -r '.buildScript')

START=$(date +%s%N)
pnpm $BUILD_SCRIPT
END=$(date +%s%N)
COLD=$((($END - $START) / 1000000))

START=$(date +%s%N)
pnpm $BUILD_SCRIPT
END=$(date +%s%N)
WARM=$((($END - $START) / 1000000))

echo "$DISPLAY_NAME - Cold: ${COLD}ms, Warm: ${WARM}ms"
echo "{\"cold\": $COLD, \"warm\": $WARM}" > "/tmp/$NAME-build-time.json"
done

- name: Save CI stats
run: |
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
FRAMEWORKS=$(cat .github/frameworks.json)
BASE_STATS=$(jq -n --arg ts "$TIMESTAMP" '{timingMeasuredAt: $ts, runner: "ubuntu-latest"}')

echo "$FRAMEWORKS" | jq -c '.[]' | while read -r framework; do
jq -c '.[]' .github/frameworks.json | while read -r framework; do
NAME=$(echo "$framework" | jq -r '.name')
PACKAGE=$(echo "$framework" | jq -r '.package')
DISPLAY_NAME=$(echo "$framework" | jq -r '.displayName')
MEASUREMENTS=$(echo "$framework" | jq -r '.measurements | join(",")')

JSON="{ \"timingMeasuredAt\": \"$TIMESTAMP\", \"runner\": \"ubuntu-latest\""
STATS="$BASE_STATS"

if [[ "$MEASUREMENTS" == *"install"* ]] && [[ -f "artifacts/install-time-$NAME/install-time.txt" ]]; then
INSTALL_TIME=$(cat "artifacts/install-time-$NAME/install-time.txt")
JSON="$JSON, \"installTimeMs\": $INSTALL_TIME"
INSTALL_FILE="artifacts/install-stats-$NAME/install-stats.json"
if [[ -f "$INSTALL_FILE" ]]; then
STATS=$(echo "$STATS" | jq --slurpfile install "$INSTALL_FILE" '. + $install[0]')
fi

if [[ "$MEASUREMENTS" == *"build"* ]] && [[ -f "/tmp/$NAME-build-time.json" ]]; then
BUILD_DATA=$(cat "/tmp/$NAME-build-time.json")
COLD=$(echo "$BUILD_DATA" | jq -r '.cold')
WARM=$(echo "$BUILD_DATA" | jq -r '.warm')
JSON="$JSON, \"coldBuildTimeMs\": $COLD, \"warmBuildTimeMs\": $WARM"
BUILD_FILE="artifacts/build-time-$NAME/build-time.json"
if [[ -f "$BUILD_FILE" ]]; then
STATS=$(echo "$STATS" | jq --slurpfile build "$BUILD_FILE" '. + {coldBuildTimeMs: $build[0].cold, warmBuildTimeMs: $build[0].warm}')
fi

JSON="$JSON }"

echo "$DISPLAY_NAME stats: $JSON"
echo "$DISPLAY_NAME stats: $STATS"
mkdir -p "packages/$PACKAGE"
echo "$JSON" | jq '.' > "packages/$PACKAGE/.ci-stats.json"
echo "$STATS" > "packages/$PACKAGE/.ci-stats.json"
done

- name: Generate stats
Expand Down
12 changes: 12 additions & 0 deletions packages/docs/src/components/DependencyStats.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ import { getCollection } from 'astro:content'

const statsEntries = await getCollection('stats')
const stats = statsEntries.map((entry) => entry.data)

const BYTES_PER_KB = 1024
const BYTES_PER_MB = BYTES_PER_KB * BYTES_PER_KB

function formatBytesToMB(bytes: number): string {
const mb = bytes / BYTES_PER_MB
return `${mb.toFixed(2)}MB`
}
---

<div id="container">
Expand Down Expand Up @@ -35,6 +43,8 @@ const stats = statsEntries.map((entry) => entry.data)
<th scope="col">Framework</th>
<th scope="col">Prod Deps</th>
<th scope="col">Dev Deps</th>
<th scope="col">Size</th>
<th scope="col">Size (Prod Only)</th>
<th scope="col">Graph</th>
</tr>
</thead>
Expand All @@ -45,6 +55,8 @@ const stats = statsEntries.map((entry) => entry.data)
<td class="framework-name">{framework.name}</td>
<td>{framework.prodDependencies}</td>
<td>{framework.devDependencies}</td>
<td>{formatBytesToMB(framework.nodeModulesSize)}</td>
<td>{formatBytesToMB(framework.nodeModulesSizeProdOnly)}</td>
<td>
<a
href={`https://npmgraph.js.org/?q=https://github.com/e18e/framework-tracker/blob/main/packages/${framework.package}/package.json`}
Expand Down
2 changes: 2 additions & 0 deletions packages/docs/src/content/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const statsCollection = defineCollection({
installTimeMs: z.number(),
coldBuildTimeMs: z.number(),
warmBuildTimeMs: z.number(),
nodeModulesSize: z.number(),
nodeModulesSizeProdOnly: z.number(),
timingMeasuredAt: z.string(),
runner: z.string(),
}),
Expand Down
4 changes: 3 additions & 1 deletion packages/docs/src/content/stats/starter-astro.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
"runner": "ubuntu-latest",
"installTimeMs": 3041,
"coldBuildTimeMs": 2479,
"warmBuildTimeMs": 2422
"warmBuildTimeMs": 2422,
"nodeModulesSize": 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

presumably these 0 will then later be updated by the CI run?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100%

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As soon as we merge it

"nodeModulesSizeProdOnly": 0
}
4 changes: 3 additions & 1 deletion packages/docs/src/content/stats/starter-next-js.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
"coldBuildTimeMs": 7396,
"warmBuildTimeMs": 7247,
"timingMeasuredAt": "2026-01-24T09:51:21Z",
"runner": "ubuntu-latest"
"runner": "ubuntu-latest",
"nodeModulesSize": 0,
"nodeModulesSizeProdOnly": 0
}
4 changes: 3 additions & 1 deletion packages/docs/src/content/stats/starter-nuxt.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
"coldBuildTimeMs": 6582,
"warmBuildTimeMs": 6370,
"timingMeasuredAt": "2026-01-24T09:51:21Z",
"runner": "ubuntu-latest"
"runner": "ubuntu-latest",
"nodeModulesSize": 0,
"nodeModulesSizeProdOnly": 0
}
4 changes: 3 additions & 1 deletion packages/docs/src/content/stats/starter-react-router.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
"coldBuildTimeMs": 2983,
"warmBuildTimeMs": 3007,
"timingMeasuredAt": "2026-01-24T09:51:21Z",
"runner": "ubuntu-latest"
"runner": "ubuntu-latest",
"nodeModulesSize": 0,
"nodeModulesSizeProdOnly": 0
}
4 changes: 3 additions & 1 deletion packages/docs/src/content/stats/starter-sveltekit.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
"coldBuildTimeMs": 4421,
"warmBuildTimeMs": 4116,
"timingMeasuredAt": "2026-01-24T09:51:21Z",
"runner": "ubuntu-latest"
"runner": "ubuntu-latest",
"nodeModulesSize": 0,
"nodeModulesSizeProdOnly": 0
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
"coldBuildTimeMs": 9738,
"warmBuildTimeMs": 9273,
"timingMeasuredAt": "2026-01-24T09:51:21Z",
"runner": "ubuntu-latest"
"runner": "ubuntu-latest",
"nodeModulesSize": 0,
"nodeModulesSizeProdOnly": 0
}
2 changes: 1 addition & 1 deletion packages/starter-astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"astro": "astro",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"type-check": "tsc --noEmit"
"type-check": "astro check"
},
"dependencies": {
"astro": "^5.16.15"
Expand Down
2 changes: 2 additions & 0 deletions packages/stats-generator/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface CIStats {
coldBuildTimeMs?: number
warmBuildTimeMs?: number
testTimeMs?: number
nodeModulesSize?: number
nodeModulesSizeProdOnly?: number
timingMeasuredAt?: string
runner?: string
}
Expand Down