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

Importing files outside main directory #232

Closed
mmahalwy opened this issue Jun 1, 2017 · 23 comments
Closed

Importing files outside main directory #232

mmahalwy opened this issue Jun 1, 2017 · 23 comments

Comments

@mmahalwy
Copy link

mmahalwy commented Jun 1, 2017

Where I have a directory for mobile and a directory for web which I'd like to share some common code (utils, etc). Currently, if I import a file outside the root directory of my generated app (outside where App.js is located), I get an error with no file found.

Is this by design? Will this be supported in the future and is this an Expo limitation or RN?

Thanks

@brentvatne
Copy link
Member

this is a behavior of the react-native packager: facebook/react-native#12241

@mmahalwy
Copy link
Author

mmahalwy commented Jun 6, 2017

@brentvatne thanks for the reply. Will this solution work with Expo?
facebook/react-native#12241 (comment)

@brentvatne
Copy link
Member

why not try it and find out? :P

yes

@kylebebak
Copy link

kylebebak commented Jul 12, 2017

@brentvatne
The solution @mmahalwy mentioned, i.e. creating rn-cli.config.js in the root of my project and using it to add the parent directory as a project root, doesn't work for me with Expo 18.

Wondering if I'm doing something wrong or whether this doesn't work anymore.

@doque
Copy link

doque commented Jul 14, 2017

Can confirm that this solution does not work. There is currently no way of importing a file from outside the app directory. Using symlinks doesn't work either.

@RobertWSaunders
Copy link

One suggestion and workaround I will add is that you could bundle the common files into a npm package and publish it on the npm registry, then you can add it as a dependency to your projects.

@brentvatne
Copy link
Member

Metro Bundler, which is used by React Native as the packager (the React Native equivalent to Webpack) doesn't support symlinks. As for rn-cli.config.js, it does still work but it's just hard to configure -- we use it on our internal "monorepo" in order to load local versions of our npm packages at Expo. Make sure in app.json, under "expo", you have:

"packagerOpts": {
    "projectRoots": "",
    "assetExts": ["ttf"],
    "config": "rn-cli.config.js"
 },

An example of a rn-cli.config.js:

const path = require('path');

module.exports = {
  getProjectRoots() {
    return [path.join(__dirname, '..', 'other-dir'), __dirname];
  }
};

There really needs to be some better documentation on this :( If someone gets it working, can you write it up?

@robdonn
Copy link

robdonn commented Aug 6, 2017

@brentvatne I can't seem to get the packager to accept the other root directories. How do you import them? Do you need to reference the path or just call the component itself?

1 - import 'component' from '../other-dir/component'
2 - import 'component' from 'component'

I'm trying to get a monorepo working similar to yours but I just can't seem to crack this.

My project directory

- apps
-- native (CRNA root)
--- app.json
--- index.js
--- package.json
- components
-- component-1
--- index.js
--- package.json
-- component-2
--- index.js
--- package.json
lerna.json
package.json

app.json

{
  "expo": {
    "sdkVersion": "19.0.0"
  },
  "packagerOpts": {
    "projectRoots": "",
    "config": "rn-cli.config.js"
 }
}

rn-cli.config.js

const path = require('path');

module.exports = {
  getProjectRoots() {
    return [
      path.join(__dirname, '..', '..', 'components'),
      __dirname
    ];
  }
};

@robdonn
Copy link

robdonn commented Aug 6, 2017

Nevermind! I'm an idiot! I misunderstood "under expo" in the app.json - It's all working now.

app.json

{
  "expo": {
    "sdkVersion": "19.0.0",
    "packagerOpts": {
      "projectRoots": "",
      "config": "rn-cli.config.js"
    }
  }
}

@dariocravero
Copy link

@robdonn @brentvatne did this work for you when the module used react and react-native as peer dependencies? After updating to the latest everything (my project had a very old crna-scripts), rn-cli.config.js was being accepted with that config but no matter what I tried, it kept on failing to recognise the linked module.

There's nothing fancy going on with the library (https://github.com/viewsdx/keyboard-avoiding-and-dismissing-view), just one file that exports a component and imports react and some stuff from react-native. Both, react and react-native are marked as peerDependencies and in the latest yarn (1.1.0) they don't get installed at all.

Using the config above it picks up the module alright but it fails to load react with Module does not exist in the module map:

screen shot 2017-09-25 at 20 40 25

If react is a dev dependency (with the same dependencies that my app is using), it goes even crazier because of duplicate module definitions:
screen shot 2017-09-25 at 20 43 07

It doesn't matter if my module comes before or after __dirname in getProjectRoots or if I reset the cache in between. The moment the module is linked it goes boom. :(

It is important to say that it works fine if my linked module doesn't use react, react-native or any other dependency that the project I'm linking from uses.

I wonder if anyone ran into this and got around it somehow?

Do you reckon I should be posting this at facebook/metro/issues/1 instead?

@roblafeve
Copy link

@dariocravero did you ever find a solution to this problem? This is exactly what I'm up against right now.

@pankaj-arunsingh
Copy link

This is crazy. Please post a solution to this problem if anyone finds it.

@roblafeve
Copy link

roblafeve commented Nov 12, 2017

@pankaj-arunsingh I can’t find the original issue where I finally found this but basically add only the external module path you want to import. In my case my module had a dependency on React and React-Native so those are added as extraNodeModules. Hope this helps.

Inside rn-cli.config.js:

var path = require("path");
const metroBundler = require('metro-bundler');

var config = {
  extraNodeModules: {
    "react-native": path.resolve(__dirname, "node_modules/react-native"),
    "react": path.resolve(__dirname, "node_modules/react"),
  },
  getProjectRoots() {
    return [
      // Keep your project directory.
      path.resolve(__dirname),
      path.resolve(__dirname, "../native"), // path to the external module
    ];
  }
}
module.exports = config;

@brentvatne
Copy link
Member

@pankaj-arunsingh - post to https://github.com/facebook/metro-bundler and be sure to fill out their issue template if you think this should be easier or whatever! @roblafeve's solution works

@dariocravero
Copy link

dariocravero commented Dec 2, 2017

In case anyone is stuck with this and until the different issues are fixed, I made a little guide on how to use yarn workspaces with Create React App and Create React Native App (Expo) to share common code across. Hope you find it handy! https://learn.viewsdx.com/how-to-use-yarn-workspaces-with-create-react-app-and-create-react-native-app-expo-to-share-common-ea27bc4bad62 https://medium.com/viewsdx/how-to-use-yarn-workspaces-with-create-react-app-and-create-react-native-app-expo-to-share-common-ea27bc4bad62

@jonasdumas
Copy link

jonasdumas commented Dec 19, 2017

@dariocravero your link didn't work ..
But i found it tru the research on medium. Here's the URL that works for me:
https://medium.com/viewsdx/how-to-use-yarn-workspaces-with-create-react-app-and-create-react-native-app-expo-to-share-common-ea27bc4bad62

@dariocravero
Copy link

Thanks @jonasdumas, we unlinked that domain from medium and I forgot to update the link here!

@dalinarkholin
Copy link

@dariocravero I got it working using your guide, however the nature of my project requires using a classic react-native project. After ejecting are their some steps to get things working?

Let me know thanks!

@dalinarkholin
Copy link

dalinarkholin commented Dec 30, 2017

@dariocravero and anyone using his crna-make-symlinks-for-yarn-workspaces module, if you are running a classic react native project(or a ejected crna project) you can remove link('expo', root, from); from the script to prevent errors on a second call of it. I am personally running the script on every react-native start so that I don't have to worry about it. Without making that change I would get yelled at.

@exhibition315
Copy link

@roblafeve your solution is working!!!
I add "node_modules/redux" in extraNodeModules, it work very well!!!

@nikodunk
Copy link

nikodunk commented Jul 6, 2022

In 2022, a variant of this is working for us. Above posted solutions didn't seem to work anymore, and the linked article was not expo-specific (didn't include the defaultConfig, which leads to assets missing when compiling a binary on EAS). Solution below:

// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config')
const path = require('path')
const config = getDefaultConfig(__dirname)

const extraNodeModules = {
  common: path.resolve(__dirname + '/../common')
}

config.watchFolders = [path.resolve(__dirname + '/../common')]

config.resolver.extraNodeModules = new Proxy(extraNodeModules, {
  get: (target, name) =>
    //redirects dependencies referenced from common/ to local node_modules
    name in target
      ? target[name]
      : path.join(process.cwd(), `node_modules/${name}`)
})

module.exports = config

@wagnercsfilho
Copy link

Here's another 2022 update. It seems the only solution that worked for me was the official workspace documentation. Here it is:

// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");
const path = require("path");
const config = getDefaultConfig(__dirname);

const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, "../..");

const monorepoPackages = {
  blocks: path.resolve(workspaceRoot, "packages/common"),
};

config.watchFolders = [__dirname, ...Object.values(monorepoPackages)];

config.resolver.extraNodeModules = monorepoPackages;

config.resolver.nodeModulesPaths = [
  path.resolve(projectRoot, "node_modules"),
  path.resolve(workspaceRoot, "node_modules"),
];

config.resolver.disableHierarchicalLookup = true;

module.exports = config;

@zealousFred
Copy link

Is there a similar solution for using static assets outside of the main directory?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

16 participants