Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

babelLoader always uses hermes-parser, unlike Metro — 90s on one prebuilt file

Open
#1,447 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
react-native, typescript

Research direction

Start in packages/repack/src/loaders/babelLoader/babelLoader.ts:89-96 and compare its parser selection with the preset behavior described for babel-plugin-syntax-hermes-parser. Use the reported @lottiefiles/dotlottie-react file and Flow examples to verify the selection, then confirm that bundle output remains byte-identical and that non-Flow files avoid the hermes-parser slowdown.

Written by the indexing model from the issue text.

Description

The problem

babelLoader parses every non-TypeScript file with hermes-parser (packages/repack/src/loaders/babelLoader/babelLoader.ts:89-96):

const sourceAst = isTS || isTSX
  ? parseSync(src, babelConfig)
  : hermesParser.parse(src, { babel: true, ... });

Metro does not. @react-native/babel-preset passes parseLangTypes: 'flow' to babel-plugin-syntax-hermes-parser, which skips hermes-parser when a file has no @flow pragma and lets @babel/parser handle it:

// babel-plugin-syntax-hermes-parser/dist/index.js:56
if (parseLangTypes === 'flow' && !/@flow/.test(code)) { return; }

So Re.Pack ignores the preset's own parseLangTypes, and a dependency that is free under Metro can cost minutes here.

What it cost us

We bundle the Expensify app with Re.Pack. Our cold production iOS build was ~114s, against Metro's ~83s. One file accounted for 94s of that:

@lottiefiles/dotlottie-react/dist/browser/index.js — 502 KB, 22 lines, prebuilt minified ESM, no @flow pragma.

parser time
hermes-parser 90.1s
@babel/parser 0.1s

That is not the parse itself. parse(src, {babel: false}) takes 0.1s; all 88s is in hermes-parser's Hermes-AST to Babel-AST conversion, which is quadratic in the number of sibling nodes: every node replacement copies the whole sibling array. This file has one ArrayExpression with 128,834 elements. Reported upstream with a profile and a minimal repro: facebook/hermes#2158.

Because it is one serial task, it set a minimum time for the whole build no matter how many workers we gave it.

It was also hard to spot: no warning, and nothing showed up in per-loader timings. We only found it by bisecting.

Across our whole babel lane (3522 files), 2779 have no pragma. Those parse in 1.1s with @babel/parser versus 4.7s with hermes-parser — and hermes-parser fails on 2 of them.

Why not simply switch parsers

hermes-parser is still needed for the other files. @babel/parser fails on 241 files in react-native 0.86, which use Flow syntax it cannot read (as casts, component(...) types). The pragma check is the fix, not a swap.

Suggested fix
const sourceAst = isTS || isTSX
  ? parseSync(src, babelConfig)
  : /@flow/.test(src)
    ? hermesParser.parse(src, { babel: true, ... })
    : parseSync(src, babelConfig);

Better still, read the preset's parseLangTypes instead of hard-coding the check, so parseLangTypes: 'all' still sends everything to hermes-parser.

We have tested this

We run it in our app through the existing hermesParserPath option, which babelSwcLoader passes through. Our replacement parser checks for @flow and picks hermes-parser only then.

  • bundle output is byte-identical, apart from the sourcemap filename
  • build time is flat, because we had already moved that package to another loader
  • the file above: 90.1s → 0.1s

Identical output on a 35 MB bundle shows the check does not change what gets built.

Happy to open a PR.

Related

#1422 covers Flow in the SWC-native lane (getJsTransformRules). This report is about the babel lane that the templates use, so they do not overlap, but both come down to how Flow is detected.

Environment

@callstack/repack@5.2.5, Rspack 2.1.3, hermes-parser@0.36.1, @babel/parser@7.29.7, react-native 0.86, Node 26.5.0, macOS arm64.

Dominant language
TypeScript
Stars
1.9k
Forks
164
Avg merge
10d 13h
Merged PRs (30d)
10

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from callstack/repack

All issues in callstack/repack

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.