Skip to content

Metro resolver loop with react-native-worklets bundle mode: "Maximum call stack size exceeded" on launch (get NativeModules) #473

Description

@jsg2021

Describe the bug

With react-native-worklets' bundle mode enabled (getBundleModeMetroConfig), the app crashes on launch, before AppRegistry registers anything:

RangeError: Maximum call stack size exceeded (native stack depth)
Invariant Violation: "main" has not been registered.

The stack is get NativeModules repeated, from the lazy getters in react-native-css/dist/commonjs/components/index.cjs.

Cause

Two Metro resolvers both rewrite react-native, and react-native-css hands its own react-native imports to the resolver below it:

  1. App code imports react-native. Worklets bundle mode resolves it to react-native-worklets/bundleMode/shims/reactNativeShim.js.
  2. The shim runs module.exports = require('react-native'). react-native-css's nativeResolver redirects that to react-native-css/components.
  3. react-native-css/components/index.cjs imports react-native. Because the origin is internal, nativeResolver returns the parent resolver's result, and the parent (worklets) sends it back to the shim.
  4. The shim's exports are the react-native-css components, whose getters call require('react-native'), which returns the shim again. The getters recurse forever.

It loops whichever order the two configs are applied in. nativeResolver already exempts react-native/index.js as an origin. The internal-origin path has a similar problem: it trusts the parent resolver to return the real package.

Workaround (in metro.config.js, after both wrappers):

const path = require('node:path');

const reactNativeCssDir = `${path.sep}node_modules${path.sep}react-native-css${path.sep}`;
const resolveRequest = config.resolver.resolveRequest;
config.resolver.resolveRequest = (context, moduleName, platform) =>
	moduleName === 'react-native' &&
	context.originModulePath.includes(reactNativeCssDir)
		? context.resolveRequest(context, moduleName, platform)
		: resolveRequest(context, moduleName, platform);

Suggested fix: in nativeResolver, resolve react-native from an internal origin with Metro's default context.resolveRequest instead of the parent resolver. react-native-css's components always need the real package.

Reproduction

npx rn-new@next --nativewind, add react-native-worklets, then in metro.config.js:

const { getDefaultConfig } = require('expo/metro-config');
const { withNativewind } = require('nativewind/metro');
const { getBundleModeMetroConfig } = require('react-native-worklets/bundleMode');

let config = getDefaultConfig(__dirname);
config = withNativewind(config);
config = getBundleModeMetroConfig(config);
module.exports = config;

Launch on iOS. The crash happens during module init, before any component renders.

Versions

  • nativewind 5.0.0-rc.0
  • react-native-css 3.1.0-rc.0
  • react-native-worklets 0.13.0 (react-native-reanimated 4.7.1)
  • react-native 0.86.3, expo 57.0.27
  • tailwindcss 4.3.3
  • iOS simulator, Hermes

Possibly related: #455 has the same error text, but a different trigger (styled() around react-native-svg at render time).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions