<?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: liyachun</title>
    <description>The latest articles on DEV Community by liyachun (@liyachun).</description>
    <link>https://dev.to/liyachun</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%2F415716%2Fb7add4c5-f088-421d-aae8-f8f604cce59d.png</url>
      <title>DEV Community: liyachun</title>
      <link>https://dev.to/liyachun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/liyachun"/>
    <language>en</language>
    <item>
      <title>JavaScript Monorepo Implemented by Lerna with Yarn Workspaces and Git Submodules</title>
      <dc:creator>liyachun</dc:creator>
      <pubDate>Sat, 21 Nov 2020 16:19:07 +0000</pubDate>
      <link>https://dev.to/liyachun/javascript-monorepo-by-lerna-with-yarn-workspaces-and-git-submodules-4189</link>
      <guid>https://dev.to/liyachun/javascript-monorepo-by-lerna-with-yarn-workspaces-and-git-submodules-4189</guid>
      <description>&lt;p&gt;This is me: 🐣.&lt;/p&gt;

&lt;p&gt;And my thoughts while implementing a JavaScript monorepo using lerna and yarn workspaces, as well as git submodules.&lt;/p&gt;

&lt;h1&gt;
  
  
  Disclaimers
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;The term &lt;code&gt;monorepo&lt;/code&gt; seems to be controversial when it comes to project structuring, some may prefer &lt;code&gt;multi-package&lt;/code&gt; (&lt;code&gt;lerna&lt;/code&gt; itself once was &lt;code&gt;A tool for managing javascript monorepos&lt;/code&gt;, it's now &lt;code&gt;A tool for managing JavaScript projects with multiple packages&lt;/code&gt;) .&lt;/li&gt;
&lt;li&gt;Not a step by step guide on tools, links to well maintained official docs will be provided.&lt;/li&gt;
&lt;li&gt;To record (&lt;strong&gt;not to debate&lt;/strong&gt;) my own thoughts and details-of-implementation on 'monorepo'. Corrections and guidances are welcome!&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Monorepo What and Why
&lt;/h1&gt;

&lt;h2&gt;
  
  
  TL; DR
&lt;/h2&gt;

&lt;p&gt;Back to those early days in my web projects as a noob, typically I would create repositories like one named &lt;code&gt;frontend&lt;/code&gt;, another one named &lt;code&gt;server&lt;/code&gt;, separately maintained and git-versioned. In the real world  two simple sub-repositories may not cover many of those complicated scenarios. Think about those lovely UI components you would like to pet and spread, and those clever utils/middlewares you want to extract and share.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;frontend &lt;span class="c"&gt;# a standalone repo&lt;/span&gt;
├── scripts
├── components
│   ├── some-lovely-ui
│   └── ...
├── index.html
└── ...

server &lt;span class="c"&gt;# a standalone repo&lt;/span&gt;
├── utils
│   ├── some-mighty-util
│   └── ...
├── middlewares
│   ├── some-clever-middleware
│   └── ...
├── router.js
├── app.js
├── package.json
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The noob structure&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes, we must protect our innovative ideas, by creating a few more standalone repositories, which should turn the whole project into a booming repo-society.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;webapp &lt;span class="c"&gt;# standalone&lt;/span&gt;
├── node_modules
├── package.json
├── .gitignore
├── .git
├── dotenvs
├── some-shell-script
├── some-lint-config
├── some-lang-config
├── some-ci-config
├── some-bundler-config
└── ...

server &lt;span class="c"&gt;# standalone as it was&lt;/span&gt;
├── node_modules
├── package.json
├── .gitignore
├── .git
├── dotenvs
├── same-old-confs
└── ...

whateverapp &lt;span class="c"&gt;# say, an electron-app&lt;/span&gt;
├── same-old-js &lt;span class="c"&gt;# a standalone javascript-domain repo, again&lt;/span&gt;
└── ...

some-lovely-ui &lt;span class="c"&gt;# need to be independently bootstraped and managed&lt;/span&gt;
├── same-old-setup
└── ...

some-mighty-util &lt;span class="c"&gt;# share almost identical structure&lt;/span&gt;
├── same-old-structure
└── ...

some-clever-middleware &lt;span class="c"&gt;# inherit absolute pain&lt;/span&gt;
├── same-old-pain
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The real world?&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With the help of the &lt;code&gt;link&lt;/code&gt; command provided by yarn (&lt;a href="https://classic.yarnpkg.com/en/docs/cli/link/" rel="noopener noreferrer"&gt;-link&lt;/a&gt;) or npm (&lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-link" rel="noopener noreferrer"&gt;-link&lt;/a&gt;), you can easily try development features across projects and packages on the fly. Say, if you are developing &lt;code&gt;Project A&lt;/code&gt; and &lt;code&gt;Package B&lt;/code&gt; simultaneously but as separate repos, and want use B as a dependency of A. Perform &lt;code&gt;yarn link&lt;/code&gt; under &lt;code&gt;Package B&lt;/code&gt; and &lt;code&gt;yarn link B&lt;/code&gt; under &lt;code&gt;Project A&lt;/code&gt;, you will find a symlink folder like &lt;code&gt;path/to/A/node_modules/B&lt;/code&gt;(same result when you perform a &lt;code&gt;yarn add&lt;/code&gt;) which links to your still-active local &lt;code&gt;Package B&lt;/code&gt;. Details beyond scope, dig for yourself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So far so good, until then you quickly find yourself annoyed by what everybody tends to get rid of: Repository Bootstrapping, during which, if you care about maintainability and  consistency, almost identical configurations have to be set for version control, dependency control, bundling, linting, CI, etc. meanwhile almost identical solutions have to be make to avoid madness, one of the baddest villains for example: The 'node_modules' 🕳️.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Silver Lining
&lt;/h2&gt;

&lt;p&gt;While dirty jobs must not be avoided, there is still a silver lining here—dirty jobs done once and for all, at least to get rid of the duplicated painfulness.&lt;/p&gt;

&lt;p&gt;The approach is simple. Step zero, since all the repositories we've built are meant to serve the same big blueprint, joining them into one single repository sounds just modern and intuitive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;the &lt;span class="o"&gt;[&lt;/span&gt;project] root
├── apps
│   ├── webapp
│   ├── server
│   ├── some-lovely-ui
│   ├── some-mighty-util
│   └── ...
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The what?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Such approach, looks like a history rewind&lt;/strong&gt;. As I've not-very-deeply learned, many ancient projects in corporations used to be structured in a &lt;code&gt;monolith&lt;/code&gt;ic way, but gradually suffer from maintenance and collaboration problems. Wait, still?&lt;/p&gt;

&lt;p&gt;What is the confusion? What is our goal by putting things together? Our wish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being saved from redundant jobs.&lt;/li&gt;
&lt;li&gt;Promote code consistency&lt;/li&gt;
&lt;li&gt;Version control made easy&lt;/li&gt;
&lt;li&gt;Best practices possible for all sub projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MANAGEABILITY, I think.&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Manageability Up
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;The &lt;span class="o"&gt;[&lt;/span&gt;project] root
├── apps
│   ├── webapp
│   │   ├── package.json &lt;span class="c"&gt;# sub-project manifests and deps&lt;/span&gt;
│   │   ├── lint-conifgs &lt;span class="c"&gt;# sub-project-wide lint, can extend or override global confs&lt;/span&gt;
│   │   ├── lang-configs &lt;span class="c"&gt;# sub-project-wide, can extend or override global confs&lt;/span&gt;
│   │   ├── bundler-configs &lt;span class="c"&gt;# sub-project-wide&lt;/span&gt;
│   │   ├── README.md
│   │   └── ...
│   ├── server
│   │   ├── package.json &lt;span class="c"&gt;# sub-project manifests and deps&lt;/span&gt;
│   │   ├── sub-project-level-confs
│   │   └── ...
│   ├── some-lovely-ui
│   │   ├── sub-project-level-stuff
│   │   └── ...
│   ├── some-clever-middleware
│   │   └── ...
│   └── ...
├── package.json &lt;span class="c"&gt;# global manifests, deps, resolutions, root-only deps (husky for instance)&lt;/span&gt;
├── .gitignore &lt;span class="c"&gt;# git once for all&lt;/span&gt;
├── .git &lt;span class="c"&gt;# git once for all&lt;/span&gt;
├── dotenvs &lt;span class="c"&gt;# dotenvs for all&lt;/span&gt;
├── shell-scripts &lt;span class="c"&gt;# maintainance for all&lt;/span&gt;
├── lint-configs &lt;span class="c"&gt;# lint for all&lt;/span&gt;
├── lang-configs &lt;span class="c"&gt;# helpers for all&lt;/span&gt;
├── ci-configs &lt;span class="c"&gt;# publish made handy&lt;/span&gt;
├── bundler-configs &lt;span class="c"&gt;# bundler for all&lt;/span&gt;
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The advanced structure&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here we've introduced several familiar faces into the root of the project directory, they are manifests or config files once only dwelled in each sub-project. This made these configs effect project-wide, allowing a baseline to be set and shared among all sub-projects, aka code consistency. A sub project may still hold its private-scope configs to override or extend the global standard—all thanks to the &lt;code&gt;inheritance-like&lt;/code&gt; feature in most dev toolchains—if a variation has to be made, in many cases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The model must be flexible. Take code linting for instance, to save lives, almost every well maintained framework, both frontend and backend included, has their own boilerplates and cli tools to the rescue, which usually have linting covered. Though many of them use &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;eslint&lt;/a&gt; and promote the &lt;a href="https://standardjs.com/" rel="noopener noreferrer"&gt;JavaScript Standard Style&lt;/a&gt;, there are ts / js variations, there are still &lt;a href="https://github.com/palantir/tslint" rel="noopener noreferrer"&gt;tslint&lt;/a&gt; advocates, there are complexity between lint plugins, and for sure there can be strict-or-not conventions in your company. Wise choices have to be made on your own responsibility.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bravo?
&lt;/h2&gt;

&lt;p&gt;Let's now bravely call our project a &lt;code&gt;monorepo&lt;/code&gt; &lt;strong&gt;already!&lt;/strong&gt; By the name we infer (?) that this is basically a project with all its ingredient parts in a single / monophonic repository. Meanwhile the ability of serving a project-wide but extendable development standard is made possible.&lt;/p&gt;

&lt;p&gt;Manageability achieved! Now who be the manager?&lt;/p&gt;

&lt;h1&gt;
  
  
  Sir, We have a problem！
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The installing process for a JS project is never satisfying. It creates fat and tricky &lt;code&gt;node_modules&lt;/code&gt;. Multiple projects in one?&lt;/p&gt;

&lt;p&gt;🍭 &lt;strong&gt;Not human-life-saving:&lt;/strong&gt; I have to &lt;code&gt;cd&lt;/code&gt; and perform &lt;code&gt;yarn add&lt;/code&gt; per sub-project folder.&lt;/p&gt;

&lt;p&gt;🔋 &lt;strong&gt;Not battery-life-saving:&lt;/strong&gt; A sub-project's deps are installed under its own directory. To the global scale, heavy loads of duplications are produced and will keep expand.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cleverer ideas and methods needed for handling sub-project versions, and cross-d relations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Introducing Lerna
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;In action, I didn't come across like having headaches resolving package dependencies, went search and found a solution. I've heard of &lt;code&gt;lerna&lt;/code&gt; in the first place, found it handy while performing semver bumps, and then got to know &lt;code&gt;monorepo&lt;/code&gt; goodness.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As described on its website, lerna is &lt;a href="https://lerna.js.org/" rel="noopener noreferrer"&gt;a tool for managing JavaScript projects with multiple packages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://github.com/lerna/lerna/tree/main/commands/init#lernainit" rel="noopener noreferrer"&gt;lerna init&lt;/a&gt; command creates a new (or updgrade an existing project into a) lerna project, which typically structures like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root
├── lerna.json
├── package.json
├── node_modules
└── packages
    ├── packageA
    │   ├── node_modules
    │   ├── package.json
    │   └── ...
    ├── packageB
    │   ├── node_modules
    │   ├── package.json
    │   └── ...
    └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like pretty much a &lt;a href="https://github.com/lerna/lerna#lernajson" rel="noopener noreferrer"&gt;lerna.json&lt;/a&gt; file introduced into our previous mono-structure. The file is the config file for your globally npm-installed or yarn-added lerna command line tool, a project-wide lerna should also be automatically added to &lt;code&gt;root/package.json/devDependencies&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;A minimal effective lerna config be like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;project/root&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;/lerna.json&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;"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="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="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"independent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"npmClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;npm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pnpm?&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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;p&gt;The &lt;code&gt;packages&lt;/code&gt; entry is a glob list that matches the locations of sub-projects, for instance, &lt;code&gt;"["clients/*", "services/*", "hero"]&lt;/code&gt; should make valid sub-projects (having a valid package.json) directly located under &lt;code&gt;clients&lt;/code&gt; and &lt;code&gt;services&lt;/code&gt;, as well of the exact &lt;code&gt;hero&lt;/code&gt; project which located under the root, recognized as lerna packages.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;version&lt;/code&gt; entry, if given a valid semver string, all packages should always share the same version number. "independent" means packages have different versions in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful Commands
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/lerna/lerna/tree/main/commands/bootstrap#readme" rel="noopener noreferrer"&gt;lerna bootstrap&lt;/a&gt; (once, from any location, project wide):&lt;/p&gt;

&lt;p&gt;🍭 Install dependencies for every single package (sub-project only, root dependencies not included), no per directory by-hand installs.&lt;/p&gt;

&lt;p&gt;🔋 With a &lt;code&gt;--hoist&lt;/code&gt; flag, can resolve duplication of common dependencies.&lt;/p&gt;

&lt;p&gt;⚔️ Link cross dependencies, same results (see &lt;a href="https://github.com/lerna/lerna/tree/main/commands/add#readme" rel="noopener noreferrer"&gt;lerna add&lt;/a&gt; and &lt;a href="https://github.com/lerna/lerna/tree/main/commands/link" rel="noopener noreferrer"&gt;lerna link&lt;/a&gt;) as performing &lt;code&gt;yarn link&lt;/code&gt;s per package&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/lerna/lerna/tree/main/commands/clean#lernaclean" rel="noopener noreferrer"&gt;lerna clean&lt;/a&gt;: Remove installs (purge the &lt;code&gt;node_modules&lt;/code&gt; folder) from every package (root excepted)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/lerna/lerna/tree/main/commands/version#lernaversion" rel="noopener noreferrer"&gt;lerna version&lt;/a&gt; and &lt;a href="https://github.com/lerna/lerna/tree/main/commands/publish" rel="noopener noreferrer"&gt;lerna publish&lt;/a&gt; as lerna's selling point:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftaccob0c96o420ait6a0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftaccob0c96o420ait6a0.png" alt="lerna version"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb7ds1v1h9vh913i1tuxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb7ds1v1h9vh913i1tuxt.png" alt="lerna publish"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BETTER READ THE DOCS FOR THIS SECTION BY YOURSELF&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You must be smart if you use conventional commits in your repo at the same time, it gives you much more advantages.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Conventional Commits
&lt;/h2&gt;

&lt;p&gt;A repo who follows the &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/" rel="noopener noreferrer"&gt;Conventional Commits&lt;/a&gt;  has its commit messages structured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;type&amp;gt;[optional scope]: &amp;lt;description&amp;gt;

[optional body]

[optional footer(s)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;We have &lt;a href="https://github.com/typicode/husky" rel="noopener noreferrer"&gt;husky&lt;/a&gt; the git hooks manager, as well as &lt;a href="https://commitizen.github.io/cz-cli/" rel="noopener noreferrer"&gt;commitizen&lt;/a&gt; the commit util. They create interactive prompts as you &lt;code&gt;git commit&lt;/code&gt;, simplifying the making of commit messages. Dig it for your self.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Informations provided in a conventional commit message correlate with the &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;Semantic Versioning&lt;/a&gt; spec very well. Typically, given that a full semver number can be &lt;code&gt;MAJOR.MINOR.PATCH-PRERELEASE&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;As a possible value of the &lt;em&gt;type&lt;/em&gt; section, a &lt;code&gt;fix&lt;/code&gt; commit should stand for a &lt;code&gt;PATCH&lt;/code&gt; semver bump,.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;feat&lt;/code&gt; commit stands for a &lt;code&gt;MINOR&lt;/code&gt; bump.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;BREAKING CHANGE&lt;/code&gt; &lt;em&gt;optional footer stands for a &lt;code&gt;MAJOR&lt;/code&gt; bump.&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This makes it easier to write automated tools on top of.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meanwhile with lerna, an illustrational workflow on conventional version bump&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current package versions (independently versioned)

&lt;ul&gt;
&lt;li&gt;Package &lt;a href="mailto:A@1.0.5"&gt;A@1.0.5&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Package &lt;a href="mailto:B@1.5.6"&gt;B@1.5.6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Package &lt;a href="mailto:C@2.0.5"&gt;C@2.0.5&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Package &lt;a href="mailto:D@1.0.0"&gt;D@1.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Make  some updates

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;MAJOR&lt;/code&gt; level performance updates on Package A, with &lt;code&gt;perf(package-a)!: bump electron version&lt;/code&gt; as the commit message.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;MINOR&lt;/code&gt; level feature updates on Package B, with a &lt;code&gt;feat(package-b): add folder draggability&lt;/code&gt; commit message.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;PATCH&lt;/code&gt; level fix on Package C, with a &lt;code&gt;fix(package-c/error-interception): fix type defs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No modifications on Package D.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Perform &lt;code&gt;lerna version&lt;/code&gt; with the &lt;code&gt;--conventional-commits&lt;/code&gt; flag, the process and the results

&lt;ol&gt;
&lt;li&gt;Read current versions from the &lt;code&gt;package.json&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt;Read from git history (and actual code changes), determine what commit was made in what package.&lt;/li&gt;
&lt;li&gt;Resolve commit messages, generate corresponding version bumps.&lt;/li&gt;
&lt;li&gt;Once get confirmed, will:

&lt;ul&gt;
&lt;li&gt;Modify &lt;code&gt;package.json/version&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt;Create a git commit as well as new version tags (the message format can be configued in &lt;code&gt;lerna.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Push to remote.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;New versions

&lt;ul&gt;
&lt;li&gt;Package &lt;a href="mailto:A@2.0.0"&gt;A@2.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Package &lt;a href="mailto:B@1.6.6"&gt;B@1.6.6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Package &lt;a href="mailto:C@2.0.6"&gt;C@2.0.6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Package &lt;a href="mailto:D@1.0.0"&gt;D@1.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You should read the docs for prerelease bumps and more capabilities utilizing lerna.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introducing Yarn Workspaces
&lt;/h1&gt;

&lt;p&gt;Using lerna to handle package installs, though is applicable, is not a very good idea. Especially when you are having root-only dependencies, and when you are using &lt;a href="https://classic.yarnpkg.com/lang/en/" rel="noopener noreferrer"&gt;Yarn&lt;/a&gt; (the classic version).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article only focuses on yarn 1.x only. Having introduced the currently-not-very-widely-supported but advanced &lt;a href="https://yarnpkg.com/features/pnp" rel="noopener noreferrer"&gt;Plug'n'Play&lt;/a&gt; feature, Yarn 2.x has had very significant diverges both conceptually and functionally. It's even incubating its own &lt;a href="https://yarnpkg.com/features/release-workflow" rel="noopener noreferrer"&gt;release-workflow&lt;/a&gt;, fyi.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Hoist in Lerna
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa1lyp1yfx4yu3u9be9ux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa1lyp1yfx4yu3u9be9ux.png" alt="Hoist in Lerna"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;says &lt;a href="https://classic.yarnpkg.com/blog/2017/08/02/introducing-workspaces/" rel="noopener noreferrer"&gt;this official blog&lt;/a&gt; from yarn, which also introduced yarn workspaces and its relationship with Lerna&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With the above said, I don't really remember since which version, to solve duplicated installation problem, Lerna does provide a &lt;a href="https://github.com/lerna/lerna/blob/main/doc/hoist.md" rel="noopener noreferrer"&gt;--hoist&lt;/a&gt; flag while it &lt;code&gt;bootstrap&lt;/code&gt;s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root
├── package.json &lt;span class="c"&gt;# deps: lerna&lt;/span&gt;
├── node_modules
│   ├── typescript @4.0.0 &lt;span class="c"&gt;# HOISTED because of being a common dep&lt;/span&gt;
│   ├── lodash ^4.17.10 &lt;span class="c"&gt;# HOISTED because of being a common dep&lt;/span&gt;
│   ├── lerna &lt;span class="c"&gt;# root only&lt;/span&gt;
│   └── ...
├── package A
│   ├── package.json &lt;span class="c"&gt;# deps: typescript @4.0.0, lodash ^4.17.10&lt;/span&gt;
│   ├── node_modules
│   │   ├── .bin
│   │   │   ├── tsc &lt;span class="c"&gt;# still got a tsc executable in its own scope&lt;/span&gt;
│   │   │   └── ...
│   │   └── ... &lt;span class="c"&gt;# typescript and lodash are HOISTED, won't be installed here&lt;/span&gt;
│   └── ...
├── package B
│   ├── package.json &lt;span class="c"&gt;# dpes: typescript @4.0.0, lodash ^4.17.10&lt;/span&gt;
│   ├── node_modules
│   │   ├── .bin
│   │   │   ├── tsc &lt;span class="c"&gt;# still got a tsc executable in its own scope&lt;/span&gt;
│   │   │   └── ...
│   │   └── ... &lt;span class="c"&gt;# typescript and lodash are HOISTED, won't be installed here&lt;/span&gt;
│   └── ...
├── package C
│   ├── package.json &lt;span class="c"&gt;# dpes: lodash ^4.17.20, wattf @1.0.0&lt;/span&gt;
│   ├── node_modules
│   │   ├── .bin
│   │   │   ├── wtfdotsh &lt;span class="c"&gt;# got an executable from wattf&lt;/span&gt;
│   │   │   └── ...
│   │   ├── lodash ^4.17.20 &lt;span class="c"&gt;# only package C asks for this version of lodash&lt;/span&gt;
│   │   ├── watf @1.0.0 &lt;span class="c"&gt;# package C's private treasure&lt;/span&gt;
│   │   └── ...
│   └── ...
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which means that common dependencies around the repo should get recognized and installed only once into the &lt;code&gt;project/root/node_modules&lt;/code&gt;, while the binary executable of each (if it has one) should still be accessible per &lt;code&gt;package/dir/node_modules/.bin&lt;/code&gt;, as required by package scripts. &lt;/p&gt;

&lt;p&gt;However, still, this absolutely very positive feature is only available during &lt;code&gt;lerna bootstrap&lt;/code&gt;, while in most common cases we are installing new packages during development, using a package manager.&lt;/p&gt;

&lt;p&gt;Plus, &lt;a href="https://github.com/lerna/lerna/blob/main/doc/hoist.md#disadvantages-with-hoisting" rel="noopener noreferrer"&gt;Lerna knows the disadvantages with hoisting&lt;/a&gt;, and it doesn't have a way to solve it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So far with Lerna:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🔭 Good for managing "macro"-scopic packages.&lt;/p&gt;

&lt;p&gt;🔬 Bad at resolving microscopic dependencies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Easy-to-break package symlinks.&lt;/li&gt;
&lt;li&gt;None-desirable overhead control.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Nohoist in Yarn
&lt;/h2&gt;

&lt;p&gt;Finally we welcome &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces" rel="noopener noreferrer"&gt;Yarn Workspaces&lt;/a&gt; on stage. And she comes with such a duty:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;She has Hoisting as her key feature.&lt;/li&gt;
&lt;li&gt;She knows &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces#toc-limitations-caveats" rel="noopener noreferrer"&gt;the caveats of hoisting&lt;/a&gt; as well, and provides a &lt;code&gt;—no-hoist&lt;/code&gt; option (very helpful, &lt;a href="https://classic.yarnpkg.com/blog/2018/02/15/nohoist/" rel="noopener noreferrer"&gt;PLEASE DO READ THIS)&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Its even easier to call her number, by modifying your existing &lt;code&gt;repo/root/package.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;root&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;/package.json&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;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pretty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;familliar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;setup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Lerna&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;"workspace-a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"workspace-b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"services/*"&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;p&gt;&lt;em&gt;This turn a repo into workspaces&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, instead of &lt;code&gt;lerna bootstrap&lt;/code&gt;, calling &lt;code&gt;yarn [install/add]&lt;/code&gt; anywhere in the repo and anytime during dev, hoisting will be applied (honestly, more time consuming, but tolerable by all means).&lt;/p&gt;

&lt;p&gt;What about nohoisting? Sometimes you don't want some package / workspace having some of there deps installed globally even though they share common versions. It's as simple as adding yet another entry with glob patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;root&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;/package.json&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;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&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="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;even&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Lerna&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&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="s2"&gt;"workspace-a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"workspace-b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"services/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;exceptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;globs&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"nohoist"&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;"**/react-native"&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-native/**"&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;DETAILS? AGAIN, PLEASE DO READ &lt;a href="https://classic.yarnpkg.com/blog/2018/02/15/nohoist" rel="noopener noreferrer"&gt;THIS FINE BLOG FROM YARN&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Friendship
&lt;/h2&gt;

&lt;p&gt;Its easy to notice similarities in the way Lerna and Yarn manifest a monorepo. In fact the integration of both is &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces#toc-how-does-it-compare-to-lerna" rel="noopener noreferrer"&gt;encouraged by Yarn&lt;/a&gt; and &lt;a href="https://github.com/lerna/lerna/pull/899" rel="noopener noreferrer"&gt;programmatically supported in Lerna&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;root&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;/lerna.json&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;"npmClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"useWorkspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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;p&gt;&lt;em&gt;This join hands together&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The above &lt;code&gt;useWorkspaces&lt;/code&gt;, once set to &lt;code&gt;true&lt;/code&gt;, we get Lerna to read package / workspace globs from &lt;code&gt;package.json&lt;/code&gt; instead. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our original goal&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x]  A manageable monorepo

&lt;ul&gt;
&lt;li&gt;[x]  Package / Workspace versioning made easy&lt;/li&gt;
&lt;li&gt;[x]  Low level dependency well controlled&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Not an Intruder - Git Submodules
&lt;/h1&gt;

&lt;p&gt;In my actual dev experience, I'd ran into scenarios as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have to pick some package out, cuz I want opensource it.&lt;/li&gt;
&lt;li&gt;I am not satisfied with some certain dependency, I'd better fork it and constantly modify and use it in action.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A none-perfect solution&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;With &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules" rel="noopener noreferrer"&gt;Git Submodules&lt;/a&gt;, we can &lt;a href="https://www.atlassian.com/git/tutorials/git-submodule" rel="noopener noreferrer"&gt;leverage git as an external dependency management tool&lt;/a&gt; as well. In a nutshell, it made possible placing a package inside a big repo, while having its private scope git storage. Details of implementation, please read the above links and &lt;a href="https://github.blog/2016-02-01-working-with-submodules/" rel="noopener noreferrer"&gt;this github blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a quick peek, see this sample project structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root
├── apps
│   ├── auth-web &lt;span class="c"&gt;# a lerna package / yarn workspace&lt;/span&gt;
│   ├── electron-app &lt;span class="c"&gt;# a lerna package / yarn workspace&lt;/span&gt;
│   └── ...
├── nest-services &lt;span class="c"&gt;# a lerna package / yarn workspace&lt;/span&gt;
├── submodules
│   ├── awesome-plugin &lt;span class="c"&gt;# MUST NOT be a lerna package / yarn workspace&lt;/span&gt;
│   │   ├── node_modules &lt;span class="c"&gt;# deps manually installed&lt;/span&gt;
│   │   ├── package.json &lt;span class="c"&gt;# nohoist anything&lt;/span&gt;
│   │   ├── .git &lt;span class="c"&gt;# havs its own git history with its own remote origin&lt;/span&gt;
│   ├── some-framework-adapter &lt;span class="c"&gt;# MUST NOT be a lerna package / yarn workspace&lt;/span&gt;
│   │   ├── .tsconfig.json &lt;span class="c"&gt;# private configs&lt;/span&gt;
│   │   ├── .ci-conf &lt;span class="c"&gt;# SHOULD have its own CI config&lt;/span&gt;
│   │   ├── .eslintrc &lt;span class="c"&gt;# MAY break code consistency.&lt;/span&gt;
│   │   ├── .git
│   │   └── ...
│   └── ...
├── package.json
├── lerna.json
├── .gitmodules &lt;span class="c"&gt;# the config for submodules&lt;/span&gt;
├── .git &lt;span class="c"&gt;# project git history&lt;/span&gt;
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And this config:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# [root]/.gitmodules&lt;/span&gt;

&lt;span class="nn"&gt;[submodule "submodules/awesome-plugin"]&lt;/span&gt;
    &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;submodules/awesome-plugin&lt;/span&gt;
    &lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;https://github.com/awesome-plugin&lt;/span&gt;
&lt;span class="nn"&gt;[submodule "submodules/some-framework-adapter"]&lt;/span&gt;
    &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;submodules/some-framework-adapter&lt;/span&gt;
    &lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;https://private.gitlab.com/some-framework-adapter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Caveats:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The implementation is tricky.&lt;/li&gt;
&lt;li&gt;Its recommended that a submodule should not be a Lerna package / workspace, meaning we should regard it as a completely standalone project, perform everything respectively.&lt;/li&gt;
&lt;li&gt;Can possibly break the code consistency.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;USE WITH CAUTION.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion - your own responsibility&lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;As I've been sticking with the Lerna-Yarn-Workspaces scheme for a while, questionmarks constantly emerge. Here are some notes of mine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Git commits must be strictly governed, or they could easily end up a mess. For instance, you should always avoid blending changes in various packages into one commit.&lt;/li&gt;
&lt;li&gt;Handle dependencies carefully. I've made mistakes while I was dealing with multiple &lt;a href="https://docs.nestjs.com/" rel="noopener noreferrer"&gt;Nestjs&lt;/a&gt; projects. Nest with the help of its CLI tool has its own &lt;a href="https://docs.nestjs.com/cli/monorepo#monorepo-mode" rel="noopener noreferrer"&gt;monorepo mode&lt;/a&gt;. I radically tried to merge the Nest monorepo into the Lerna-Yarn-Workspaces one. So I moved all nest-ly common deps (say: express, typescript, prettier plugins) to the project root, make every nest workspace a yarn workspace. This ended up with warnings everywhere, breaking the overall ecosystem. Turns out I had to leave nest inside its own playground and find back inner peace.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've also investigated the &lt;a href="https://github.com/microsoft/rushstack" rel="noopener noreferrer"&gt;Rushstack&lt;/a&gt; a bit, another monorepo implementation from Microsoft. It works best with &lt;code&gt;pnpm&lt;/code&gt; and has &lt;a href="https://github.com/microsoft/rushstack/issues/673#issuecomment-394859359" rel="noopener noreferrer"&gt;many conceptual differences from Lerna&lt;/a&gt;. For me the most significant is it doesn't encourage root package.json, and they have &lt;a href="https://github.com/microsoft/rushstack/issues/711" rel="noopener noreferrer"&gt;their ideas on husky&lt;/a&gt; and &lt;a href="https://github.com/microsoft/rushstack/pull/916" rel="noopener noreferrer"&gt;pre-commit git hooks&lt;/a&gt;. Moreover &lt;a href="https://rushjs.io/pages/maintainer/setup_new_repo/" rel="noopener noreferrer"&gt;its configs are somehow complicated&lt;/a&gt;, should be suitable for LARGE monorepos, in things like even detailed file permissions, I think.&lt;/p&gt;

&lt;p&gt;I still use Lerna and Yarn for my own convenience and simplicity. And now the final question: &lt;strong&gt;Should I always PUT EVERYTHING IN, company-wide for example, like what some big firms does; Or should I be cool, do it project by project; or even completely avoid this approach？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer? Maintaining monorepos isn't easy, weigh pros and cons on your own responsibility.&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.atlassian.com/git/tutorials/monorepos" rel="noopener noreferrer"&gt;Monorepos in Git | Atlassian Git Tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.toptal.com/front-end/guide-to-monorepos" rel="noopener noreferrer"&gt;Guide to Monorepos for Front-end Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b" rel="noopener noreferrer"&gt;Monorepos: Please don't!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules" rel="noopener noreferrer"&gt;Git - Submodules&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.nrwl.io/misconceptions-about-monorepos-monorepo-monolith-df1250d4b03c" rel="noopener noreferrer"&gt;Misconceptions about Monorepos: Monorepo != Monolith&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@maoberlehner/monorepos-in-the-wild-33c6eb246cb9" rel="noopener noreferrer"&gt;Monorepos in the Wild&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@brockreece/from-monolith-to-monorepo-19d78ffe9175" rel="noopener noreferrer"&gt;From Monolith to Monorepo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/blog/2017/08/02/introducing-workspaces/" rel="noopener noreferrer"&gt;Workspaces in Yarn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/microsoft/rushstack/issues/673#issuecomment-394859359" rel="noopener noreferrer"&gt;License Compliance Question · Issue #673 · microsoft/rushstack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=PvabBs_utr8&amp;amp;feature=youtu.be&amp;amp;t=16m24s" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=PvabBs_utr8&amp;amp;feature=youtu.be&amp;amp;t=16m24s&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/microsoft/rushstack/issues/711" rel="noopener noreferrer"&gt;[rush] Support Husky for git commit hooks · Issue #711 · microsoft/rushstack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/microsoft/rushstack/pull/916" rel="noopener noreferrer"&gt;[rush] Add support for git hooks by nchlswhttkr · Pull Request #916 · microsoft/rushstack&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
