MetaMask / MetaMask/metamask-mobile
Reduce/avoid `export *` barrel files that defeat Metro tree-shaking
- Dominant language
- TypeScript
- Stars
- 3k
- Forks
- 1.7k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
> **Performance audit finding** · Severity: **Medium** · Effort: Hard · Fix risk: Risky · Test safety net: Uncovered
> Owner: `@MetaMask/mobile-platform (suggested)`
> File: `app/ (966 re-export index files; ~158 use `export * from`)`
### What is this about?
966 `index.ts(x)` files re-export other modules; 158 of them use the wildcard form `export * from './x'`. Metro does not reliably tree-shake barrels: importing a single symbol from a barrel that does `export *` evaluates *every* module the barrel touches at startup. The most impactful are the aggregate barrels that fan out to many submodules and sit on hot import paths.
**Why it matters**
Barrels that re-export with `export *` force eager evaluation of the whole subtree on first import, inflating module-init time (TTI) and bundle reachability. Two categories matter most here:
1. **Selectors** — 870 files import from `app/selectors/...`; the directory barrels use `export *` (e.g. `app/selectors/snaps/index.ts`, `app/selectors/multichain/index.ts`), so importing one selector can pull a whole controller's selector tree.
2. **Engine** — `app/core/Engine/index.ts` does `export * from './types'` and is on the app-init path; `app/core/Engine/` contains 21 `export *` index files.
Note: the bulk of `app/component-library/components/**/index.ts` and `app/components/Views/**/index.ts` barrels are single-line `export { default } from './X'` re-exports (1020 files import from `component-library/components`). These single-default barrels are far cheaper than `export *` fan-out barrels and are NOT the priority — flag only the wildcard aggregate barrels.
### Scenario
N/A — see Technical Details.
### Design
N/A — internal performance change; no UI/design impact.
### Technical Details
**Evidence**
`grep -rln "export \* from" app --include="*.ts" --include="*.tsx" | grep -E "index\.(ts|tsx)$" | wc -l` => **158** wildcard barrels. By top directory:
```
81 app/components/Views
28 app/components/UI
21 app/core/Engine
6 app/util/notifications
5 app/components/hooks
```
- `app/selectors/snaps/index.ts:1`
```ts
export * from './interfaceController';
export * from './permissionController';
export * from './snapController';
```
- `app/core/Engine/index.ts:2` — `export * from './types';` (app-init path)
- 870 files import `from '.../selectors/...'`, 1020 from `component-library/components`.
**Fix**
- For aggregate `export *` barrels on hot paths (selectors, Engine), prefer deep imports at call sites (`import { selectX } from 'app/selectors/foo/specific'`) and/or convert `export *` to explicit named `export { selectX } from './specific'` so unused modules are not transitively reachable.
- Add an ESLint `no-restricted-imports` / `no-barrel-files` rule for new `export *` index files in `app/selectors` and `app/core/Engine`.
- Do NOT mass-rewrite the single-default `export { default }` component barrels — low payoff, high churn.
### Threat Modeling Framework
N/A — performance-only change; behavior is preserved, no new data flow / trust boundary / attack surface.
### Acceptance Criteria
- - Metro bundle/module-count diff before/after debarreling a target directory (`react-native bundle` stats or source-map-explorer).
- TTI trace (`trace()` instrumentation, see `mms-performance`) on cold start before/after.
- `yarn lint`, `yarn lint:tsc`, full unit suite pass (import churn is the main risk; size/startup are not asserted by tests => Uncovered).
### References
- File: `app/ (966 re-export index files; ~158 use `export * from`)`
- Source: MetaMask Mobile performance audit — finding `bundle-barrel-export-files`
- Owner (CODEOWNERS / best-effort): @MetaMask/mobile-platform (suggested)
- Status: **UNVALIDATED**
Contributor guide
Research direction
Start by inspecting app/selectors/snaps/index.ts and app/core/Engine/index.ts, then identify wildcard aggregate barrels on hot import paths using the provided grep command. Measure a selected change with react-native bundle stats or source-map-explorer and mms-performance cold-start traces; done means the targeted imports are debarreled, lint and TypeScript checks pass, and the unit suite remains green.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react-native, typescript
- Domain
- mobile-dev, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100