Skip to content

feat: expose modules in runtime both in development and production#1645

Open
tjzel wants to merge 1 commit into
react:mainfrom
tjzel:@tjzel/expose-modules
Open

feat: expose modules in runtime both in development and production#1645
tjzel wants to merge 1 commit into
react:mainfrom
tjzel:@tjzel/expose-modules

Conversation

@tjzel

@tjzel tjzel commented Jan 29, 2026

Copy link
Copy Markdown

Summary

Expose getModules function in both development and release environments. This allows the developers to dynamically replace a module in runtime.

I personally use it in react-native-worklets to replace react-native module on extra JavaScript runtimes. react-native module includes side-effects that are fatal on these extra runtimes and replacing it with a mock module improves developer experience significantly, in case they import react-native accidentally in runtime.

Changelog: [Feature] Expose modules in runtime both in development and production

Test plan

Nothing to test really.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jan 29, 2026
@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jan 29, 2026
@robhogan

robhogan commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

I personally use it in react-native-worklets to replace react-native module on extra JavaScript runtimes.

Hey, sorry for the delay responding here but to be honest, this worries me - using the returned value of getModules to arbitrarily mutate the module graph at runtime isn't what it's designed for even in __DEV__ and feels like a creative workaround rather than a candidate for a new public API.

Couldn't the additional runtime resolve react-native differently instead, or even transform imports of react-native before they're resolved? It's been a while since I've looked closely at worklets tbh, I can't remember just how the pipeline works.

@tjzel

tjzel commented Apr 8, 2026

Copy link
Copy Markdown
Author

Hey @robhogan, thanks for answering. I understand your concerns and I agree that it's not the optimal solution - but I couldn't come up with an alternate one that would be as simple as this one.

To state the problem more explicitly, let's say we have the following file:

import { TurboModuleRegistry } from 'react-native';

TurboModuleRegistry.getEnforcing("someModule").doSomething();

Now the issue is that this code is perfectly fine in an React Native app when it's executed on the RN Runtime, the same runtime where the components are rendered etc.

However, if this code is evaluated on an additional runtime, it's going to throw an error. The other runtime doesn't have the native bindings injected into the global scope that drive the TurboModuleRegistry. And it's fine - TurboModules shouldn't be accessed on additional runtimes.

What I want to achieve is to throw a meaningful error in this situation - that the user tried to access react-native APIs on a runtime not configured for these.

However, both runtimes share exactly the same bundle, so effectively they get the transformed output of the file:

__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
  var _reactNative = _$$_REQUIRE(_dependencyMap[0]);
  _reactNative.TurboModuleRegistry.getEnforcing('someModule').doSomething();
},0,[1]);

At this point, the __d define call will be executed before any __r entry point call - meaning that I can't change the reference to _$$_REQUIRE that the module gets so I can't really put a wrapper to it that would disallow imports from react-native. This is why I resolved to replacing the whole module instead

https://github.com/software-mansion/react-native-reanimated/blob/main/packages/react-native-worklets/src/bundleMode/metroOverrides.native.ts#L38-L79

Note that this has really nothing to do with worklets or the worklet directive in particular.

@robhogan

robhogan commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for the detail!

However, both runtimes share exactly the same bundle

This is something I'd want to explore. I think ideally we'd be aware at build time that the code is targeting a secondary runtime and we could catch disallowed imports in the resolver.

Sorry to be a pain, but it'd help me understand the this end-to-end and see if there might be other options if you wouldn't publishing a quick worklets demo app exhibiting the problem.

@tjzel

tjzel commented Apr 13, 2026

Copy link
Copy Markdown
Author

@robhogan Sure, I created a reproduction app with all the explanations in README - I tried to describe the behavior as is, let me know if you want some more in-depth explanations, perhaps we could schedule a meeting in this case.

https://github.com/tjzel/Worklets-Metro-integration

@robhogan

robhogan commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Hi @tjzel, sorry this stalled again - bit chaotic here.

First, huge thanks for the repo - I had a chance to play with it last week and it's certainly helped me understand how bundle mode works and what the challenges are here. I'm working on a virtual modules implementation using this as an example use case.

For the issue at hand in this PR though - manipulating the module graph at runtime still feels like a last resort. I wonder - especially since RNW already applies a custom resolveRequest and a context global, why not resolve react-native to a shim module that behaves differently at runtime according to whether it's executing as a worklet?

@tjzel

tjzel commented Jun 8, 2026

Copy link
Copy Markdown
Author

Hey @robhogan, no worries - this feature isn't a high priority, thanks for the reply!

I'll try the shim approach, you are an absolute genius! I'll let you know if it covers my use-case.

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants