Comments (11)
Well, that wasn't too difficult: #1136.
Please take it for a spin to see if it fixes your issue too 🙂
from metro.
I think I understand the issue better now. This happens only when the package being imported is in the haste map, because it's in the list of watchFolders.
I've added a failing test to main...kraenhansen:metro:fixing-1128 showing how this is broken and I'll try to see if I can contribute a fix for it too 🙏
from metro.
leave it open until your PR is merged in?
Please leave it open 🙂 I expect it to close as the PR merge 🙏
from metro.
Reproducible example
from metro.
Hi!
I think i discovered the issue and I have a proposal for a solution but I need your help!
The error is easily reproducible with the examples from the repo I posted above in the issue. The problem I found is in the metro-resolver
package.
Whenever you have a module which is using the "new" exports way of exposing its content, like this in package.json
:
"exports": {
"./native": {
"types": "./lib/native/index.d.ts",
"import": "./lib/native/index.mjs",
"require": "./lib/native/index.cjs"
},
"./web": {
"types": "./lib/web/index.d.ts",
"import": "./lib/web/index.mjs",
"require": "./lib/web/index.cjs"
},
"./package.json": "./package.json"
},
and then you are consuming this module like this:
import Whatever from '@dp/mypackage/native'
OR import Whatever from '@dp/mypackage/web'
for web in this case...
the metro-resolver
=> resolve.js
will consider this as a "Haste Module" BUT IT IS NOT!!
My Solution
I created this additional method, which checks if the exports
are defined in the package.json
of the imported module and if the unstable_enablePackageExports
flag is set. If that is the case then this module should not be considered as a "Haste Module" and that part of the code should be skipped...
My function:
function isExportedSource(context, moduleName) {
if (context.unstable_enablePackageExports) {
let packageName = moduleName;
let packageJsonPath = context.resolveHastePackage(packageName);
while (packageJsonPath == null && packageName && packageName !== ".") {
packageName = _path.default.dirname(packageName);
packageJsonPath = context.resolveHastePackage(packageName);
}
if (packageJsonPath == null) {
return failedFor();
}
if (packageJsonPath) {
const pkg = context.getPackageForModule(packageJsonPath);
const exportsField = pkg?.packageJson.exports;
const packageName = pkg?.packageJson.name;
const exportName = moduleName.replace(packageName, '');
return Object.keys(exportsField).some((item) => item.includes(exportName));
}
}
return false;
}
and should be used in the condition which wraps the call for resolving Haste named modules
:
const isExportedSrc = isExportedSource(context, normalizePath(realModuleName));
if (context.allowHaste && !isDirectImport && !isExportedSrc) {
const normalizedName = normalizePath(realModuleName);
const result = resolveHasteName(context, normalizedName, platform);
if (result.type === "resolved") {
return result.resolution;
}
}
When I have this in place, all modules all resolved correctly and my app builds without issues :)
Any thoughts? Should I create a proposal PR for you guys? PLEASE let me know :) :)
Thank you very much!
from metro.
Maybe @robhogan or @huntie could you have look please? If you think this actually could be the solution I can open a PR as well 🥸. Thank you very much for your help and contribution!
from metro.
One additional important note...
If in the example above, I remove config.watchFolders = [".."];
option for metro, then it will look for packages only inside the current directory (single repo situation) and it will work. BUT if we have a monorepo, like in our case and we need to look for modules outside the app folder this problem re-occurs.
from metro.
@Bekaxp I wanted to verify your fix: Where do I put / call the isExportedSource
and isExportedSrc
functions?
from metro.
Hi @kraenhansen, thanks for trying it out.
Once you install all the dependencies of the repro above, you should have in the node_modules a package named metro-resolver
. In there you will find a file src/resolve.js
. You should put that code in there. The isExportedSource
should be checked in the condition for haste modules
=> if (context.allowHaste && !isDirectImport) {
, which should be around line 90 in the resolve.js
file.
I did some more testings with my function and is not perfect because actually some modules should be considered as haste
and should enter into that condition. But at least I think it shows that there is a problem how Metro is trying to resolve modules in a monorepo.
Also, if you run a react-native project standalone (not in a monorepo) then the Metro resolver works correctly. This problem only happens when we have a monorepo and the files which needs to be resolved are all over the place 😃.
For now I solved the problem with exporting our packages in a different way (package.json example):
{
"react-native": "./lib/native/index.mjs",
"exports": {
".": {
"types": "./lib/native/index.d.ts",
},
"./native": {
"types": "./lib/native/index.d.ts",
"import": "./lib/native/index.mjs",
"require": "./lib/native/index.cjs"
},
"./web": {
"types": "./lib/web/index.d.ts",
"import": "./lib/web/index.mjs",
"require": "./lib/web/index.cjs"
},
"./package.json": "./package.json"
},
}
Of course then we need to import it from @dp/mypackage
and not @dp/mypackage/native
. Then Metro is happy and is able to resolve it correctly. But like I said it is just a workaround. I would really like to see Metro resolving it correctly.
Thank you again for trying it out!
from metro.
I would really like to see Metro resolving it correctly.
I agree. As a React Native library developer, I'm experiencing the same issue.
I verified that simply disabling the if (context.allowHaste && !isDirectImport)
branch (adding && false
) in metro-resolver/src/resolve.js
makes a package exports
sub-path resolve correctly, which indicates that the PackageExportsResolve
works as expected.
It definitely looks like this code is thoroughly tested by packages/metro-resolver/src/tests/package-exports-test.js. I've found context.allowHaste
is true
when the tests are running, but the result
from returned by resolveHasteName
is { type: 'failed', candidates: undefined }
instead of it throwing.
from metro.
@kraenhansen OMG!!! You did it! It is fixed! It works 😃😃😃. I will patch it for our projects but I guess the next version will already have your fix in place.
Should I close the issue or leave it open until your PR is merged in?
Thank you very much for this!
from metro.
Related Issues (20)
- (Suggestion): Add SWC
- Could not find or use auto-linked framework 'CoreAudioTypes': framework 'CoreAudioTypes' not found HOT 4
- Duplicate files error while running was as react-native backend after reloading.
- React-Native ERROR Invariant Violation: `new NativeEventEmitter()` requires a non-null argument., "myApp" has not been registered. iOS only
- `react-dom/server` cannot be resolved with `unstable_enablePackageExports` and condition `browser` HOT 5
- FATAL EXCEPTION
- SHA-1 for file C:\Users\Eportal\AppData\Roaming\npm\node_modules\react-native\node_modules\metro-runtime\src\polyfills\require.js is not computed. HOT 2
- unstable_enableSymlinks does not seems to work HOT 15
- Property "watcher.unstable_workerThreads" shows warning message when running Metro HOT 3
- unstable_enableSymlinks not work with library with react components with Babel transpilation
- Android font not shown even after linking
- None of these files exist HOT 2
- Cannot find module @callstack/out-of-tree-platforms after run `yarn start --reset-cache` in rn-tester app HOT 1
- How to use it in android tv?
- [Question]: Why is Metro hot update not affected by project size, as always fast HOT 1
- Issues with redux-saga when using unstable_enablePackageExports
- [website] "Fast", "Scalable", and "Integrated" images don't load.
- error: bundling failed: SyntaxError: Unexpected reserved type (any, string, func, Boolean ) HOT 1
- [Question]: Custom logo and welcome message on Metro server launch? HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from metro.