How to import a common module in CRA using Yarn workspaces? - create-react-app

I have a monorepo using Yarn workspaces. I have one package based on create-react-app which should import from a common module (sibling package in the repo).
This common package doesn't contain any React components. Just business logic. Its source is transpiled from Typescript. The common package works fine with my backend code in other packages.
Out of the box this doesn't work with CRA and gives me an error:
./src/App.tsx
Module not found: Can't resolve '#gemini/common'
What do I need to do to configure CRA to find the module?
I found this open issue on CRA monorepo support, but I'm unsure how it relates and there's a lot of information in there.

Lets assume your structure is similar to
|- monorepo
|- package.json
|- packages
|- cra
| |- package.json
|
|- common
|- package.json
make sure workspaces are registered
// monorepo/packages.json
{
"workspaces": {
"packages": [
"packages/*"
]
},
}
the gemini package name need to match to required path
// monorepo/packages/gemini/package.json
{
"name": "#gemini/common"
}
and last, use it as dependency in cra
// monorepo/packages/cra/package.json
{
"dependencies": {
"#gemini/common": "*"
}
}
run yarn to link gemini in cra

Related

Snowpack with local npm packages

We have problem to run snowpack with our package structure.
Our structure:
adapters
app
core
presentation
Each package contains typescript and all are used in the app package.
"dependencies": {
"#project/adapters": "file:../../adapters",
"#project/core": "file:../../core",
"#project/presentation": "file:../../presentation",
}
I get the error Dependency Install Error: Package "#project/adapters/src/repositories/GradeFeedRepositoryImpl" not found. Have you installed it?
How do I need to configure snowpack, web pack, babel, ... to run this?
I have had success with packing modules (using: npm pack /path/to/module from the root of the module's folder) and adding the tarball to my package.json from a folder within the repo. e.g.,
"dependencies": {
...
"adapters": "file:packs/adapters-1.0.0.tgz"
...
}
Another option, see if making this edit to your snowpack.config.js file helps:
packageOptions: {
external: [
"#projects/adapters"
]
}

How to bundle dependencies in npm package?

I have a npm package which reference an other local package. It has a structure like so.
deploy
typescriptapp.tgz
references
mydependency
package.json
app.js
app.css
typescriptapp
package.json
webapp
My typescriptapp package.json has the following dependencies
"dependencies": {
"mydependency": "file:../references/mydependency"
},
My webapp package.json has the following dependencies
"dependencies": {
"typescriptapp": "file:../deploy/typescriptapp-1.0.0.tgz"
},
When I use npm pack it work fine, but it is not included in the tarball. I also move the tarball to a deploy folder
When I try npm install, it doesn't work because the reference folder does not exist in the deploy folder.
I also tried to change the dependencies for bundledDependencies
"bundledDependencies": [
"file:../references/mydependency"
]
But it does not seem to work either.
How do I pack my typescript app to be able to install it in my webapp with a single file?

Import local packages as modules not in the React-Native project

In a React-Native project, I found that if your local package has a valid package.json file, you can import that like a module in node_modules without a relative path .
For example,
app/ # a React-Native project
|- any_folder_for_packages/
| |- foo/
| |- package.json
| |- index.js
|- src/
| |- bar/
| |- bar.js
|- ...
In this case, you can import the package foo in the bar.js using either:
// with a relative path
import foo from '../../any_folder_for_packages/foo';
or
// like a module (without a relative path)
import foo from 'foo';
The packager will try to find and use any local module which contains a package.json matching the requirement.
The question
I have a local package which is not in the React-Native project, how can I import it
projects/
|- app/ # a React-Native project
| |- src/
| | |- bar/
| | |- bar.js # need to import the package foo as a module which is not in this project
| |- ...
|- other_paths/
|- foo/
|- package.json
|- index.js
I tried to use
// like a module (without a relative path)
import foo from 'foo';
but it failed to find the module.
How can I use react-native to find the package when packing for development or bundling for the production?
Answer given by #Plague doesn't work in react native. You have to configure your metro.config.js file to use a local package.
If Using Expo
In expo, metro.config.js file may not be visible, so in the root directory of your project, run this command;
npx expo customize metro.config.js
This will generate the file for you with some default configuration. Now to import any package, you have to modify two things here;
config.resolver.nodeModulesPaths, to include the package, and ;
config.watchFolders to keep a watch on the package.
In nutshell, metro.config.js file should be like this;
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");
const config = getDefaultConfig(__dirname);
const packagePath = "/full/path/to/package";
// here i added the packagePath to the list in the configuration to include it
config.resolver.nodeModulesPaths.push(packagePath);
// here i added the package again to another configuration key, to keep a watch on it if we make any changes.
config.watchFolders.push(packagePath);
module.exports = config;
That is it, but your code editor may report that module is not found, for that warning to remove, you can just run the command below;
npm install /path/to/package
BUT there is a problem with this
The dependencies in our package conflicts with that of main project.
So here is another way of installing a local package.
Using install-local
For this we don't need to make any changes to any file. We just need to install this package;
npm install install-local
And then install the local package using this;
npx install-local --save path/to/package
You will need to make your application aware of it through your root package.json files.
Confirm the new local module has a basic package.json configured.
For example:
{
"name": "foo",
"version": "0.0.1"
}
Ensure the package has an index.js and that it has something exported or export default.
For example, at the bottom of the index.js:
export default foo;
Then, simply save the package as a project dependency in your root module to via NPM. To do this and install it automatically into your node_modules you can use this command.
npm install --save file:other_paths/foo
note make sure you have the correct relative path from your general project package.json to your new module package.json.
Example, in this case it would be:
app/ # a React-Native project root
|- other_paths/
| |- foo/
| |- package.json # new module package json
| |- index.js
|- src/
| |- bar/
| |- bar.js
|- package.json # project package json
If set up correctly you should then be able reference the module anywhere in your project without relative path.
import Foo from 'foo';

Install NPM dependancies for sub package which is nested within my main project?

I have a main package.json for my project. I also have a component within my project which I'm publishing to NPM, so that requires its own package.json.
package.json
index.html
-folder
--component-folder
---package.json
Both package.json files define dependancies. At the moment I have to run npm install from both my project root and from component-folder. Is there a way of making it install dependancies for both when its only run from the project root?
Try using subpackage:
{
"name": "my-awesome-project",
"version": "2.5.1",
"subPackages": [
"packages/sub-package-1",
"packages/sub-package-2"
]
}
https://www.npmjs.com/package/subpackage

How to avoid React loading twice with Webpack when developing

Given the following directory structure:
my-project
|
|-- node_modules
|
|-- react
|-- module-x
|
|--node_modules
|
|--react
You can see both my-project and module-x require React. I have the same problem as described on this question, but the suggestion is to remove react from the package.json dependencies. I do that and it works fine, as long as no node_modules are installed in module-x, because Webpack will use React from my-project. But if I'm in the process of developing module-x and the node_modules are installed, Webpack uses React from both my-project and module-x.
Is there a way I could have Webpack make sure only one instance of React is used, even though it's required on two separate levels?
I know I could keep module-x in a separate directory when developing, but it seems like I'd have to publish it and then install it in my-project to test it, and that's not very efficient. I thought about npm link, but had no luck with it since it still has node_modules installed in module-x.
This here sounds a lot like the same challenge I'm having, but it doesn't seem like npm dedupe or Webpack's dedupe option would work. I'm probably not understanding some important detail.
This issue usually arises when using npm link. A linked module will resolve its dependencies in its own module tree, which is different from the one of the module that required it. As such, the npm link command installs peerDependencies as well as dependencies.
You can use resolve.alias to force require('react') to resolve to your local version of React.
resolve: {
alias: {
react: path.resolve('./node_modules/react'),
},
},
If you don’t want to (or can’t) modify the project configuration, there is a more straightforward solution: just npm link React itself back to your project:
# link the component
cd my-app
npm link ../my-react-component
# link its copy of React back to the app's React
cd ../my-react-component
npm link ../my-app/node_modules/react
Just in case it's useful for others, the solutions suggested above didn't work for me, I had to perform the following steps to solve it:
In the library:
Setup the libraries that are generating issues as peerDependencies in the package.json instead of dependencies or devDependencies, e.g. in my case react:
"peerDependencies": {
"react": "^16.8.6",
...
}
run npm install
build the library (in my case, with a rollup -c npm script
In my main app:
change the version of my library to point to my local project with a relative path in package.json, e.g.
"dependencies": {
"my-library": "file:../../libraries/my-library",
...
}
Add resolve.symlinks = false to my main app's webpack configuration
Add --preserve-symlinks-main and --preserve-symlinks to my package.json start script, e.g:
"scripts": {
"build": "set WEBPACK_CONFIG_FILE=all&& webpack",
"start": "set WEBPACK_CONFIG_FILE=all&& webpack && node --preserve-symlinks-main --preserve-symlinks dist/server.js",
}
run npm install
run npm run start
In the same vein as the accepted answer here's how you can achieve the desired outcome with Craco:
const path = require('path')
module.exports = {
webpack: {
configure: config => {
config = {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'react': path.resolve('./node_modules/react'),
},
},
}
// console.log(config)
return config
},
},
}
It's important to note you can't just pass resolve as a key, you have to do your own deep merge using the configure callback.