Skip to content

Commit 08e3465

Browse files
authored
Bundle analyzer (#843)
* Initialize bundle analyzer package * update bundle baseline
1 parent 729aa6e commit 08e3465

13 files changed

Lines changed: 1589 additions & 10 deletions
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Bundle Analysis
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- packages/layerchart/**
7+
- bundle-analyzer/**
8+
types: [opened, synchronize, reopened]
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.event.number }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
bundle-analysis:
16+
name: Bundle Size Analysis
17+
runs-on: ubuntu-latest
18+
19+
permissions:
20+
contents: read
21+
pull-requests: write
22+
23+
steps:
24+
- uses: actions/checkout@v4
25+
with:
26+
fetch-depth: 0
27+
- uses: pnpm/action-setup@v4
28+
- uses: actions/setup-node@v4
29+
with:
30+
node-version: 20
31+
cache: pnpm
32+
33+
- name: Install dependencies
34+
run: pnpm install --frozen-lockfile
35+
36+
- name: Build packages
37+
run: pnpm build:packages
38+
39+
- name: Run bundle analysis on PR
40+
run: pnpm bundle:analyze
41+
42+
- name: Save PR bundle report
43+
run: |
44+
mkdir -p /tmp/bundle-analysis
45+
cp ./bundle-analyzer/bundle-reports/latest.json /tmp/bundle-analysis/pr-current.json
46+
47+
- name: Get target branch bundle report
48+
id: check-target-bundle
49+
run: |
50+
if git cat-file -e origin/${{ github.base_ref }}:bundle-analyzer/bundle-reports/latest.json 2>/dev/null; then
51+
echo "exists=true" >> $GITHUB_OUTPUT
52+
git show origin/${{ github.base_ref }}:bundle-analyzer/bundle-reports/latest.json > /tmp/bundle-analysis/target-baseline.json
53+
else
54+
echo "exists=false" >> $GITHUB_OUTPUT
55+
fi
56+
57+
- name: Generate target baseline if missing
58+
if: steps.check-target-bundle.outputs.exists == 'false'
59+
run: |
60+
git stash push -m "temp stash for baseline generation" || true
61+
git checkout origin/${{ github.base_ref }}
62+
pnpm install --frozen-lockfile
63+
pnpm build:packages
64+
pnpm bundle:analyze || echo "Bundle analysis failed on target branch"
65+
if [ -f "./bundle-analyzer/bundle-reports/latest.json" ]; then
66+
cp ./bundle-analyzer/bundle-reports/latest.json /tmp/bundle-analysis/target-baseline.json
67+
else
68+
echo '{"timestamp":"","results":[]}' > /tmp/bundle-analysis/target-baseline.json
69+
fi
70+
git checkout ${{ github.head_ref }}
71+
git stash pop || true
72+
73+
- name: Generate bundle comparison comment
74+
run: |
75+
node ./bundle-analyzer/generate-pr-comment.js \
76+
/tmp/bundle-analysis/pr-current.json \
77+
/tmp/bundle-analysis/target-baseline.json
78+
79+
- name: Find existing comment
80+
uses: peter-evans/find-comment@v3
81+
id: existing-comment
82+
with:
83+
issue-number: ${{ github.event.pull_request.number }}
84+
comment-author: "github-actions[bot]"
85+
body-includes: "## Bundle Size Analysis"
86+
87+
- name: Update or create comment
88+
uses: peter-evans/create-or-update-comment@v4
89+
with:
90+
comment-id: ${{ steps.existing-comment.outputs.comment-id }}
91+
issue-number: ${{ github.event.pull_request.number }}
92+
body-path: /tmp/bundle-analysis/comment.md
93+
edit-mode: replace
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: Update Bundle Baseline
2+
3+
on:
4+
push:
5+
branches:
6+
- next
7+
paths:
8+
- packages/layerchart/**
9+
- bundle-analyzer/**
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
update-baseline:
17+
name: Update Bundle Size Baseline
18+
runs-on: ubuntu-latest
19+
20+
permissions:
21+
contents: write
22+
pull-requests: write
23+
24+
steps:
25+
- uses: actions/checkout@v4
26+
with:
27+
token: ${{ secrets.GITHUB_TOKEN }}
28+
29+
- uses: pnpm/action-setup@v4
30+
31+
- uses: actions/setup-node@v4
32+
with:
33+
node-version: 20
34+
cache: pnpm
35+
36+
- name: Install dependencies
37+
run: pnpm install --frozen-lockfile
38+
39+
- name: Build packages
40+
run: pnpm build:packages
41+
42+
- name: Run bundle analysis
43+
run: pnpm bundle:analyze
44+
45+
- name: Check if baseline changed
46+
id: check-changes
47+
run: |
48+
if [ ! -f "./bundle-analyzer/bundle-reports/latest.json" ]; then
49+
echo "No latest.json generated"
50+
echo "changed=false" >> $GITHUB_OUTPUT
51+
exit 0
52+
fi
53+
54+
if git cat-file -e HEAD:bundle-analyzer/bundle-reports/latest.json 2>/dev/null; then
55+
OLD_FILE=$(git show HEAD:bundle-analyzer/bundle-reports/latest.json)
56+
NEW_FILE=$(cat ./bundle-analyzer/bundle-reports/latest.json)
57+
58+
# Remove timestamp fields and compare
59+
OLD_NO_TS=$(echo "$OLD_FILE" | jq 'del(.timestamp)')
60+
NEW_NO_TS=$(echo "$NEW_FILE" | jq 'del(.timestamp)')
61+
62+
if [ "$OLD_NO_TS" = "$NEW_NO_TS" ]; then
63+
echo "Only timestamp changed"
64+
echo "changed=false" >> $GITHUB_OUTPUT
65+
else
66+
echo "Bundle data changed"
67+
echo "changed=true" >> $GITHUB_OUTPUT
68+
fi
69+
else
70+
echo "First baseline"
71+
echo "changed=true" >> $GITHUB_OUTPUT
72+
fi
73+
74+
- name: Create or update bundle baseline PR
75+
if: steps.check-changes.outputs.changed == 'true'
76+
uses: peter-evans/create-pull-request@v7
77+
with:
78+
token: ${{ secrets.GITHUB_TOKEN }}
79+
branch: chore/update-bundle-baseline
80+
title: "chore: update bundle size baseline"
81+
body: |
82+
## Bundle Size Baseline Update
83+
84+
This PR updates the bundle size baseline after recent changes to the `next` branch.
85+
86+
### What changed?
87+
- Updated `bundle-analyzer/bundle-reports/latest.json` with current scenario sizes
88+
- This serves as the baseline for future bundle size comparisons in PRs
89+
90+
### How to review
91+
1. Check that the bundle size changes align with recent merged PRs
92+
2. Verify no unexpected size increases
93+
3. Merge when satisfied with the baseline update
94+
95+
_This PR was automatically created by the bundle analysis workflow._
96+
commit-message: "chore: update bundle size baseline"
97+
add-paths: |
98+
bundle-analyzer/bundle-reports/latest.json
99+
delete-branch: true
100+
author: "github-actions[bot] <action@github.com>"
101+
committer: "github-actions[bot] <action@github.com>"
102+
base: next

bundle-analyzer/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.temp-bundle-analysis/
2+
bundle-reports/bundle-report-*.json

bundle-analyzer/README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Bundle Analyzer
2+
3+
Measures bundle sizes for layerchart across different use-case scenarios, helping track the cost of common chart configurations and detect regressions over time.
4+
5+
## Quick start
6+
7+
```bash
8+
# Build layerchart first (required)
9+
pnpm --filter layerchart package
10+
11+
# Run analysis on all scenarios
12+
pnpm bundle:analyze
13+
14+
# Compare two reports
15+
pnpm bundle:compare -- path/to/new.json path/to/old.json
16+
```
17+
18+
## How it works
19+
20+
For each scenario, the analyzer:
21+
22+
1. Creates a temporary entry file that imports the specified components from `layerchart`
23+
2. Builds it with Vite + esbuild (minified, tree-shaken, Svelte externalized)
24+
3. Measures the resulting bundle size (raw + gzipped)
25+
4. Saves results to `bundle-reports/latest.json`
26+
27+
Svelte runtime is excluded from measurements since it's shared across all components. The reported sizes reflect layerchart code + its dependencies (d3, dagre, etc.).
28+
29+
## Scenarios
30+
31+
Scenarios are defined in [`define-scenarios.ts`](./define-scenarios.ts) and represent real-world usage patterns:
32+
33+
| Scenario | Description |
34+
|----------|-------------|
35+
| `core` | Bare minimum: `Chart` + `Svg` |
36+
| `line-chart` | Line chart with axes and grid |
37+
| `line-chart-interactive` | Line chart with tooltip and highlight |
38+
| `area-chart` | Area chart with axes |
39+
| `bar-chart` | Bar chart with axes |
40+
| `scatter-chart` | Scatter plot with points |
41+
| `pie-chart` | Pie/donut chart with arcs |
42+
| `high-level-charts` | All high-level chart components |
43+
| `geo` | Geographic map with paths |
44+
| `geo-tiles` | Geographic map with tile layer |
45+
| `geo-full` | All geo components |
46+
| `force` | Force-directed graph |
47+
| `hierarchy-tree` | Tree layout |
48+
| `hierarchy-treemap` | Treemap layout |
49+
| `hierarchy-pack` | Circle packing |
50+
| `dagre` | Dagre directed graph |
51+
| `sankey` | Sankey flow diagram |
52+
| `chord` | Chord diagram |
53+
| `canvas` | Canvas-based rendering |
54+
| `all` | Everything from layerchart |
55+
56+
## CLI options
57+
58+
```bash
59+
# Analyze all use-case scenarios (default)
60+
pnpm bundle:analyze
61+
62+
# Also measure individual components
63+
pnpm bundle:analyze -- --components
64+
65+
# Analyze specific scenarios or components by name
66+
pnpm bundle:analyze -- geo dagre
67+
68+
# Compare two report files
69+
pnpm bundle:compare -- report-new.json report-old.json
70+
```
71+
72+
## CI integration
73+
74+
Two GitHub Actions workflows automate bundle tracking:
75+
76+
- **`bundle-analysis.yml`** - Runs on PRs that touch `packages/layerchart/` or `bundle-analyzer/`. Posts a comment comparing bundle sizes against the baseline.
77+
- **`update-bundle-baseline.yml`** - Runs on pushes to `next`. If sizes changed, opens a PR to update `bundle-reports/latest.json`.
78+
79+
## Adding scenarios
80+
81+
Edit [`define-scenarios.ts`](./define-scenarios.ts) to add new scenarios to the `scenarios` array:
82+
83+
```ts
84+
{
85+
name: "my-scenario",
86+
description: "What this scenario represents",
87+
imports: ["Chart", "Svg", "MyComponent"],
88+
}
89+
```
90+
91+
## Output
92+
93+
Reports are saved to `bundle-reports/`:
94+
- `latest.json` - Current baseline (committed to git)
95+
- `bundle-report-{timestamp}.json` - Timestamped snapshots (gitignored)

0 commit comments

Comments
 (0)