Question about use of the module
stephane-r opened this issue · 2 comments
-
I'm submitting a ...
[ ] bug report
[ ] feature request
[ ] question about the decisions made in the repository
[x ] question about how to use this project -
Summary
Hi :)
I try to init React monorepo projet with React-Native, React web, and common modules. I would like try your nice module, but how use it after create the project structure ?
Thank you !
Hello, @stephane-r ! I'm really sorry for the late reply, it seems I had misconfigured github's notifications and I didn't see your question in time!
First, some remarks
This project is only meant to be a configuration builder that helps you set up the correct set of values to make React Native understand where your hoisted/symlinked dependencies are, if any (and this may include your monorepo modules).
react-native-monorepo-helper
is not a plugin of any sort; you merely require('react-native-monorepo-helper')
and "build" your rn-cli.config.js
's configuration according to your project's needs.
react-native-monorepo-helper
acts only in your React Native's project, and it only exists because:
- Metro doesn't follow symlinks (which are created when hoisting dependencies); and
- React Native seems to not be able to properly resolve hoisted dependencies that follow the new module resolution rules, for some reason (at least in my experience; that "Haste module map" is probably the most esoteric black box I've seen in ages).
Yarn has a wondeful explanation of what "hoisting" actually is: https://yarnpkg.com/blog/2018/02/15/nohoist/
Project layout
The helper assumes you either use:
- Yarn Workspaces; or
- Lerna.
It'll try to check for Lerna first. Supposing your project is properly configured and doesn't duplicate information, it'll correctly and exactly identify your monorepo's package/project folders by reading your lerna.json
(for Lerna) or package.json
(for Yarn Workspaces).
Converting your rn-cli.config.js
If you already have a monorepo project laid out, you only need to substitute whatever you have exported in rn-cli.config.js
by whatever the helper builds in the end. For example, suppose you currently have this in your rn-cli.config.js
:
const path = require('path');
module.exports = {
projectRoot: __dirname,
watchFolders: [
path.resolve(__dirname),
path.resolve(__dirname, '../non-project-module'),
],
port: 8090
};
A naive (or immediate) conversion would require you to:
- Extract
projectRoot
from the config (whatever value it is; most probably,__dirname
); - Create the helper by passing to it the
projectRoot
; - Provide additional configuration, if needed;
- Provide extra Metro/Haste configuration via
helper.defaultOptions()
; and - Export the result of
helper.generate()
.
A fully converted and correct (and quite verbose) rn-cli.config.js
using the config helper:
// Here are your old imports and configuration
const path = require('path');
const oldConfig = {
projectRoot: __dirname,
watchFolders: [
path.resolve(__dirname),
path.resolve(__dirname, '../non-project-module'),
],
port: 8090
};
// Import the config helper builder
const { metroConfigHelper } = require('react-native-monorepo-helper');
// Create the config helper (which is actually a builder)...
const helper =
// ... with your project's root folder
metroConfigHelper(oldConfig.projectRoot)
// ... and give to the builder your old configuration; the builder will override
// your default configuration whenever needed, otherwise its values are
// passed along as-is when `generate()` is called
.defaultConfig(oldConfig)
// ... optionally, the helper can properly configure your rn-cli.config.js
// to work with e.g. typeScript. See the documentation for more details.
// (But I confess that the documentation is lacking! Sorry! :< )
.typeScript(true);
// Create the new config
const newConfig = helper.generate();
// Export the new config
module.exports = newConfig;
Internally, react-native-monorepo-helper
will re-set projectRoot
, watchFolders
and some fields in resolver
with exactly the values that will allow for proper hoisted/symlinked dependency resolution in Haste (please see here for more details on how the final configuration object is built).
If your rn-cli.config.js
only has watchFolders
that refer to monorepo modules, you can omit it altogether. Otherwise, prefer to use helper.watchFolder()
instead.
Now, a simplified version of the previous example:
const path = require('path');
const { metroConfigHelper } = require('react-native-monorepo-helper');
module.exports = metroConfigHelper(projectRoot)
.defaultConfig({
port: 8090
})
.watchedFolder(path.resolve(__dirname, '../non-project-module')),
.typeScript(true)
.generate();
For more details on what fields are valid in defaultConfig()
, please refer to Metro's documentation.
About nohoist
and final remarks
Unfortunately some dependencies cannot be hoisted. For those dependencies, you need to tell your monorepo manager to avoid hoisting them, i.e. nohoist
ing.
By my knowledge, you need to nohoist
:
- Any official
react-native
dependencies ( e.g. thereact-native
package itself); and - Any React transformers and/or plugins (e.g.
react-native-typescript-transformer
).
You may also need to nohoist
packages that are meant to be react-native link
ed, but that's not necessarily a rule. If Metro/Haste is complaining about a missing dependency in the Haste module map
, try nohoisting it -- until we can figure out a better solution.
The way you nohoist
dependencies may vary depending on which package/monorepo manager you use.
Sorry if i dragged this for too long. Also, please do not hesitate to ask if you have more questions! :)
Closing the issue. If you still have any questions, or need to clear anything up, please do not hesitate to comment!