-
Notifications
You must be signed in to change notification settings - Fork 631
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
Modules required from outside of root directory does not find node_modules #7
Comments
Tested with fresh |
Hi @zth I'm facing a similar issue here. So, if you do the following setup:
Does it work? I agree it sucks to have two node_modules folder, but this could be a temporary solution for me until there's a proper solution from metro. |
So I managed to make this work following these steps:
"dependencies": { My folder organization is: I'm sure there's something bad about this structure because I ended up with different node_modules folders: Not sure about the side effects here (larger binary?) but this works for now. |
@fredbt there's a major problem with your solution. If you update a file in common, then the changes are not reflected (and loaded) into app1. app1 still has the old version of the file. Like you say, there is a copy of common in app1/node_modules/common. The copy is not updated when you edit the original lib. |
Thanks @jc-murray-1986 |
@fredbt yes they do. But that's not very useful when you're working on one of your common components. Every time you save the file you need to run npm install. It's not a very nice workflow at all |
We're having the same issue with a monorepo structure where we currently have web/mobile/shared folders. In our case the root also has a package.json to keep a single version of things like ESLint and Relay and importing those dependencies in the app doesn't work either. The web app is built with webpack and can handle this fine just by configuring module roots and/or module aliases. Adding every possible module directory to an rn-cli-config.js doesn't seem to make a difference:
Our initial solution was using shared as a local dependency ("file" in package.json which creates symlinks) and each package being completely separate. This kind of worked but caused some weird install issues where dependencies from shared got added or removed depending on where the install ran and having to manage multiple versions and configs of things like ESLint was really annoying. |
Has there been any progress on this issue? It seems to be a major show stopper for a unified codebase. |
Yes. If you do So one solution is to have all common dependencies be installed in the root directory, that can be resolved from anywhere. Another solution might be to use the extraNodeModules: {
'foobar': path.resolve(__dirname, '../common/node_modules/foobar'),
}, |
@jeanlauliac can you provide more information about your solution? The extraNodeModules link now resolves to a 404 page. |
I moved on onto other projects so I'm not sure what the status of this field in the configuration at the moment. @CompuIves , does the |
@reggie3 @CompuIves @jeanlauliac's syntax is the one I've been using. I'm working in a monorepo with yarn workspaces. I'm using a combination of getProjectRoots to tell metro where to find packages. And then using extraNodeModules to make sure that the local package goes looking for the
My use case is probably not the same as yours but I hope it helps. Also I'm using |
@willgriffiths thank you for that solution. Based on yours, I made a more generic one: const path = require("path");
const installedDependencies = require("./package.json").dependencies;
const extraNodeModules = {};
Object.keys(installedDependencies).forEach(dep => {
extraNodeModules[dep] = path.resolve(__dirname, "node_modules", dep);
});
module.exports = {
extraNodeModules: extraNodeModules
}; |
@willgriffiths and @Strate solutions work great. If you're using TS don't forget to add your shared code path to "rootDirs": [
".",
"../shared"
] /* List of root folders whose combined content represents the structure of the project at runtime. */, in |
The solution @willgriffiths and @Strate have posted is confusing to me: why do you have to tell And do you have to do this for every single dependency? If so, issues can arise in a workspace setup, because there's no guarantee if a dependency will be installed at the top-level |
i am in version RN0.57, latest version i still have this issue, any solutions? none of the solutions aboved worked |
With the 0.57 of react-native, it looks like the extraNodeModules feature doesn't work anymore? |
In RN 0.57 the format of the Metro config options has changed slightly, now the @RomualdPercereau are you configuring the |
This setup works when used with yarn nohoist. nohoist allows you to force modules to stay in project folders and stop them from being hoisted up to the monorepo root. RN doesn't work if Nohoist is how you can guarantee that the modules end up insider your RN project folder. Also, any library that has native code has to be no hoisted so that you can Be careful though, no hoisting everything reduces the benefits of yarn workspaces and increases install times so I only use it if I have to. My team and I are hoping that metro (and as a side note: react-scripts) start working nicer with yarn workspaces.
In RN if you import a component from "../../components/Button" and that Button component imports It starts in the component's folder "../../components/Button/node_modules/react-native" and if it doesn't find it node_modules it climbs up a level checks in node_modules .
By telling metro that react and react-native are located inside the React Native project folder then you override this behaviour so it goes looking in:
I start no hoisting or adding entries to extra node modules when I get a metro errors like:
|
Thanks for the detailed info @willgriffiths 😄 Your example with importing
Yep, agreed with the downsides. We actually ended up adding
Yes, I hope so too! So far it's been a bit of a hassle to get the react-native toolchain (metro) to work in a nice way with our old web-only yarn workspaces setup. Hopefully FB will have the resources to improve things, maybe the metro team and the yarn team can have a nice fun offsite together and work on these issues 😆 (Thanks to both teams for all the work they've put in to their tools so far!) |
I came to the same conclusion too! Yes I end up with a larger workspace but the benefits of not having to hunt these things down far outweigh the cons of a larger workspace size. As mentioned above, for RN 0.57 you have to put your
|
@rafeca - can we get some docs on the above? it's painful to update to the latest version of react-native on a project that uses rn-cli.config.js right now |
I share the same @brentvatne sentiment on this. I know that the new |
Hi! We added a util method in the You can call it by doing: const {convert} = require('metro-config');
module.exports = convert.convertOldToNew({ /* old config object */}); This works as a temporary workaround when upgrading to the latest metro version. |
Regarding the docs, I agree that having some quick migration guide will be helpful. @mmazzarolo , @brentvatne does any of you want to help creating such quick guide? It can be created in the docs folder and will appear in the website. I can help with clarifications around some parameters if needed 😃 |
I didn't know about the About the docs: I meant adding it in the release notes the next time (since it's release-specific). That said, I'll try to write a few lines in the weekend 👍 |
"dependencies": { |
I am totally lost about this issue :/
How could I import |
For the record, my final config file for making Storybook find the upper folder
|
The solution provided by @fiznool works. But in RN59, |
The solution of @fiznool works but if I import a file that needs transpiling by And adding Any idea ? |
@Edmond-XavierCollot |
I'm seeing the same thing too @Edmond-XavierCollot - thank you @denwakeup for the fix. Here's an updated const path = require('path');
module.exports = {
/**
* Ensure any imports inside the shared 'components' folder resolve to the local node_modules folder
*/
resolver: {
extraNodeModules: new Proxy({}, {
get: (target, name) => path.join(process.cwd(), `node_modules/${name}`),
}),
},
/**
* Add our workspace roots so that react native can find the source code for the included packages
* in the monorepo
*/
projectRoot: path.resolve(__dirname),
watchFolders: [
path.resolve(__dirname, "../../components"),
path.resolve(__dirname, "../../node_modules")
]
} |
Here is mine solution combining above approaches which is tested for RN 0.61.2, Typescript, Yarn Workspaces monorepo with React web(CRA) and React Native(without Expo) and shared folders which are not standalone packages (core, theme, ...). It supports absolute paths (I ommited |
Here's my solution for workspaces and importing shared code (on a non-Expo app) // package.json
"devDependencies": {
...
"expo-yarn-workspaces": "1.2.1"
},
"workspaces": {
"nohoist": [
"react",
"react-native",
"react-native-*",
"react-native/**",
"expo-yarn-workspaces"
]
} // metro.config.js
const { createMetroConfiguration } = require("expo-yarn-workspaces")
const config = createMetroConfiguration(__dirname)
// Make react-native import from files in other workspace resolve to node_modules in this dir
config.resolver.extraNodeModules["react"] = `${__dirname}/node_modules/react`
config.resolver.extraNodeModules["react-native"] = `${__dirname}/node_modules/react-native`
// Default metro config
config.transformer.getTransformOptions = async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
})
module.exports = config |
I'm still having problems on a react-native app with absolute imports. |
Something that worked for me, mixture of existing solutions, for an Expo app that is not yet part of a monorepo: "resolutions": {
"@company/api": "link:../../frontend/packages/api",
"@company/config": "link:../../frontend/packages/config",
},
"dependencies": {
"@company/api": "link:../../frontend/packages/api",
"@company/config": "link:../../frontend/packages/config",
} const path = require('path');
const { getDefaultConfig } = require('expo/metro-config');
const { mapValues } = require('lodash');
// Add there all the Company packages useful to this app
const CompanyPackagesRelative = {
'@company/config': '../../frontend/packages/config',
'@company/api': '../../frontend/packages/api',
};
const CompanyPackages = mapValues(CompanyPackagesRelative, (relativePath) =>
path.resolve(relativePath),
);
// Inspired by expo-yarn-workspace
// https://github.com/expo/expo/blob/master/packages/expo-yarn-workspaces/index.js
function createMetroConfiguration(projectPath) {
projectPath = path.resolve(projectPath);
const defaultConfig = getDefaultConfig(projectPath);
const watchFolders = [
...Object.values(CompanyPackages),
...defaultConfig.watchFolders,
];
const extraNodeModules = {
...CompanyPackages,
...defaultConfig.resolver.extraNodeModules,
};
// Should fix error "Unable to resolve module @babel/runtime/helpers/interopRequireDefault"
// See https://github.com/facebook/metro/issues/7#issuecomment-508129053
// See https://dushyant37.medium.com/how-to-import-files-from-outside-of-root-directory-with-react-native-metro-bundler-18207a348427
const extraNodeModulesProxy = new Proxy(extraNodeModules, {
get: (target, name) => {
if (target[name]) {
return target[name];
} else {
return path.join(projectPath, `node_modules/${name}`);
}
},
});
return {
...defaultConfig,
projectRoot: projectPath,
watchFolders,
resolver: {
...defaultConfig.resolver,
extraNodeModules: extraNodeModulesProxy,
},
};
}
module.exports = createMetroConfiguration(__dirname);; |
For expo eas monorepo users: I had to add: similar to what is in: In order to fix the error for using @slorber 's comment |
For non expo users using this in the root folder package.json:
here was the metro.config.js that worked for me:
|
Hi! I hope I post this in the right place, sorry if I'm not.
I'm trying to setup requiring files outside of the root project directory, as I'm building both a web app and a native app that'll share some code. I've configured this using
rn-cli.config.js
, and I can successfully import my shared files from outside of my root.However, the files imported from outside of the root does not find any modules from
node_modules
(React for example). Files required from inside of the project root does find them (which I guess is becausenode_modules
is in a parent directory to them). I just get aCannot resolve module 'React'
.The folder structure looks like this:
rn-cli.config.js:
If I add a package.json to
common
and installreact
there it works. So I guess this has to do with the packager walking the file upwards and not findingnode_modules
as it's in a sibling directory and not a parent directory?Am I missing something obvious here? I've tried clearing the cache etc. I'm on the latest Expo which I'm pretty sure uses RN 0.43.
Thanks in advance!
The text was updated successfully, but these errors were encountered: