question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Asset paths (httpServerLocation) invalid when asset is in watchFolders

See original GitHub issue

Do you want to request a feature or report a bug? Bug

What is the current behavior? When I specify a directory in watchFolders from outside of my project root, the path reported via httpServerLocation is broken. For example, /symlinked-watch-folder/subdir/image@3x.png.

If the current behavior is a bug, please provide the steps to reproduce and a minimal repository on GitHub that we can yarn install and yarn test.

  1. Create a React Native project in one directory.
  2. Create a sibling directory called symlinked-watch-folder with a Node module in it named symlinked-module.
  3. Add assets to this module, specifically one in subdir/image@3x.png.
  4. In this module, reference the image like so: <Image source={require('../subdir/image.png')} />
  5. Now from the RN project directory, use npm to install the module: npm install ../symlinked-watch-folder.
  6. Make a component in your project root that displays the module’s component with the image in it.
  7. Run the React Native project. The image will not appear and a request for the image will 404 because the path is invalid.

What is the expected behavior? It looks like there are different valid locations, including: /assets/symlinked-module/subdir/image@3x.png /assets/subdir/image@3x.png /subdir/image@3x.png

More info Because this repo isn’t very publicly documented, I can’t tell exactly what’s going on. But it looks like getAssetData is trying to path.join these two paths: /assets ../symlinked-watch-folder/subdir/image@3x.png https://github.com/facebook/metro/blob/bba48f068c6e709187d36170d3e5d49b7e9f6169/packages/metro/src/Assets.js#L181 The ../ wipes out the /assets and we end up with a result that isn’t valid: /symlinked-watch-folder/subdir/image@3x.png

I believe the relative path is coming from here: https://github.com/facebook/metro/blob/bba48f068c6e709187d36170d3e5d49b7e9f6169/packages/metro/src/DeltaBundler/Serializers/getAssets.js#L44

The asset is being served correctly, it’s just that the httpServerLocation that’s being generated is not correct. I can access the asset just fine if I correct the path to one of these: /assets/symlinked-module/subdir/image@3x.png /assets/subdir/image@3x.png /subdir/image@3x.png

If there is some way to handle this using the documented options, please let me know. (cc: @rafeca )

Please provide your exact Metro configuration and mention your Metro, node, yarn/npm version and operating system. I’ve tried both Metro 0.47.1 and Metro 0.48.0 Node: 10.11.0 npm: 6.4.1 OS: macOS High Sierra

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:28
  • Comments:22 (1 by maintainers)

github_iconTop GitHub Comments

34reactions
mmazzarolocommented, Apr 26, 2022

FYI, I abstracted @vitramir fix in a module that I’m using in a monorepo: get-metro-android-assets-resolution-fix.js.

You can use it this way:

const {
  getMetroAndroidAssetsResolutionFix,
} = require("@rnup/build-tools");

const androidAssetsResolutionFix = getMetroAndroidAssetsResolutionFix({
  depth: 3,
});

module.exports = {
  transformer: {
    // Apply the Android assets resolution fix to the public path...
    publicPath: androidAssetsResolutionFix.publicPath,
  },
  server: {
    // ...and to the server middleware.
    enhanceMiddleware: (middleware) => {
      return androidAssetsResolutionFix.applyMiddleware(middleware);
    },
  }
};

Wondering if we could just default to a huge depth (e.g. 20) without drawbacks.

Edit: I also published it in react-native-monorepo-tools.

16reactions
vitramircommented, Oct 18, 2019

I got this issue while using yarn workspaces and here is how I solved it:

1)Our first issue is resources destination path outside of /assets directory. It breaks both debug server and bundle.

There is config transformer.publicPath which default value is /assets. So, I changed it to /assets/dir1/dir2/dir3 (because my watchDir is ‘…/…/…/’). Now it will generate valid destination path for resources outside of projectRoot. For example /assets/dir1/dir2/dir3/../../common/images/image.png. react-native bundle will work too.

2)The second issue with Metro Server. It should resolve requests for IOS (http://localhost:8081/assets/dir1/dir2/dir3/../../common/images/image.png) and Android (http://localhost:8081/assets/dir1/common/images/image.png) the same way.

I changed server.enhanceMiddleware config this way:

server: {
    enhanceMiddleware: (middleware, server) => {
      return (req, res, next) => {
        if(req.url.startsWith("/assets/dir1/dir2/dir3")){
          req.url = req.url.replace('/assets/dir1/dir2/dir3', '/assets');
        }else if(req.url.startsWith("/assets/dir1/dir2")){
          req.url = req.url.replace('/assets/dir1/dir2', '/assets/..');
        }else if(req.url.startsWith("/assets/dir1")){
          req.url = req.url.replace('/assets/dir1', '/assets/../..');
        }else if(req.url.startsWith("/assets")){
          req.url = req.url.replace('/assets', '/assets/../../..');
        }
        return middleware(req, res, next);
      };
    },
  },

I want to notice here two more things:

1)This code doesn’t use config value transformer.publicPath. I think it is wrong. https://github.com/facebook/metro/blob/d9c556c04dc863b334720ab7eed9c94bc2841995/packages/metro/src/Server.js#L306-L307

2)And another solution of this issue is to update code of assetUrlPath generation and replace ../ into something like __/ or _.._/.

For example, this line https://github.com/facebook/metro/blob/d9c556c04dc863b334720ab7eed9c94bc2841995/packages/metro/src/Assets.js#L197-L199 may become:

let assetUrlPath =path.join(publicPath, path.dirname(localPath).replace(/..\//g, '__/'));

Also we should add reverse operation at Server module. I can make PR with this changes if community approve the idea.


I am currently using this config and it works with yarn workspaces as expected:

module.exports = {
  watchFolders: [path.resolve(__dirname, '../../../')],
  server: {
    enhanceMiddleware: (middleware, server) => {
      return (req, res, next) => {
        if(req.url.startsWith("/assets/dir1/dir2/dir3")){
          req.url = req.url.replace('/assets/dir1/dir2/dir3', '/assets');
        }else if(req.url.startsWith("/assets/dir1/dir2")){
          req.url = req.url.replace('/assets/dir1/dir2', '/assets/..');
        }else if(req.url.startsWith("/assets/dir1")){
          req.url = req.url.replace('/assets/dir1', '/assets/../..');
        }else if(req.url.startsWith("/assets")){
          req.url = req.url.replace('/assets', '/assets/../../..');
        }
        return middleware(req, res, next);
      };
    },
  },
  transformer: {
    publicPath: '/assets/dir1/dir2/dir3',
    babelTransformerPath: path.resolve(
      __dirname,
      './metro-transformer/index.js'
    ),
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: false,
      },
    }),
  },
};

I don’t provide babelTransformer. Nothing interesting there.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to fix the asset file path in my Vue (Vite) application build?
I recently completed a small project in Vue but when I uploaded it to my server, I am just seeing a blank screen....
Read more >
Asset - Expo Documentation
An asset is any file that lives alongside the source code of your app that the app needs at runtime. Examples include images,...
Read more >
Bud with Bedrock setup incorrect asset URL's - Roots Discourse
When I compile my assets using bud, it uses the WP_SITEURL from the .env file to create the path to my assets. However,...
Read more >
Asset Folders - Hexo
Referencing images or other assets using normal markdown syntax and relative paths may lead to incorrect display on archive or index pages.
Read more >
Asset Pipeline - Ruby on Rails Guides
vendor/assets is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks. 2.1.1 Search paths. When...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found