Repository navigation
babel import-plugin still rewrites react-native-web internals, causing a circular-import crash on web (incomplete fix of #196 / #202) #380
Description
Activity
🤖 Auto-triage
Status: CONFIRMED
Type: bug
Version: v5 (react-native-css@3.0.7, nativewind@5.0.0-preview.4)Findings
Confirmed. The babel
import-pluginrewrites relative imports whose resolved paths land insidereact-native-web/dist, including react-native-web's own internal imports. I reproduced all three of the reporter's cases (userland../Viewinsidereact-native-web/dist/modules/ScrollView/index.js,../../../../exports/FlatListinsidedist/vendor/react-native/Animated/components/AnimatedFlatList.js, and the_interopRequireDefault(require('../View'))variant) — every one gets rewritten toreact-native-css/components/*, producing the exact circular-import cycle described. The existing tests insrc/__tests__/babel/react-native-web.test.ts:39-44and:87-93in fact codify this behavior as expected, so the fix will need to update those assertions too.The reporter's diagnosis is precise:
isFromThisModuleinsrc/babel/import-plugin.ts:32-36guards origins in the RNC dist/src but not inreact-native-web.webResolverinsrc/metro/resolver.ts:62-98only excludes target files inreact-native-web/dist/vendor, not origins.nativeResolver(same file, line 28-34) already has the analogous origin guard (isReactNativeIndex), so the asymmetry is clear.
What I tested
- Read
src/babel/import-plugin.ts,src/babel/react-native-web.ts, andsrc/metro/resolver.tsto verify the reporter's code-path analysis. - Wrote a Jest test invoking the babel plugin via
@babel/coretransformSyncwithfilenameset to/…/react-native-web/dist/modules/ScrollView/index.jsand/…/react-native-web/dist/vendor/react-native/Animated/components/AnimatedFlatList.js. All three assertions failed — the plugin rewrote every relative specifier toreact-native-css/components/*, matching the crash chain in the report. - Confirmed
parseReactNativeWebSource(src/babel/react-native-web.ts:15-43) does exactly the relative→absolute→react-native-web/dist-slice rewrite the reporter described. - Reviewed the existing
react-native-web.test.tscases withfilename: "react-native-web/dist/modules/ScrollView/index.js"— they intentionally assert the buggy transform.
Next steps
- Apply the two guards the reporter proposes (accept the offer of a PR if convenient):
src/babel/import-plugin.ts— extendisFromThisModule(or a sibling check) to bail whenstate.filenamecontains${sep}react-native-web${sep}.src/metro/resolver.ts— add the equivalent origin guard towebResolver, mirroringnativeResolver'sisReactNativeIndex.
- Update
src/__tests__/babel/react-native-web.test.tscases at lines 39-44 and 87-93 — these currently assert the buggy behavior and must be inverted (or moved into "should NOT rewrite" cases) once the fix lands. - Add regression tests covering both the
ScrollView/index.js → ../ViewandAnimatedFlatList.js → ../../../../exports/FlatListshapes. - Sanity-check native platform behavior is untouched (the change is scoped to react-native-web origin detection).
This is an automated triage. See auto-triage.yml.
- addedauto-triagedIssue has been automatically triaged by the auto-triage workflowIssue has been automatically triaged by the auto-triage workflowbugSomething isn't workingSomething isn't workingconfirmedBug reproduced and confirmed by triageBug reproduced and confirmed by triage
on Jul 17, 2026
Before submitting a new issue
Bug summary
This is a follow-up to #196 — the fix in #202 is incomplete, so the react-native-web circular-import crash still reproduces on web with
react-native-css@3.0.7.On web, the app crashes at bundle load time:
(In a production bundle this surfaces as
can't access property "default", _exportsFlatList2 is undefined.)Root cause
There are two layers that rewrite
react-native(-web)imports toreact-native-css/components/*: the metrowebResolver(src/metro/resolver.ts) and the babelimport-plugin(src/babel/import-plugin.ts). #202 only touched the resolver, and only by excluding resolution targets insidereact-native-web/dist/vendor(plusVirtualizedList). Neither layer excludes origins inside react-native-web, and the babel plugin was not changed at all.The babel plugin's
parseReactNativeWebSourceresolves relative import specifiers against the current filename and rewrites anything that lands insidereact-native-web/dist. So react-native-web's own internal imports get rewritten, which creates the cycle:Animated→react-native-web/dist/vendor/react-native/Animated/Animated.jsloadsAnimatedFlatList.js.AnimatedFlatList.jsimports../../../../exports/FlatList→ resolves intoreact-native-web/dist→ babel rewrites it toreact-native-css/components/FlatList. (The resolver's vendor exclusion doesn't help: it checks the target path, and the target here isexports/FlatList, not a vendor file. The babel plugin rewrites the specifier before the resolver would matter anyway.)react-native-css/components/FlatListimportsreact-native→react-native-web/dist/index.js.export { default as FlatList } from './exports/FlatList'was also babel-rewritten toreact-native-css/components/FlatList— which is the module currently mid-initialization in step 3 → its partial exports have nodefault→ crash.Note the native side already has exactly the guard that's missing here:
nativeResolverskips rewriting when the origin isreact-native/index.js.Suggested fix (verified locally via
pnpm patchagainst the publisheddist):src/babel/import-plugin.ts— extend the existingisFromThisModuleskip so files insidereact-native-webare never rewritten:src/metro/resolver.ts— add the equivalent origin guard towebResolver(mirrorsnativeResolver'sisReactNativeIndexguard):With both guards in place: userland imports of
react-native/react-native-webstill resolve to the wrapped components (via the resolver / babel rewrite of their imports), the wrappers still receive the raw react-native-web implementations, the cycle is gone, and the app renders on web. Android/native is unaffected. Happy to open a PR if that helps.Library version
3.0.7
Environment info
Steps to reproduce
create-expo-app, default template — it already usesAnimatedvia its demo screens; any code path that touches react-native-web'sAnimatedexport triggers the bug).nativewind@5.0.0-preview.4+react-native-css@3.0.7) with the standardwithNativewindmetro config andnativewind/babelpreset.expo start, open the web target.Reproducible example repository
The full root-cause analysis and a verified fix are included above; my project is private, but I can extract a minimal public repro from the steps above if needed.