Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encode ..'s in asset paths fixes #290 #533

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

imownbey
Copy link

@imownbey imownbey commented Mar 16, 2020

Summary
Android's http client will strip out '..'s from urls before sending a request. This causes projects using yarn workspaces with assets hosted outside of the project root to fail. For more information you can see #290 .

This fixes that by encoding ..'s as __'s inside getAssetData and then unencodes the __'s as ..'s in getAsset.

Other things I considered were moving path to a query param. This seems like it would require a change in React Native as well as Metro though so is maybe a bit more dangerous.

Caveat of this change is it will break anyone who has directories named __ which is probably okay, happy to change encoding to something else though.

Test plan

Updated tests to reflect the changed URLs.

@facebook-github-bot facebook-github-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 Mar 16, 2020
@imownbey imownbey changed the title Encode ..'s in asset paths to fix #290 Encode ..'s in asset paths fixes #290 Mar 18, 2020
@cpojer
Copy link
Contributor

cpojer commented Mar 20, 2020

I'm not a fan of this change. Why not just add all assets as a root or watchFolder?

@imownbey
Copy link
Author

imownbey commented Mar 24, 2020

@cpojer Often people like to have assets live alongside the javascript, and when you have a project broken up into yarn workspaces that can mean that assets live above the root. I agree this isn't the best solution but it seemed to be the one with the smallest footprint.

The current behavior is ..'s will be in URLs in iOS and stripped out in Android so it is definitely broken behavior even if you disagree with people using workspaces and seems like one people will continue to run into. You can see other PRs like react-native-community/cli#939 (comment) where this has had to be fixed in other parts of the workflow, so I think probably matching behavior is useful.

@cpojer
Copy link
Contributor

cpojer commented Mar 24, 2020

That doesn't answer my question, why not include all those files in roots/watch folders?

@imownbey
Copy link
Author

I believe the asset is always referenced from the projectRoot and not from watchFolders, although would be interested in understanding how I could fix this with watchFolders

const absolutePath = path.resolve(

@imownbey
Copy link
Author

imownbey commented Mar 24, 2020

Maybe to better illustrate in our project we have multiple yarn workspaces, one for a design system packages/designSystem/src/ and one for the app packages/app/[src, ios, android, etc]. We need to make projectRoot packages/app/ so when something in say packages/designSystem/src/Icons/X.jsx does a require('./icon.png') and then is used inside of app the asset relative path is ../designSystem/src/Icons/icon.png but OKHTTP strips out those first ../ so the path sent to metro is just designSystem/src/Icons/icon.png which 404s

@janicduplessis
Copy link
Contributor

@cpojer I think I pointed @imownbey towards this solution. This does happen for files included inside watchFolders (but not projectRoot). The case mentioned in the previous comment illustrates this. I guess an alternative to encoding ../ would be to have paths relative to the outer most watchFolder instead.

@cpojer
Copy link
Contributor

cpojer commented Mar 28, 2020

I'm in favor of merging this if we don't use a special symbol as part of the path that may cause conflicts. That seems hacky. Let's find an elegant solution that works.

@janicduplessis
Copy link
Contributor

@cpojer What about using some unique identifier like the asset hash (or whatever is the key of the asset in metro) and use that to build httpServerLocation. Maybe something like /assets/<hash>.<ext>. Then metro can extract that hash and lookup the path on disk to return the asset data.

@cpojer
Copy link
Contributor

cpojer commented Mar 29, 2020

I think that could work, yeah. I'm not sure how much work it is. We'd only have to do it for assets that are outside of the root, right?

@imownbey
Copy link
Author

Happy to look into that. Do you think the hash or moving the path to a param would be easier?

@cpojer I’d think it’d be better to request all assets the same way (by hash if moving to that) rather than changing based on if it’s in or out of the project dir, just for conformity sake. But if you think it’s better to one-off out of project files I can just do that. I just worry about bugs in one case and not the other being harder to track down.

@belemaire
Copy link
Contributor

@imownbey Thanks much for this PR 👍
We are actually running into this issue, and are now hot patching metro with your fix while waiting for it to get merged, or a better solution offered (the hacky workaround suggested in #290 (comment) won't work for us as we need to accommodate many different projects structures).
Just wanted to let you know that there is actually an issue with the code in your PR having to do with the replace statements. For example :

// OKHTTP (Android HTTP client) strips out any '..' from a URL so we replace them with 2 _'s.
const escapedLocalPath = localPath.replace(/\.\.\//, '__/');

The problem here that you are mentioning that you replace any '..' from an URL with __, while the code will actually only replace the first occurence (i.e ../ will be replace by __/ but ../../../ will be replaced by __/../../). The tests are actually only testing out a single level so I guess its slipped through.

Solution here is to simply add the global regex flag to the two replace statements you introduced in this PR.
i.e

localPath.replace(/\.\.\//g, '__/');

and

relativePath.replace(/__(\/|\\)/g, '..$1')

@imownbey
Copy link
Author

Ah nice catch @belemaire. Updated test and added that. It might be good to only replace ..'s at the start of the path, happy to do that if people would prefer.

@sibelius
Copy link

any progress on this?

@sota000 sota000 added this to Needs Triage in Metro Pull Requests Aug 2, 2021
@rvion
Copy link

rvion commented Jan 17, 2022

I'd love to have this too.
this is blocking for me as I share assets from a node_module above project root between several apps.

this allow assets to be found:

  watchFolders: [path.resolve(__dirname, '../../../node_modules')],

BUT on android, it doesn't work, because

/assets/../../../node_modules/foo/bar

is reduced to

/node_modules/foo/bar/

even before the request is dealt with. => so it ultimately is not found

@rvion
Copy link

rvion commented Jan 17, 2022

@imownbey @cpojer do you know a workaround for this ?

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.
Projects
Development

Successfully merging this pull request may close these issues.

None yet

7 participants