A bug
Images located in other watchedFolders are not resolved properly in react native after upgrade.
Upgrade details:
The structure of my files looks like this:
๐ affiny
โ ๐ affiny-app
โ โ ๐ assets
โ โ โ ๐ผ splashscreen.png
โ โ ๐ src
โ โ ๐ app.specific.stuff.ts
โ ๐ affiny-web
โ โ ๐ assets
โ โ โ ๐ผ web.specific.image.png
โ โ ๐ src
โ โ ๐ web.specific.stuff.ts
โ ๐ affny-core
โ ๐ assets
โ โ ๐ผ not.specific.image.png
โ๐ src
โ ๐ not.specific.stuff.ts
and I have a rn-cli:
const path = require('path');
module.exports = {
watchFolders: [
path.resolve(__dirname, '../affiny-core'),
],
};
<Image src={require('affiny-app/assets/splashscreen.png')} />
and
<Image src={require('affiny-core/assets/not.specific.image.png')} />
<Image src={require('affiny-app/assets/splashscreen.png')} />
but image in my-project-core, eg:
<Image src={require('affiny-core/assets/not.specific.image.png')} />
are resulting in a gray screen:

Either:
I added a breakpoint here:

Before the upgrade, the packager url for assets was:
resolver.defaultAsset().uri;
// -> "http://localhost:8081/assets/assets/img/profile_capture/search.jpg?platform=ios&hash=51dac229328bf5d045e9401cb6cb3cb4"
after the upgrade:
resolver.defaultAsset().uri;
// -> "http://localhost:8081/affiny-core/assets/img/profile_capture/search.jpg?platform=ios&hash=51dac229328bf5d045e9401cb6cb3cb4"
Note above:
When asking for http://localhost:8081/affiny-core/assets/img/profile_capture/search.jpg?platform=ios&hash=51dac229328bf5d045e9401cb6cb3cb4, I get a 404.
Note that http://localhost:8081/assets/assets/img/profile_capture/search.jpg?platform=ios&hash=51dac229328bf5d045e9401cb6cb3cb4 works on both old RN/metro and new RN/metro.
Seems like https://github.com/facebook/metro/commit/ea980fd821abae27218dc1a4a3a0b9e454958bfd#diff-71293ea379a7cf006cd4648701a7c568 (https://github.com/facebook/metro/pull/299) is the root cause.
Maybe the RN packager is not aware of the public path.
I think it is a bit involving to do a repro here. Maybe you will have an idea with above investigation, else I will spend time doing a repro.
Node: v8.11.3
React Native: 0.57.7
Metro: 0.49.2
Thanks for reporting!
I'm gonna try to reproduce, from what I understand in your example the affiny-core folder is outside of the project root right?
Ok, I've been able to reproduce it, the issue was introduced in https://github.com/facebook/metro/commit/a711d73c1ce7c1ed0bc3393b8842b7621e6d3e69 ๐
Thanks for the detailed explanation of the issue, it was very helpful to debug it ๐
Very soon we want to force all watch folders to be inside the project root of metro, this way Metro can safely use the project root as the root of the http server that provides the assets and this will fix this issue.
In order to make this change, we'll change the meaning of the projectRoot, which will just be a common folder where all the different watchFolders reside (so it's going to be a breaking change).
I'm gonna check now if there's any short-term workaround to fix this issue in the meantime
Whaa so quick.
Thanks for the detailed explanation of the issue, it was very helpful to debug it ๐
You are welcome ! Was a pleasure :) Quite a funny bug to investigate !
Very soon we want to force all watch folders to be inside the project root of metro, this way Metro can safely use the project root as the root of the http server that provides the assets and this will fix this issue.
Do you mean that with my folder structure, in the future the project root will be the parent, i.e., in my example "affiny" ?
Looks great, interested to see how RN will know which is the main directory !
I'm gonna check now if there's any short-term workaround to fix this issue in the meantime.
Thank you man ! I was thinking of patch-packaging react-native with a
function resolveAssetSource(source: any): ?ResolvedAssetSource {
if (typeof source === 'object') {
return source;
}
const asset = AssetRegistry.getAssetByID(source);
+ asset.httpServerLocation = asset.httpServerLocation.replace("affiny-core", "assets");
if (!asset) {
return null;
}
const resolver = new AssetSourceResolver(
getDevServerURL(),
getScriptURL(),
asset,
);
if (_customSourceTransformer) {
return _customSourceTransformer(resolver);
}
return resolver.defaultAsset();
}
or something uggy like this. Happy to have something less ugly ;)
Do you mean that with my folder structure, in the future the project root will be the parent, i.e., in my example "affiny" ?
Looks great, interested to see how RN will know which is the main directory !
Exactly! yeah in order to do that change we have to figure out what changes are going to be needed in the RN config side.
I'm preparing a fix for the issue, it's not going to be the most beautiful thing but will do the trick for now
I'm preparing a fix for the issue, it's not going to be the most beautiful thing but will do the trick for now.
๐
Well done sir.
For record, I tried hard to apply the fix in my project:
I finally did this hack stuff:
diff --git a/node_modules/react-native/Libraries/Image/resolveAssetSource.js b/node_modules/react-native/Libraries/Image/resolveAssetSource.js
index 9c811e5..931953d 100644
--- a/node_modules/react-native/Libraries/Image/resolveAssetSource.js
+++ b/node_modules/react-native/Libraries/Image/resolveAssetSource.js
@@ -95,6 +95,11 @@ function resolveAssetSource(source: any): ?ResolvedAssetSource {
return null;
}
+ if (!asset.httpServerLocation.startsWith('/assets')) {
+ const parsedUrl = /\/[\w\-]+(\/.*)/.exec(asset.httpServerLocation);
+ asset.httpServerLocation = parsedUrl[1];
+ }
+
const resolver = new AssetSourceResolver(
getDevServerURL(),
getScriptURL(),
using patch package.
We aim to remove this in one two month, when migrating to RN 0.58 !
Edit the above "hack does work in dev but not when assets are bundled.
I will move my assets from the centralised directory to the main directory for now, even if that means duplication.
Just wanted to chime in here and say I am also having this issue, and trying to apply the fix was unsuccessful
In my case, to the watchFolder resides out of projectRoot.
Even the metro server accepts the request like.
http://localhost:8081/assets/../../../common/assets/images/image.png
The RN Android image system uses okhttp to send requests.
okhttp seems to prune the request as
http://localhost:8081/common/assets/images/image.png
Let's why 404 happens.
My folder structure is pretty much like @tychota.
Environment: RN 0.57.7, metro 0.48.3
Well, there is a dirty workaround for me.
To create a symbolic link in current project root to the shared folder.
ln -s ../../../common # In my project root
This seems make metro able to find files even without 0eaa74182 patch.
Most helpful comment
In my case, to the watchFolder resides out of projectRoot.
Even the metro server accepts the request like.
http://localhost:8081/assets/../../../common/assets/images/image.pngThe RN Android image system uses okhttp to send requests.
okhttp seems to prune the request as
http://localhost:8081/common/assets/images/image.pngLet's why 404 happens.
My folder structure is pretty much like @tychota.
Environment: RN 0.57.7, metro 0.48.3
Well, there is a dirty workaround for me.
To create a symbolic link in current project root to the shared folder.
ln -s ../../../common # In my project rootThis seems make metro able to find files even without 0eaa74182 patch.