<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yuriy</title>
    <description>The latest articles on DEV Community by Yuriy (@ng_speedster).</description>
    <link>https://dev.to/ng_speedster</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F396709%2F13a41f9d-ec09-4d60-8182-c505b8703bcc.jpg</url>
      <title>DEV Community: Yuriy</title>
      <link>https://dev.to/ng_speedster</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ng_speedster"/>
    <language>en</language>
    <item>
      <title>React Monorepo with NPM</title>
      <dc:creator>Yuriy</dc:creator>
      <pubDate>Sat, 22 Jan 2022 11:37:44 +0000</pubDate>
      <link>https://dev.to/ng_speedster/react-monorepo-with-npm-4i94</link>
      <guid>https://dev.to/ng_speedster/react-monorepo-with-npm-4i94</guid>
      <description>&lt;h2&gt;
  
  
  Motivation - Setup react monorepo project using NPM workspaces
&lt;/h2&gt;

&lt;h3&gt;
  
  
  👉 More about &lt;a href="https://docs.npmjs.com/cli/v7/using-npm/workspaces#adding-dependencies-to-a-workspace" rel="noopener noreferrer"&gt;NPM Workspaces&lt;/a&gt;
&lt;/h3&gt;




&lt;h3&gt;
  
  
  Preferred structure
&lt;/h3&gt;

&lt;p&gt;We're going to have next project structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.
└── root/
    ├── node_modules/
    ├── packages/
    │   ├── app1/ (create-react-app)
    │   │   ├── src/
    │   │   └── package.json
    │   └── common/ (shared component library)
    │       └── components
    │       └── utils
    │       └── package.json
    ├── package.json
    └── package.lock.json


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  Implementation details 📚
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Project init via npm workspaces
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Step/1 - In the root project, create a package.json file with the following config.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm_workspaces"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./packages/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Step/2 Create &lt;strong&gt;packages&lt;/strong&gt; directory within the &lt;code&gt;root&lt;/code&gt; folder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Step/3 &lt;strong&gt;Create app1&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the packages folder&lt;/li&gt;
&lt;li&gt;Create new &lt;a href="https://reactjs.org/docs/create-a-new-react-app.html" rel="noopener noreferrer"&gt;CRA&lt;/a&gt; project using following command &lt;code&gt;npx create-react-app app1 --template typescript&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Step/4 Once we have &lt;code&gt;app1&lt;/code&gt; let's do a few changes 🛠&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;app1/package.json&lt;/code&gt; rename project name from &lt;code&gt;app1&lt;/code&gt; to &lt;code&gt;@ng_speedster/app1&lt;/code&gt;.
Why? Naming packages with @{domain}/{package-name} will group all the packages inside one common folder in node_modules. It allows for easier debugging if something some packages are not working properly or some else goes wrong. In my case, I have renamed the namekey in the app package as &lt;a class="mentioned-user" href="https://dev.to/ng_speedster"&gt;@ng_speedster&lt;/a&gt;/app1.&lt;/li&gt;
&lt;li&gt;Move packages &lt;code&gt;dependencies&lt;/code&gt; from &lt;code&gt;app1/package.json&lt;/code&gt; to &lt;code&gt;root/package.json&lt;/code&gt;. I'm doing that, cause I would like to have consistency between my react projects. In short I just want to have the same versions of react related dependencies:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@testing-library/jest-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.16.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@testing-library/react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^12.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@testing-library/user-event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^13.5.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^27.4.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^16.11.21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^17.0.38"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^17.0.11"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^17.0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^17.0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.5.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"web-vitals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.1.3"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Step/5 &lt;strong&gt;Create common&lt;/strong&gt; package&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basically, this step repeats step 3 &amp;amp; 4;&lt;/li&gt;
&lt;li&gt;Navigate to the packages folder&lt;/li&gt;
&lt;li&gt;Create new &lt;a href="https://reactjs.org/docs/create-a-new-react-app.html" rel="noopener noreferrer"&gt;CRA&lt;/a&gt; project using following command &lt;code&gt;npx create-react-app common --template typescript&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Move/merge packages &lt;code&gt;dependencies&lt;/code&gt; from &lt;code&gt;common/package.json&lt;/code&gt; to &lt;code&gt;root/package.json&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Step/6&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove node_modules from &lt;code&gt;app1&lt;/code&gt; and &lt;code&gt;common&lt;/code&gt; projects&lt;/li&gt;
&lt;li&gt;Navigate to the root folder and run &lt;code&gt;npm install&lt;/code&gt; -
It will install all the dependencies across all the packages into the root. You can then check the root node_modules folder to locate your package.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Projects is added, what's next?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Transpiling JSX into js from the shared component package
&lt;/h3&gt;

&lt;p&gt;As we are not transpiling (converting JSX into createElements) from our shared components package, we will rely on cra to do the transpilation step. It makes the process a whole lot easier. To do so, we have to customize babel by using react-app-rewired and customize-crapackages. It allows you to customize webpack configuration without ejecting react-scripts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Step/1 Installing and configuring react-app-rewired and customize-cra&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the root folder&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm workspace @ng_speedster/app add -D react-app-rewired customize-cra&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;app1/package.json&lt;/code&gt; make the next changes:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-app-rewired start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-app-rewired build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-app-rewired test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"eject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts eject"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Step/2 Override babel config by creating the config-overrides.js file in the app package.
```javascript
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;const path = require("path");&lt;br&gt;
  const { override, babelInclude } = require("customize-cra");&lt;br&gt;
  module.exports = function (config, env) {&lt;br&gt;
    return Object.assign(&lt;br&gt;
      config,&lt;br&gt;
      override(&lt;br&gt;
        babelInclude([&lt;br&gt;
          /* transpile (converting to es5) code in src/ and shared component library */&lt;br&gt;
          path.resolve("src"),&lt;br&gt;
          path.resolve("../common"),&lt;br&gt;
        ])&lt;br&gt;
      )(config, env)&lt;br&gt;
    );&lt;br&gt;
  };&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  - It instructs babel to transpile all the files in the app's src directory and shared component package.

---

## So that's it, now you can create component inside common/and import inside app package.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pyexv7hnzcd23xoet66f.png)

# Overall

Honestly, to set up a monorepo project using npm workspaces is not that painful but requires
to dig into some areas:

- [Get familiar with npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces#adding-dependencies-to-a-workspace)
- [react-app-rewire](https://www.npmjs.com/package/react-app-rewired)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ule3knxb6gwtlpkhtozr.png)

- [Customize-cra](https://www.npmjs.com/package/customize-cra) takes advantage of `react-app-rewired`'s `config-overrides.js` file. By importing `customize-cra` functions and exporting a few function calls wrapped in our `override` function, you can easily modify the underlying config objects (`webpack`, `webpack-dev-server`, `babel`, etc.) that make up `create-react-app`.

I didn't bring up, how to set up CI/CD builds and configure TS/JS tools - eslint/tslint etc, or how to manage multiple run-time builds, and many other things, therefore any
additional customization depends on your needs, hence, there are no CLI solutions like CRA from scratch etc...


### See final source code [here](https://github.com/Madebyspeedster/monorepo_approaches)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
    </item>
  </channel>
</rss>
