Skip to content

Commit d4cff54

Browse files
andrepimentaclaude
andcommitted
fix(performance): replace npx with yarn; trim to installed/Expo-native tools
Addresses PR review (supply-chain risk from npx): - react-native / expo: already deps -> `yarn <tool>` (no npx). - expo-atlas: kept as the one bundle analyzer; install via `yarn expo install` (SDK-pinned) + `EXPO_ATLAS=1 yarn expo export` + `yarn expo-atlas`. Never npx. - Excluded source-map-explorer, bundle-stats, bundle-phobia-cli (use bundlephobia.com), and react-compiler-healthcheck (use the installed ESLint react-compiler plugin instead) — none are installed and they overlap. - Rewrote bundle-analyze-js.md around Expo Atlas, dropping the source-map-explorer, bundle-stats, and Re.Pack/webpack sections (not used in this Metro/Expo app). No remaining `npx` except an explicit "don't npx" note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 4818249 commit d4cff54

8 files changed

Lines changed: 80 additions & 215 deletions

File tree

domains/performance/skills/performance/references/bundle-analyze-js.md

Lines changed: 44 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,79 @@
11
---
22
title: Analyze JS Bundle Size
33
impact: CRITICAL
4-
tags: bundle, analysis, source-map-explorer, expo-atlas
4+
tags: bundle, analysis, expo-atlas
55
---
66

77
# Skill: Analyze JS Bundle Size
88

9-
Use source-map-explorer and Expo Atlas to visualize what's in your JavaScript bundle.
9+
Use **Expo Atlas** to visualize what's in your JavaScript bundle. metamask-mobile
10+
is an Expo project (Metro bundler), and Atlas is the Expo-first-party analyzer —
11+
its hooks are built into the Expo CLI, so you enable it on the `expo export` you
12+
already run.
1013

1114
## Quick Command
1215

1316
```bash
14-
# React Native CLI
15-
npx react-native bundle \
16-
--entry-file index.js \
17-
--bundle-output output.js \
18-
--platform ios \
19-
--sourcemap-output output.js.map \
20-
--dev false --minify true && \
21-
npx source-map-explorer output.js --no-border-checks
22-
23-
# Expo
24-
EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios && npx expo-atlas
17+
# One-time: add the Atlas viewer (Expo pins the SDK-compatible version)
18+
yarn expo install expo-atlas
19+
20+
# Emit bundle data, then open the treemap viewer
21+
EXPO_ATLAS=1 yarn expo export --platform ios
22+
yarn expo-atlas
2523
```
2624

25+
> Don't `npx expo-atlas``npx` fetches and runs an unpinned remote version on
26+
> every call. `yarn expo install` pins the SDK-compatible version once.
27+
2728
## When to Use
2829

2930
- JS bundle seems too large
3031
- Want to identify heavy dependencies
3132
- Investigating startup time issues
3233
- Before/after optimization comparison
3334

34-
> **Note**: This skill involves visual treemap output (source-map-explorer, Expo Atlas). When regression checks include device flows, use `agent-device` for app evidence; install it through the environment's approved/trusted path or ask the user if verification needs it and it is missing. Treemap analysis itself may still require exported reports, browser screenshots, or human review.
35+
> **Note**: Atlas produces a visual treemap (browser UI). Treemap analysis may
36+
> require exported reports, browser screenshots, or human review.
3537
3638
## Understanding Hermes Bytecode
3739

3840
Modern React Native (0.70+) uses Hermes bytecode, not raw JavaScript:
41+
3942
- Skips parsing at runtime
4043
- Still benefits from smaller bundles
4144
- Heavy imports still execute on startup
4245

4346
**Impact of bundle size:**
47+
4448
- Larger bytecode = longer download from store
4549
- More imports on init path = slower TTI
4650

47-
## Method 1: source-map-explorer
51+
## Expo Atlas
4852

49-
### Generate Bundle with Source Map
53+
The Atlas **data emission** is built into the Expo CLI (the `EXPO_ATLAS` env var
54+
on `expo export` / `expo start`); the `expo-atlas` package provides the
55+
**viewer**. Add it once with `yarn expo install expo-atlas`.
5056

51-
**React Native CLI:**
57+
### Generate + view
5258

5359
```bash
54-
npx react-native bundle \
55-
--entry-file index.js \
56-
--bundle-output output.js \
57-
--platform ios \
58-
--sourcemap-output output.js.map \
59-
--dev false \
60-
--minify true
61-
```
60+
# Export with Atlas enabled
61+
EXPO_ATLAS=1 yarn expo export --platform ios
6262

63-
**Expo (SDK 51+):**
63+
# Or while running the dev server
64+
EXPO_ATLAS=1 yarn expo start
6465

65-
```bash
66-
npx expo export --platform ios --source-maps --output-dir dist
67-
# Bundle at: dist/ios/_expo/static/js/ios/*.js
68-
# Source map at: dist/ios/_expo/static/js/ios/*.map
66+
# Open the treemap viewer (reads .expo/atlas.jsonl)
67+
yarn expo-atlas
6968
```
7069

71-
### Analyze
72-
73-
```bash
74-
npx source-map-explorer output.js --no-border-checks
75-
```
76-
77-
**Note**: `--no-border-checks` needed due to Metro's non-standard source maps.
78-
79-
Opens browser with treemap visualization:
80-
81-
![Bundle Treemap from source-map-explorer](images/bundle-treemap-source-map-explorer.png)
82-
83-
The treemap shows:
84-
- **Hierarchy**: `node_modules/``react-native/``Libraries/` → individual files
85-
- **Size**: Box area proportional to file size (KB shown in labels)
86-
- **Major components visible**:
87-
- `react-native` (724.18 KB, 80.5%)
88-
- `Renderer` (208.44 KB) - ReactNativeRenderer-prod.js, ReactFabric-prod.js
89-
- `Components` (125.29 KB) - Touchable, ScrollView, etc.
90-
- `Animated` (79.48 KB) - Animation system
91-
- `virtualized-lists` (57.57 KB) - FlatList internals
92-
93-
Click on any section to drill down into that directory.
94-
95-
**Limitation**: May lose ~30% info due to mapping issues.
96-
97-
## Method 2: Expo Atlas
98-
99-
More accurate for Expo projects (or with workaround for bare RN).
100-
101-
### For Expo Projects
102-
103-
```bash
104-
# Start with Atlas enabled
105-
EXPO_UNSTABLE_ATLAS=true npx expo start --no-dev
106-
107-
# Or export
108-
EXPO_UNSTABLE_ATLAS=true npx expo export
109-
```
110-
111-
Then launch UI:
112-
113-
```bash
114-
npx expo-atlas
115-
```
70+
(The older env var name `EXPO_UNSTABLE_ATLAS=true` still works on some versions.)
11671

11772
![Expo Atlas Treemap](images/expo-atlas-treemap.png)
11873

119-
Expo Atlas provides more accurate visualization for Expo projects, with similar treemap interface showing module sizes and dependencies.
120-
121-
### For Non-Expo Projects
122-
123-
Use `expo-atlas-without-expo` package.
124-
125-
## Method 3: Re.Pack Bundle Analysis (Webpack/Rspack)
126-
127-
If using Re.Pack:
128-
129-
### webpack-bundle-analyzer
130-
131-
```bash
132-
rspack build --analyze
133-
```
134-
135-
### bundle-stats / statoscope
136-
137-
```bash
138-
# Generate stats
139-
npx react-native bundle \
140-
--platform android \
141-
--entry-file index.js \
142-
--dev false \
143-
--minify true \
144-
--json stats.json
145-
146-
# Analyze
147-
npx bundle-stats --html --json stats.json
148-
```
149-
150-
### Rsdoctor
151-
152-
```javascript
153-
// rspack.config.js
154-
const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin');
155-
156-
module.exports = {
157-
plugins: [
158-
process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
159-
].filter(Boolean),
160-
};
161-
```
162-
163-
Run with:
164-
165-
```bash
166-
RSDOCTOR=true npx react-native start
167-
```
74+
The treemap shows module sizes and dependencies — box area is proportional to
75+
size. Drill into `node_modules/` to find the heaviest packages (e.g.
76+
`react-native` core, renderer, `Animated`, `virtualized-lists`).
16877

16978
## What to Look For
17079

@@ -173,17 +82,17 @@ RSDOCTOR=true npx react-native start
17382
| Finding | Problem | Solution |
17483
|---------|---------|----------|
17584
| Entire library imported | Barrel exports | Use direct imports |
176-
| Duplicate packages | Multiple versions | Dedupe in package.json |
85+
| Duplicate packages | Multiple versions | Dedupe in `package.json` |
17786
| Dev dependencies in bundle | Incorrect imports | Check conditional imports |
178-
| Large polyfills | Unnecessary for Hermes | Remove (see native-sdks-over-polyfills.md) |
179-
| Moment.js with locales | Bloated date library | Switch to date-fns or dayjs |
87+
| Large polyfills | Unnecessary for Hermes | Remove (see [native-sdks-over-polyfills.md](./native-sdks-over-polyfills.md)) |
88+
| Moment.js with locales | Bloated date library | Switch to `dayjs` (already installed) |
18089

18190
### Common Offenders
18291

183-
- **Lodash full import**: Use `lodash-es` or specific imports
184-
- **Moment.js**: Replace with `date-fns` or `dayjs`
185-
- **Intl polyfills**: Check Hermes API and method coverage before removing them
186-
- **AWS SDK**: Import specific services only
92+
- **Lodash full import**: use specific submodule imports (`import x from 'lodash/x'`)
93+
- **Moment.js**: replace with `dayjs` (already installed)
94+
- **Intl polyfills**: check Hermes API and method coverage before removing them
95+
- **AWS SDK**: import specific services only
18796

18897
## Code Examples
18998

@@ -192,68 +101,17 @@ RSDOCTOR=true npx react-native start
192101
```tsx
193102
// BAD: Imports entire library through barrel
194103
import { format } from 'date-fns';
195-
196-
// In bundle: All of date-fns loaded
104+
// In bundle: all of date-fns loaded
197105

198106
// GOOD: Direct import
199107
import format from 'date-fns/format';
200-
201-
// In bundle: Only format function
108+
// In bundle: only the format function
202109
```
203110

204111
## Comparing Bundles
205112

206-
### source-map-explorer
207-
208-
```bash
209-
# Generate baseline
210-
npx react-native bundle ... --bundle-output baseline.js --sourcemap-output baseline.js.map
211-
212-
# Make changes, generate new bundle
213-
npx react-native bundle ... --bundle-output current.js --sourcemap-output current.js.map
214-
215-
# Compare manually in browser
216-
```
217-
218-
### Re.Pack (automated)
219-
220-
```bash
221-
npx bundle-stats compare baseline-stats.json current-stats.json
222-
```
223-
224-
## Quick Commands
225-
226-
**React Native CLI:**
227-
228-
```bash
229-
# iOS bundle analysis
230-
npx react-native bundle \
231-
--entry-file index.js \
232-
--bundle-output ios-bundle.js \
233-
--platform ios \
234-
--sourcemap-output ios-bundle.js.map \
235-
--dev false \
236-
--minify true && \
237-
npx source-map-explorer ios-bundle.js --no-border-checks
238-
239-
# Android bundle analysis
240-
npx react-native bundle \
241-
--entry-file index.js \
242-
--bundle-output android-bundle.js \
243-
--platform android \
244-
--sourcemap-output android-bundle.js.map \
245-
--dev false \
246-
--minify true && \
247-
npx source-map-explorer android-bundle.js --no-border-checks
248-
```
249-
250-
**Expo:**
251-
252-
```bash
253-
# Use Expo Atlas (recommended for Expo projects)
254-
EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios
255-
npx expo-atlas
256-
```
113+
Run Atlas before and after a change (or on two branches) and compare the
114+
treemaps — the heaviest boxes that grew/shrank show where the change landed.
257115

258116
## Related Skills
259117

domains/performance/skills/performance/references/bundle-library-size.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@ Evaluate third-party library size impact before adding to your project.
1717
## Quick Command
1818

1919
```bash
20-
# Check size before installing
20+
# Check size before installing — no install needed:
2121
# Visit: https://bundlephobia.com/package/[package-name]
22-
23-
# Or use CLI
24-
npx bundle-phobia-cli <package-name>
2522
```
2623

2724
## When to Use
@@ -142,14 +139,14 @@ import get from 'lodash/get'; // 8K (gzipped: 2.9K)
142139
| aws-sdk (full) | 200+ KB | @aws-sdk/client-* |
143140
| crypto-js | ~15 KB | react-native-quick-crypto |
144141

145-
## Quick Size Check Script
142+
## Quick Size Check
146143

147144
```bash
148-
# Check size before installing
149-
npx bundle-phobia-cli <package-name>
145+
# Check size before installing — no install needed:
146+
# https://bundlephobia.com/package/<package-name>
150147

151-
# Or use npm directly (less accurate)
152-
npm pack <package-name> --dry-run 2>&1 | grep "total files"
148+
# Or inspect the published tarball metadata via the package manager:
149+
yarn npm info <package-name> dist.unpackedSize
153150
```
154151

155152
## Decision Matrix

domains/performance/skills/performance/references/bundle-tree-shaking.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ Or specify files with side effects:
182182
## Verification
183183

184184
1. Build production bundle (see [bundle-analyze-js.md](./bundle-analyze-js.md))
185-
2. Analyze with source-map-explorer (see [bundle-analyze-js.md](./bundle-analyze-js.md))
185+
2. Analyze with Expo Atlas (see [bundle-analyze-js.md](./bundle-analyze-js.md))
186186
3. Search for functions you know are unused
187187
4. If found → tree shaking not working
188188

domains/performance/skills/performance/references/js-measure-fps.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,14 @@ flashlight measure
100100
**iOS (React Native CLI):**
101101
```bash
102102
# Run Metro in production mode
103-
npx react-native start --reset-cache
103+
yarn react-native start --reset-cache
104104
# Then build release variant
105105
```
106106

107107
**Expo:**
108108
```bash
109109
# Start Metro without dev mode
110-
npx expo start --no-dev --minify
110+
yarn expo start --no-dev --minify
111111
# For accurate measurements, use EAS Build for release testing
112112
```
113113

0 commit comments

Comments
 (0)