You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
unstable_enableSymlinks does not seems to work #1142
projectRoot
-> node_modules/
-> src/
...
-> neWeb/ <- The intention is for this directory to be a symlink
I am getting this error message
Unable to resolve module ./neWeb/hello.js
However, if I replace the symlink with a real directory, the error goes away
I did a quick search on the https://reactnative.dev with the unstable_enableSymlinks key word but only found the announcement. Hopefully, the intent of the feature is similar to my use case.
React Native Version
0.72.7
Output of npx react-native info
info Fetching system and libraries information...
System:
OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
CPU: (24) x64 AMD Ryzen 9 5900X 12-Core Processor
Memory: 3.79 GB / 46.94 GB
Shell:
version: 5.1.16
path: /bin/bash
Binaries:
Node:
version: 16.13.0
path: ~/.nvm/versions/node/v16.13.0/bin/node
Yarn: Not Found
npm:
version: 8.1.0
path: ~/.nvm/versions/node/v16.13.0/bin/npm
Watchman:
version: 20231001.212519.0
path: /usr/local/bin/watchman
SDKs:
Android SDK: Not Found
IDEs:
Android Studio: Not Found
Languages:
Java:
version: 11.0.20.1
path: /usr/bin/javac
Ruby: Not Found
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 18.2.0
wanted: 18.2.0
react-native:
installed: 0.72.7
wanted: 0.72.7
npmGlobalPackages:
"react-native": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: Not found
newArchEnabled: Not found
Steps to reproduce
1 - Init a standard project
2 - Configure metro.config.js with unstable_enableSymlinks, as in the following const config = { resolver: { unstable_enableSymlinks: true, }, };
3 - Add the required import
4 - npm start -- --reset-cache
5 - Type a to run on Android
Snack, screenshot, or link to a repository
The imported file is hello.js and simply have the following code.
export function dummy () {
let result = 'Hello';
return result;
}
And the default App.ts was modified to have
import { dummy } from './neWeb/hello.js';
debugger;
const a = dummy();
console.log(a);
debugger;
Hi @clauderobi - unstable_enableSymlinks is documented in Metro's docs - see in particular the notes on watchFolders and how files behind symlinks must still be under watch.
You're almost there, but you need to add your workspace root, or at least the neWeb directory, to watchFolders, something like:
Ok, now it works. But the whole concept only partially help me....
I have a very (like in very very very) naive way of dealing with shared JS source files. (*to be precise, between 3 WebApp, webpack, and now a react-native app). I simply have an extra repo with the shared source and symlink neWeb to it in the various sandboxes. The shared repo is somewhere outside of the 3 app's sansboxes.
webpack is able to cope with just allowing it to follow the symlinks. And to make things easier, webpack treats '/' as the project root directory so my imports always start with '/neWeb.....'
I was hoping to do the same with my react-native app; for this I added babel-plugin-root-import to handle the '/'. Now. the new symlink enables me to create my neWeb symlink. But, unless I am mistaken, the watchFolder configuration requires a real path. This somewhat defeats the idea of using a symlink in order to make the project configuration agnostic vs where the sandboxes are located.
I understand that monorepo was the main driver for this but I do not believe in monorepos (I do not think this is the place to discuss why here). An alternative is git submodule and subtree; but both are just nightmare....
The release note for when symlinlk was introduced talks about collecting developer experience. Being able for metro to follow symlinks too would be great!
"Follow symlinks" can mean a bunch of different things - in Metro's case, the whole file system structure (under projectRoot/watchFolders) is loaded into memory during startup, and Metro is (now) able to internally represent symlinks and "traverse" them during resolution (for example, to resolve require('../neWeb/foo.js')), without performing additional I/O. There are no calls to realpath in Metro's resolver.
That is unusual compared to other bundlers which generally just read the filesystem on demand, but it allows Metro to operate on the very largest codebases and get transform cache hits, and perform resolutions, given only a cacheable snapshot of file metadata, without reading unmodified source files at all. That architecture works especially well on codebases that may be too large to check out on a laptop, but it does make simple-sounding features like following symlinks to unknown parts of the filesystem more difficult. We are working to improve the devx around this though - for example, so that Metro can discover these external dependencies on startup and automatically watch them.
Regarding the / feature - I can see how that'd be handy especially if you're not using workspaces (where @my-monorepo/neWeb/foo.js provides similar ergonomics). We have no plans to implement that at the moment, but you could do so in a custom resolver - maybe worth looking into if you have an unusual setup. Even so though, all resolution targets must be watched right now.
Adding the path of the symlink in watchFolders work (sort of see, below); I was hoping that it would be possible to it via the symlink itself. Is that possible?
I am not able to access a source file located somewhere under the symlink.
The file I played with for the last few days had no import itself. I am now working with files where import statements exist but it does not work. The message I am getting is:
Unable to resolve module @babel/runtime/helpers/interopRequireDefault from /neWeb/neCommon/api/apiCrypto.js
If I copy everything from the symlink under the project umbrella (i.e remove the symlink and create a normal directory) all works so the problem is definitely related to the symlink and watchFolders.
It appears that the issue is with node_modules since import of my source files works.
Ah, thanks for the report - this is a subtle known issue that typically comes up in non-monorepo shared code. Metro's transformation process injects dependencies on @babel/runtime/helpers to your code, and then when transforming that file's dependencies we see those as any other require() and try to resolve them relative to the file being transformed, in this case, from somewhere under neWeb, which won't find your project's dependencies.
Two possible workarounds: install @babel/runtime as a dependency of neWeb (assuming it has a package.json) or an ancestor, or add a custom resolver to resolve @babel/runtime relative to your project instead of relative to the source location.
This has come up a few times now, I'll prioritise fixing it in Metro. Thanks for the additional info.
Reacted by jared laser, Ryan Linton and Diogo Izele
The syntax is all good since the project works well when neWeb is actually a regular directory (as a test). The resolver is obviously not needed, I just wanted to check things first. If I restore the symlink for neWeb, then I get the following in the debugger (so the project compiles, etc.)
_$$REQUIRE(...) is not a function
I was thinking that node_modules/@babel/runtime/regenerator/index.js would be the main entry point and everything else would follow. I guess not...
I try chaging the startWith and returnign the corresponding file for the actual file that was been looked at (node_modules/@babel/runtime/helpers/interopRequireDefault.js) but then other files are popping up.
Is it possible to have the resolver to inject a folder rather than complete file path?
I think you can let the existing resolver do almost all of the work here, but the key is to change the origin it's resolving from so that it won't walk up the file tree from your source (where it won't find any installed dependencies).
Or, maybe a cleaner approach - add your project's node_modules to nodeModulesPaths so that it's always used as a fallback.
(The downside to this in general - not a problem in your case - is that if you happen to have an incompatible version of @babel/runtime installed in your other "project", that'll still take precedence. nodeModulesPaths will only be used if regular resolution fails.)
@robhogan
Sorry for adding into this thread as the issue is indirectly related to unstable_enableSymlinks.
I am trying to setup a new project and I still having issues with the symlink. The reported error is: error: Error: Unable to resolve module, the path listed is correct.
metro.config.js in the new project is identical to the same in the old project, except for the adjustment to the watchFolders array.
The watchman configuration, as reported by watchman watch-list is correct. The root paths are all as they should be.
I am using babel to alter the path in some way but replacing the defective import statement path with an absolute full path fix the resolution issue for that import statement. This tells me that watchman in indeed Ok but the symlink setup is somehow invalid. But of course I have tons of import statement later that fail. I did this check just to make sure babel was not in the way (in any case, babel.config.js for both the old and the new project are identical).
I am under the impression that resolver.unstable_enableSymlinks is not taken into account. To make a long story short (well, shorter... ) in the end I did a grep for unstable_enableSymlinks for both the old and new projects. In the old project, there are a lot of hits under node_modules. But significantly less for the new project.
I won't list all the differences but of particular note, the following directories do not exist in the new project.
node_modules/metro-config/node_modules
node_modules/@react-native/metro-config/node_modules
node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-config
Another difference is that in the following 2 files, the value for unstable_enableSymlinks is true in the old project but false in the new. I di dnot make the change myself.
node_modules/metro-config/src/defaults/index.js.flow
node_modules/metro-config/src/defaults/index.js
package.json in the old project does not list anything that explains the difference in node_modules. Doing rm -rf node_modules and running npm i makes not difference.
That looks like you have multiple copies of metro-config, in a mix of older and newer versions. With RN 0.72.x, the default for unstable_enableSymlinksshould be false, you shouldn't have any new Metro versions in there.
Could you paste the output of npm why @react-native/metro-config and npm why metro-config?
@react-native/metro-config@0.72.11 dev
node_modules/@react-native/metro-config
dev @react-native/metro-config@"^0.72.11" from the root project
and
metro-config@0.76.8
node_modules/metro-config
metro-config@"0.76.8" from @react-native-community/cli-plugin-metro@11.3.10
node_modules/@react-native-community/cli-plugin-metro
@react-native-community/cli-plugin-metro@"11.3.10" from @react-native-community/cli@11.3.10
node_modules/@react-native-community/cli
@react-native-community/cli@"11.3.10" from react-native@0.72.7
node_modules/react-native
react-native@"0.72.7" from the root project
peer react-native@"*" from @react-native/virtualized-lists@0.72.8
node_modules/@react-native/virtualized-lists
@react-native/virtualized-lists@"^0.72.8" from react-native@0.72.7
peer react-native@"*" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native@6.1.9
node_modules/@react-navigation/native
@react-navigation/native@"^6.1.9" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-aes-gcm-crypto@0.2.2
node_modules/react-native-aes-gcm-crypto
react-native-aes-gcm-crypto@"^0.2.2" from the root project
peer react-native@"*" from react-native-fast-rsa@2.4.0
node_modules/react-native-fast-rsa
react-native-fast-rsa@"^2.4.0" from the root project
peer react-native@"*" from react-native-safe-area-context@4.7.4
node_modules/react-native-safe-area-context
react-native-safe-area-context@"^4.7.4" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-screens@3.27.0
node_modules/react-native-screens
react-native-screens@"^3.27.0" from the root project
peer react-native-screens@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
metro-config@"0.76.8" from @react-native/metro-config@0.72.11
node_modules/@react-native/metro-config
dev @react-native/metro-config@"^0.72.11" from the root project
metro-config@"0.76.8" from metro@0.76.8
node_modules/metro
metro@"0.76.8" from @react-native-community/cli-plugin-metro@11.3.10
node_modules/@react-native-community/cli-plugin-metro
@react-native-community/cli-plugin-metro@"11.3.10" from @react-native-community/cli@11.3.10
node_modules/@react-native-community/cli
@react-native-community/cli@"11.3.10" from react-native@0.72.7
node_modules/react-native
react-native@"0.72.7" from the root project
peer react-native@"*" from @react-native/virtualized-lists@0.72.8
node_modules/@react-native/virtualized-lists
@react-native/virtualized-lists@"^0.72.8" from react-native@0.72.7
peer react-native@"*" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native@6.1.9
node_modules/@react-navigation/native
@react-navigation/native@"^6.1.9" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-aes-gcm-crypto@0.2.2
node_modules/react-native-aes-gcm-crypto
react-native-aes-gcm-crypto@"^0.2.2" from the root project
peer react-native@"*" from react-native-fast-rsa@2.4.0
node_modules/react-native-fast-rsa
react-native-fast-rsa@"^2.4.0" from the root project
peer react-native@"*" from react-native-safe-area-context@4.7.4
node_modules/react-native-safe-area-context
react-native-safe-area-context@"^4.7.4" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-screens@3.27.0
node_modules/react-native-screens
react-native-screens@"^3.27.0" from the root project
peer react-native-screens@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
metro@"0.76.8" from metro-config@0.76.8
metro@"0.76.8" from metro-transform-worker@0.76.8
node_modules/metro-transform-worker
metro-transform-worker@"0.76.8" from metro@0.76.8
Old project (for reference) - Where symlinks work
@react-native/metro-config@0.72.11 dev
node_modules/@react-native/metro-config
dev @react-native/metro-config@"^0.72.11" from the root project
and
metro-config@0.76.8
node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-config
metro-config@"0.76.8" from @react-native-community/cli-plugin-metro@11.3.10
node_modules/@react-native-community/cli-plugin-metro
@react-native-community/cli-plugin-metro@"11.3.10" from @react-native-community/cli@11.3.10
node_modules/@react-native-community/cli
@react-native-community/cli@"11.3.10" from react-native@0.72.7
node_modules/react-native
react-native@"0.72.7" from the root project
peer react-native@"*" from @react-native/virtualized-lists@0.72.8
node_modules/@react-native/virtualized-lists
@react-native/virtualized-lists@"^0.72.8" from react-native@0.72.7
peer react-native@"*" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native@6.1.9
node_modules/@react-navigation/native
@react-navigation/native@"^6.1.9" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-aes-gcm-crypto@0.2.2
node_modules/react-native-aes-gcm-crypto
react-native-aes-gcm-crypto@"^0.2.2" from the root project
peer react-native@"*" from react-native-fast-rsa@2.4.0
node_modules/react-native-fast-rsa
react-native-fast-rsa@"^2.4.0" from the root project
peer react-native@"*" from react-native-safe-area-context@4.7.4
node_modules/react-native-safe-area-context
react-native-safe-area-context@"^4.7.4" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-screens@3.27.0
node_modules/react-native-screens
react-native-screens@"^3.27.0" from the root project
peer react-native-screens@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@">=0.40.0" from react-native-tcp@3.3.2
node_modules/react-native-tcp
react-native-tcp@"^3.3.2" from the root project
metro-config@0.76.8 dev
node_modules/@react-native/metro-config/node_modules/metro-config
metro-config@"0.76.8" from @react-native/metro-config@0.72.11
node_modules/@react-native/metro-config
dev @react-native/metro-config@"^0.72.11" from the root project
metro-config@0.80.1
node_modules/metro-config
metro-config@"^0.80.1" from the root project
metro-config@"0.80.1" from metro@0.80.1
node_modules/metro-config/node_modules/metro
metro@"0.80.1" from metro-config@0.80.1
metro@"0.80.1" from metro-transform-worker@0.80.1
node_modules/metro-config/node_modules/metro-transform-worker
metro-transform-worker@"0.80.1" from metro@0.80.1
metro-config@0.76.8
node_modules/metro/node_modules/metro-config
metro-config@"0.76.8" from metro@0.76.8
node_modules/metro
metro@"0.76.8" from @react-native-community/cli-plugin-metro@11.3.10
node_modules/@react-native-community/cli-plugin-metro
@react-native-community/cli-plugin-metro@"11.3.10" from @react-native-community/cli@11.3.10
node_modules/@react-native-community/cli
@react-native-community/cli@"11.3.10" from react-native@0.72.7
node_modules/react-native
react-native@"0.72.7" from the root project
peer react-native@"*" from @react-native/virtualized-lists@0.72.8
node_modules/@react-native/virtualized-lists
@react-native/virtualized-lists@"^0.72.8" from react-native@0.72.7
peer react-native@"*" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native@6.1.9
node_modules/@react-navigation/native
@react-navigation/native@"^6.1.9" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-aes-gcm-crypto@0.2.2
node_modules/react-native-aes-gcm-crypto
react-native-aes-gcm-crypto@"^0.2.2" from the root project
peer react-native@"*" from react-native-fast-rsa@2.4.0
node_modules/react-native-fast-rsa
react-native-fast-rsa@"^2.4.0" from the root project
peer react-native@"*" from react-native-safe-area-context@4.7.4
node_modules/react-native-safe-area-context
react-native-safe-area-context@"^4.7.4" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-screens@3.27.0
node_modules/react-native-screens
react-native-screens@"^3.27.0" from the root project
peer react-native-screens@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@">=0.40.0" from react-native-tcp@3.3.2
node_modules/react-native-tcp
react-native-tcp@"^3.3.2" from the root project
metro@"0.76.8" from metro-config@0.76.8
node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-config
metro-config@"0.76.8" from @react-native-community/cli-plugin-metro@11.3.10
node_modules/@react-native-community/cli-plugin-metro
@react-native-community/cli-plugin-metro@"11.3.10" from @react-native-community/cli@11.3.10
node_modules/@react-native-community/cli
@react-native-community/cli@"11.3.10" from react-native@0.72.7
node_modules/react-native
react-native@"0.72.7" from the root project
peer react-native@"*" from @react-native/virtualized-lists@0.72.8
node_modules/@react-native/virtualized-lists
@react-native/virtualized-lists@"^0.72.8" from react-native@0.72.7
peer react-native@"*" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native@6.1.9
node_modules/@react-navigation/native
@react-navigation/native@"^6.1.9" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer @react-navigation/native@"^6.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-aes-gcm-crypto@0.2.2
node_modules/react-native-aes-gcm-crypto
react-native-aes-gcm-crypto@"^0.2.2" from the root project
peer react-native@"*" from react-native-fast-rsa@2.4.0
node_modules/react-native-fast-rsa
react-native-fast-rsa@"^2.4.0" from the root project
peer react-native@"*" from react-native-safe-area-context@4.7.4
node_modules/react-native-safe-area-context
react-native-safe-area-context@"^4.7.4" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/elements@1.3.21
node_modules/@react-navigation/elements
@react-navigation/elements@"^1.3.21" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native-safe-area-context@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@"*" from react-native-screens@3.27.0
node_modules/react-native-screens
react-native-screens@"^3.27.0" from the root project
peer react-native-screens@">= 3.0.0" from @react-navigation/native-stack@6.9.17
node_modules/@react-navigation/native-stack
@react-navigation/native-stack@"^6.9.17" from the root project
peer react-native@">=0.40.0" from react-native-tcp@3.3.2
node_modules/react-native-tcp
react-native-tcp@"^3.3.2" from the root project
metro@"0.76.8" from metro-config@0.76.8 dev
node_modules/@react-native/metro-config/node_modules/metro-config
metro-config@"0.76.8" from @react-native/metro-config@0.72.11
node_modules/@react-native/metro-config
dev @react-native/metro-config@"^0.72.11" from the root project
metro@"0.76.8" from metro-transform-worker@0.76.8
node_modules/metro-transform-worker
metro-transform-worker@"0.76.8" from metro@0.76.8
metro@"0.76.8" from metro-config@0.76.8
It looks like the difference is that your old project has metro-config@"^0.80.1" in its package.json, which is pulling in all of Metro 0.80.
It may be that your new project is hitting a bug in Metro 0.76 that's fixed in Metro 0.80, but I'd need more detail on what's failing to resolve and what the project structure is to determine that. It's not obviously a symlink-related issue, as far as I can tell.
I did npm i --save metro-config (I do not recall doing it in the old project, probably something did it for me under the hood).
But no difference, I still get Error: Unable to resolve module (I did the rm -rf /tmp/metro-* drill and npm start -- --reset-cache).
Now, grep -r unstable_enableSymlinks * return the same thing for both the old nad new project. In node_modules/metro-config/src/defaults/index.js.flow and node_modules/metro-config/src/defaults/index.js, unstable_enableSymlinks is not set to true.
Sorry for the delay, I was pulled to something else. I am not back at trying to solve the problem with resolving modules that are access via the symlink.
To eliminate problem with watchman I did this test:
stopping (ctrl-c) the metro bundler
watchman watch-del-all
watchman watch-list
// Restarting the bundler (npm start)
watchman watch-list
watchman find <edited>/neWeb | grep api
The watch-list shows empty for the first invocation and then the folders I configured in (Using path.resolve) for the 2nd.
watchman find listed my files.
Then I ran the following command
npx metro get-dependencies ./src/test.js
and got the Error: Unable to resolve module result.
(metro version, as reported by the cli, is 0.76.8)
In package.json, I have the 2 modules:
"metro-config": "^0.80.1",
"@react-native/metro-config": "^0.72.11",
Both the project which works and the new project have the same thing. I redid an npm install on the new.
Symlink support is no longer configurable. It became the default in Metro 0.80 and unstable_enableSymlinks was removed in 0.81. Targets still need to be under projectRoot or watchFolders, as they always did.
The @babel/runtime failure from shared code outside the project is tracked separately with a fix in progress - Metro's metro:babel-runtime resolver (#1804) and the React Native preset side (react/react-native#57974). See #1029.
The resolution failures in the newer project were a mix of Metro versions in node_modules rather than anything symlink-related. Installing metro-config directly alongside the copy React Native ships causes that.
Activity
clauderobi commentedon Nov 20, 2023
Ok, I created the git repo;
https://github.com/clauderobi/rn_Symlink
It is configured to use a symLink and it fails the build.
robhogan commentedon Nov 22, 2023
Hi @clauderobi -
unstable_enableSymlinksis documented in Metro's docs - see in particular the notes onwatchFoldersand how files behind symlinks must still be under watch.You're almost there, but you need to add your workspace root, or at least the
neWebdirectory, towatchFolders, something like:Let us know if that doesn't help. (And thanks for the quality report with repro! 👍 )
clauderobi commentedon Nov 22, 2023
Ok, now it works. But the whole concept only partially help me....
I have a very (like in very very very) naive way of dealing with shared JS source files. (*to be precise, between 3 WebApp, webpack, and now a react-native app). I simply have an extra repo with the shared source and symlink neWeb to it in the various sandboxes. The shared repo is somewhere outside of the 3 app's sansboxes.
webpack is able to cope with just allowing it to follow the symlinks. And to make things easier, webpack treats '/' as the project root directory so my imports always start with '/neWeb.....'
I was hoping to do the same with my react-native app; for this I added babel-plugin-root-import to handle the '/'. Now. the new symlink enables me to create my neWeb symlink. But, unless I am mistaken, the watchFolder configuration requires a real path. This somewhat defeats the idea of using a symlink in order to make the project configuration agnostic vs where the sandboxes are located.
I understand that monorepo was the main driver for this but I do not believe in monorepos (I do not think this is the place to discuss why here). An alternative is git submodule and subtree; but both are just nightmare....
The release note for when symlinlk was introduced talks about collecting developer experience. Being able for metro to follow symlinks too would be great!
robhogan commentedon Nov 22, 2023
Appreciate the feedback @clauderobi
"Follow symlinks" can mean a bunch of different things - in Metro's case, the whole file system structure (under
projectRoot/watchFolders) is loaded into memory during startup, and Metro is (now) able to internally represent symlinks and "traverse" them during resolution (for example, to resolverequire('../neWeb/foo.js')), without performing additional I/O. There are no calls torealpathin Metro's resolver.That is unusual compared to other bundlers which generally just read the filesystem on demand, but it allows Metro to operate on the very largest codebases and get transform cache hits, and perform resolutions, given only a cacheable snapshot of file metadata, without reading unmodified source files at all. That architecture works especially well on codebases that may be too large to check out on a laptop, but it does make simple-sounding features like following symlinks to unknown parts of the filesystem more difficult. We are working to improve the devx around this though - for example, so that Metro can discover these external dependencies on startup and automatically watch them.
Regarding the
/feature - I can see how that'd be handy especially if you're not using workspaces (where@my-monorepo/neWeb/foo.jsprovides similar ergonomics). We have no plans to implement that at the moment, but you could do so in a custom resolver - maybe worth looking into if you have an unusual setup. Even so though, all resolution targets must be watched right now.clauderobi commentedon Nov 24, 2023
Thanks for the long feedback @robhogan
The '/' is handled Ok by babel...
Adding the path of the symlink in watchFolders work (sort of see, below); I was hoping that it would be possible to it via the symlink itself. Is that possible?
I am not able to access a source file located somewhere under the symlink.
The file I played with for the last few days had no import itself. I am now working with files where import statements exist but it does not work. The message I am getting is:
Unable to resolve module @babel/runtime/helpers/interopRequireDefault from /neWeb/neCommon/api/apiCrypto.js
If I copy everything from the symlink under the project umbrella (i.e remove the symlink and create a normal directory) all works so the problem is definitely related to the symlink and watchFolders.
It appears that the issue is with node_modules since import of my source files works.
Here is what is in metro.config.js
node_modules is at the correct place, in the project directory, where metro.config.js and packahe.json, etc) is located.
watchman find . and watchman find ../../../NearEDGE/neWeb works as expected (after I resolved the inotify limits in the kernel).
I also deleted /tmp/metro-* and used npm start -- --reset-cache
(And, @babel/runtime/xxxx is indeed present in the FS).
robhogan commentedon Nov 24, 2023
Ah, thanks for the report - this is a subtle known issue that typically comes up in non-monorepo shared code. Metro's transformation process injects dependencies on
@babel/runtime/helpersto your code, and then when transforming that file's dependencies we see those as any otherrequire()and try to resolve them relative to the file being transformed, in this case, from somewhere underneWeb, which won't find your project's dependencies.Two possible workarounds: install
@babel/runtimeas a dependency ofneWeb(assuming it has apackage.json) or an ancestor, or add a custom resolver to resolve@babel/runtimerelative to your project instead of relative to the source location.This has come up a few times now, I'll prioritise fixing it in Metro. Thanks for the additional info.
clauderobi commentedon Nov 25, 2023
My neWeb library does not have any package.json file it is just plain functional code that rely on the project to do everything else.
I try the custom resolver route. My first attempt was with this metro.config.js
The syntax is all good since the project works well when neWeb is actually a regular directory (as a test). The resolver is obviously not needed, I just wanted to check things first. If I restore the symlink for neWeb, then I get the following in the debugger (so the project compiles, etc.)
_$$REQUIRE(...) is not a function
I was thinking that node_modules/@babel/runtime/regenerator/index.js would be the main entry point and everything else would follow. I guess not...
I try chaging the startWith and returnign the corresponding file for the actual file that was been looked at (node_modules/@babel/runtime/helpers/interopRequireDefault.js) but then other files are popping up.
Is it possible to have the resolver to inject a folder rather than complete file path?
robhogan commentedon Nov 25, 2023
I think you can let the existing resolver do almost all of the work here, but the key is to change the origin it's resolving from so that it won't walk up the file tree from your source (where it won't find any installed dependencies).
Or, maybe a cleaner approach - add your project's
node_modulestonodeModulesPathsso that it's always used as a fallback.(The downside to this in general - not a problem in your case - is that if you happen to have an incompatible version of
@babel/runtimeinstalled in your other "project", that'll still take precedence.nodeModulesPathswill only be used if regular resolution fails.)clauderobi commentedon Nov 28, 2023
Yes, the nodeModulesPaths approach is simpler and works. Should the issue be closed or stay open until a fix is available?
clauderobi commentedon Nov 30, 2023
@robhogan
Sorry for adding into this thread as the issue is indirectly related to unstable_enableSymlinks.
I am trying to setup a new project and I still having issues with the symlink. The reported error is:
error: Error: Unable to resolve module, the path listed is correct.
metro.config.js in the new project is identical to the same in the old project, except for the adjustment to the watchFolders array.
The watchman configuration, as reported by watchman watch-list is correct. The root paths are all as they should be.
I am using babel to alter the path in some way but replacing the defective import statement path with an absolute full path fix the resolution issue for that import statement. This tells me that watchman in indeed Ok but the symlink setup is somehow invalid. But of course I have tons of import statement later that fail. I did this check just to make sure babel was not in the way (in any case, babel.config.js for both the old and the new project are identical).
I am under the impression that resolver.unstable_enableSymlinks is not taken into account. To make a long story short (well, shorter... ) in the end I did a grep for unstable_enableSymlinks for both the old and new projects. In the old project, there are a lot of hits under node_modules. But significantly less for the new project.
I won't list all the differences but of particular note, the following directories do not exist in the new project.
node_modules/metro-config/node_modules
node_modules/@react-native/metro-config/node_modules
node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-config
Another difference is that in the following 2 files, the value for unstable_enableSymlinks is true in the old project but false in the new. I di dnot make the change myself.
node_modules/metro-config/src/defaults/index.js.flow
node_modules/metro-config/src/defaults/index.js
package.json in the old project does not list anything that explains the difference in node_modules. Doing rm -rf node_modules and running npm i makes not difference.
I exhausted all I can think of. Any suggestion?
robhogan commentedon Nov 30, 2023
That looks like you have multiple copies of
metro-config, in a mix of older and newer versions. With RN 0.72.x, the default forunstable_enableSymlinksshould befalse, you shouldn't have any new Metro versions in there.Could you paste the output of
npm why @react-native/metro-configandnpm why metro-config?clauderobi commentedon Nov 30, 2023
New project - Symlinks not working
and
Old project (for reference) - Where symlinks work
and
robhogan commentedon Nov 30, 2023
It looks like the difference is that your old project has
metro-config@"^0.80.1"in itspackage.json, which is pulling in all of Metro 0.80.It may be that your new project is hitting a bug in Metro 0.76 that's fixed in Metro 0.80, but I'd need more detail on what's failing to resolve and what the project structure is to determine that. It's not obviously a symlink-related issue, as far as I can tell.
clauderobi commentedon Nov 30, 2023
I did npm i --save metro-config (I do not recall doing it in the old project, probably something did it for me under the hood).
But no difference, I still get Error: Unable to resolve module (I did the rm -rf /tmp/metro-* drill and npm start -- --reset-cache).
Now, grep -r unstable_enableSymlinks * return the same thing for both the old nad new project. In node_modules/metro-config/src/defaults/index.js.flow and node_modules/metro-config/src/defaults/index.js, unstable_enableSymlinks is not set to true.
The structure is as follow
In metro.config. js, I have
Output from watchman watch-list
If I replace the neWeb symlink with a real directory hierarchy (using cp -a) all is good.
clauderobi commentedon Dec 10, 2023
@robhogan
Sorry for the delay, I was pulled to something else. I am not back at trying to solve the problem with resolving modules that are access via the symlink.
To eliminate problem with watchman I did this test:
The watch-list shows empty for the first invocation and then the folders I configured in (Using path.resolve) for the 2nd.
watchman find listed my files.
Then I ran the following command
npx metro get-dependencies ./src/test.jsand got the Error: Unable to resolve module result.
(metro version, as reported by the cli, is 0.76.8)
In package.json, I have the 2 modules:
"metro-config": "^0.80.1",
"@react-native/metro-config": "^0.72.11",
Both the project which works and the new project have the same thing. I redid an
npm installon the new.Any suggestion on how to proceed here?
Thanks.
robhogan commentedon Aug 31, 2026
Closing this - taking the threads in turn:
Symlink support is no longer configurable. It became the default in Metro 0.80 and
unstable_enableSymlinkswas removed in 0.81. Targets still need to be underprojectRootorwatchFolders, as they always did.The
@babel/runtimefailure from shared code outside the project is tracked separately with a fix in progress - Metro'smetro:babel-runtimeresolver (#1804) and the React Native preset side (react/react-native#57974). See #1029.The resolution failures in the newer project were a mix of Metro versions in
node_modulesrather than anything symlink-related. Installingmetro-configdirectly alongside the copy React Native ships causes that.