Managing JavaScript projects in a MonoRepo Using Lerna

Agenda MultiRepo vs MonoRepo Simple Lerna Workflow Use cases Changelog automation

The Problem Projects tend to grow Split them into sub-projects Create multiple repos Broken dependencies hell

MultiRepo MonoRepo 1 project

= 1 project 1:1 of repo to package 1:n of repo to package Slower refactoring Faster refactoring Forces decoupling Allow but doesn’t force decoupling More decentralized More centralized More individualistic More communal Harder to test Easier to test Harder to share config Easier to share config Harder to share dev tooling Easier to share dev tooling Multiple CI pipelines Single CI pipeline Many places to report issues Single place to report issues Good for simple interdependencies Good for complex interdependencies Faster setup, builds and tests Slower setup, builds, and tests Discourages consistency Encourages consistency More duplication Less duplication Harder to coordinate changes Easier to coordinate changes Harder to browse full project Easier to browse full project Harder to sync project deps Easier to sync project deps Smaller repo Larger repo Less activity More activity

MultiRepo MonoRepo 1 project

= 1 project 1:1 of repo to package 1:n of repo to package Slower refactoring Faster refactoring Forces decoupling Allow but doesn’t force decoupling More decentralized More centralized More individualistic More communal Harder to test Easier to test Harder to share config Easier to share config Harder to share dev tooling Easier to share dev tooling Multiple CI pipelines Single CI pipeline Many places to report issues Single place to report issues Good for simple interdependencies Good for complex interdependencies Faster setup, builds and tests Slower setup, builds, and tests Discourages consistency Encourages consistency More duplication Less duplication Harder to coordinate changes Easier to coordinate changes Harder to browse full project Easier to browse full project Harder to sync project deps Easier to sync project deps Smaller repo Larger repo Less activity More activity

Who Uses MonoRepos? Google Facebook Air BnB Pinterest Angular React RxJS Vue

npm install -g lerna

npm lerna init

πŸ“project/

npm lerna init πŸ“„ lerna.json npx: installed 708 in 56.182s πŸ“„ package.json lerna notice cli v3.20.2 πŸ“ packages/ lerna info Creating package.json lerna info Creating lerna.json lerna info Creating packages directory lerna success Initialized Lerna files { β€œname”: β€œroot”, β€œprivate”: true, β€œdevDependencies”: { β€œlerna”: β€œ^3.20.2” } }

πŸ“project/

npm lerna init πŸ“„ lerna.json npx: installed 708 in 56.182s πŸ“„ package.json lerna notice cli v3.20.2 πŸ“ packages/ lerna info Creating package.json lerna info Creating lerna.json lerna info Creating packages directory lerna success Initialized Lerna files { β€œpackages”: [ β€œpackages/*” ], β€œversion”: β€œ0.0.0” }

πŸ“project/

npm lerna init πŸ“„ lerna.json npx: installed 708 in 56.182s πŸ“„ package.json lerna notice cli v3.20.2 πŸ“ packages/ lerna info Creating package.json lerna info Creating lerna.json lerna info Creating packages directory lerna success Initialized Lerna files npm lerna init β€”independent { β€œpackages”: [ β€œpackages/*” } npx: installed 708 in 56.182s lerna notice cli v3.20.2 ], lerna info Creating package.json β€œversion”: β€œindependent”, lerna info Creating lerna.json lerna info Creating packages directory lerna success Initialized Lerna files

πŸ“lerna-demo/

git clone git@github.com:siiron/lerna-demo.git πŸ“„ lerna.json Cloning into β€˜lerna-demo’… πŸ“„ package.json Done. πŸ“ packages/ πŸ“ rs-btn/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ src/ πŸ“„ package.json

git clone git@github.com:siiron/lerna-demo.git πŸ“lerna-demo/ πŸ“„ lerna.json Cloning into β€˜lerna-demo’… πŸ“„ package.json Done. πŸ“ packages/ πŸ“ rs-btn/ πŸ“ src/ .js πŸ“„ package.json .scss πŸ“ rs-demo/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ src/ πŸ“„ package.json .scss

πŸ“lerna-demo/

git clone git@github.com:siiron/lerna-demo.git πŸ“ node_modules/ Cloning into β€˜lerna-demo’… πŸ“„ lerna.json Done. πŸ“„ package.json πŸ“ packages/ πŸ“ rs-btn/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ src/ πŸ“„ package.json cd lerna-demo && npm install Installing… Done.

πŸ“lerna-demo/

npm run bootstrap πŸ“ node_modules/ lerna bootstrap πŸ“„ lerna.json Bootstrapping 3 packages πŸ“„ package.json Installing external dependencies πŸ“ packages/ πŸ“ rs-btn/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json Symlinking packages and binaries Bootstrapped 3 packages

πŸ“lerna-demo/

npm run bootstrap πŸ“ node_modules/ lerna bootstrap πŸ“„ lerna.json Bootstrapping 3 packages πŸ“„ package.json Installing external dependencies πŸ“ packages/ πŸ“ rs-btn/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json Symlinking packages and binaries Bootstrapped 3 packages

πŸ“lerna-demo/

lerna bootstrap β€”hoist πŸ“ node_modules/ Bootstrapping 3 packages πŸ“„ lerna.json Installing external dependencies πŸ“„ package.json Installing hoisted dependencies into root πŸ“ packages/ πŸ“ rs-btn/ Pruning hoisted dependencies Finished pruning hoisted dependencies πŸ“ node_modules/ Finished installing in root πŸ“ src/ Symlinking packages and binaries πŸ“„ package.json Bootstrapped 3 packages πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json

πŸ“lerna-demo/

lerna add lodash πŸ“ node_modules/ Installed lodash in rs-btn πŸ“„ lerna.json Installed lodash in rs-demo πŸ“„ package.json Installed lodash in rs-style πŸ“ packages/ πŸ“ rs-btn/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json lerna add lodash -D

πŸ“lerna-demo/ πŸ“ node_modules/ πŸ“„ lerna.json πŸ“„ package.json πŸ“ packages/ πŸ“ rs-btn/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json

lerna add lodash –-scope @siiron/rs-btn Installed lodash in rs-btn

πŸ“lerna-demo/

lerna add lodash β€”ignore @siiron/rs-btn πŸ“ node_modules/ Installed lodash in rs-demo πŸ“„ lerna.json Installed lodash in rs-styles πŸ“„ package.json πŸ“ packages/ πŸ“ rs-btn/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json

πŸ“lerna-demo/

lerna β€”scope @siiron/rs-styles run build πŸ“ node_modules/ Building rs-styles… πŸ“„ lerna.json Done. πŸ“„ package.json πŸ“ packages/ πŸ“ rs-btn/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ dist/ πŸ“ src/ πŸ“„ package.json

πŸ“lerna-demo/

lerna run build πŸ“ node_modules/ Building rs-styles… πŸ“„ lerna.json Building rs-btn… πŸ“„ package.json Building rs-demo… πŸ“ packages/ πŸ“ rs-btn/ πŸ“ node_modules/ πŸ“ dist/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ build/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ dist/ πŸ“ src/ πŸ“„ package.json Done.

πŸ“lerna-demo/

lerna exec β€” rm –rf ./dist πŸ“ node_modules/ Removed ./dist in rs-styles… πŸ“„ lerna.json Removed ./dist in rs-btn… πŸ“„ package.json Done. πŸ“ packages/ πŸ“ rs-btn/ lerna β€”scope @siiron/rs-demo exec β€” rm –rf ./build πŸ“ node_modules/ Removed ./build in rs-demo… πŸ“ src/ Done. πŸ“„ package.json πŸ“ rs-demo/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json πŸ“ rs-styles/ πŸ“ node_modules/ πŸ“ src/ πŸ“„ package.json

{

lerna publish β€œpackages”: [ β€œpackages/*” ? Select a new version (currently 0.1.0) (Use arrow keys) > Patch (0.1.1) ], Minor (0.2.0) β€œversion”: β€œ0.1.1” β€œ0.1.0” Major (1.0.0) } Prepatch (0.1.1-alpha.0) Preminor (0.2.0-alpha.0) Premajor (1.0.0-alpha.0) Custom Prerelease Custom Version Changes: - @siiron/rs-btn: 0.1.0 => 0.1.1 - @siiron/rs-demo: 0.1.0 => 0.1.1 {

  • @siiron/rs-styles: 0.1.0 => 0.1.1 β€œpublishConfig”: { β€œregistry”: β€œhttps://registry.npmjs.org” } } ? Are you sure you want to publish these packages? (ynH)

LERNA PUBLISH β€”allow-branch β€”no-commit-hooks β€”legacy-auth β€”amend β€”no-git-tag-version β€”no-git-reset β€”conventional-commits β€”no-push β€”no-verify-access β€”conventional-graduate β€”preid β€”otp β€”conventional-prerelease β€”sign-git-commit β€”preid β€”changelog-preset β€”sign-git-tag β€”pre-dist-tag <tag> β€”exact β€”yes β€”registry <url> β€”force-publish β€”tag-version-prefix β€”tag-version-prefix β€”git-remote β€”canary β€”temp-tag β€”create-release β€”contents <dir> β€”yes β€”ignore-changes β€”dist-tag <tag> β€”ignore-scripts β€”git-head <sha> β€”include-merged-tags β€”graph-type <all|dependencies> β€”message β€”ignore-scripts β€”no-changelog β€”ignore-prepublish

primer/primer babel/babel facebook/create-react-app ReactTraining/react-router pugjs/pug facebook/jest reactotron/reactotron ElemeFE/mint-ui devtools-html/debugger.html alibaba/rax Turfjs/turf babel/babili storybooks/storybook xmppjs/xmpp.js wooorm/retext jumpsuit/jumpsuit strapi/strapi cloudflare/cf-ui colmena/colmena angus-c/just cerebral/cerebral nteract/nteract wooorm/remark webpack-preset/webpack-preset vazco/uniforms metal/metal.js steelbrain/pundle neos/neos-ui mosjs/mos lore/lore act-framework/act yvele/poosh noderaider/gridiron superawesomelabs/leo tocco/tocco-client zalando-incubator/tessellate sencha/extjs-reactor DigitalRiver/react-atlas forivall/tacoscript Boxable/box-ui flux-capacitor/flux-capacitor trepo/trepo-js spacedoc/spacedoc oknosoft/metadata.js uber-web/uber-eslint brittanica/brittanica sterjakovigor/vqua wireapp/wire-web-packages klis87/redux-saga-requests WordPress/gutenberg react-cosmos/react-cosmos jsbites/jedifocus-monorepo FoalTS/foal emotion-js/emotion Bamieh/reflow transloadit/uppy olistic/warriorjs gatsbyjs/gatsby feathersjs/feathers vuejs/vue-cli dvajs/dva umijs/umi SBoudrias/Inquirer.js pedronauck/docz tasitlabs/tasitsdk zeit/next.js react-bootstrap-table/reactbootstrap-table2 webpack/webpack-cli

We have a bug 🐞! Use-case example #1

startlan-esoknad β€’ Uses Husbanken Designsystem for css + images β€’ Uses HB Angular Diverse for components β€’ There is a bug that needs fixing 2 repos β€’ Create Jira-ticket for startlan-esoknad #EST-666 β€’ Create Jira-ticket for designsystem #STG-777 β€’ Create Jira-ticket for hb-angular-diverse #FRED-888 β€’ Link tickets together

πŸ“husbanken-design/ πŸ“„ lerna.json πŸ“„ package.json πŸ“ packages/ πŸ“ design/ πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ static/ πŸ“„ package.json πŸ“ tokens/ πŸ“„ package.json #STG-777

πŸ“husbanken-design/

lerna version πŸ“„ lerna.json ? Select a new version (currently 5.7.2) (Use arrow keys) πŸ“„ package.json Patch (5.7.3) πŸ“ packages/ πŸ“ design/ πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ static/ πŸ“„ package.json Minor (5.8.0) Major (6.0.0) Prepatch (5.7.3-alpha.0) Preminor (5.8.0-alpha.0) Premajor (6.0.0-alpha.0) Custom Prerelease Custom Version πŸ“ tokens/ πŸ“„ package.json Changes: - @husbanken/design: 5.7.2 => 5.7.3 - @husbanken/design-docs: 5.7.2 => 5.7.3 - @husbanken/design-static: 5.7.2 => 5.7.3 - @husbanken/design-tokens: 5.7.2 => 5.7.3 #STG-777

πŸ“husbanken-design/ GIT TEKTON push npm build πŸ“„ lerna.json πŸ“„ package.json πŸ“ packages/ πŸ“ design/ πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ static/ πŸ“„ package.json πŸ“ tokens/ πŸ“„ package.json #STG-777 pull request πŸ•“ approved βœ”οΈ

πŸ“husbanken-design/ GIT TEKTON NEXUS πŸ“„ lerna.json πŸ“„ package.json πŸ“ packages/ πŸ“ design/ πŸ“¦ 5.7.3 πŸ“¦ 5.7.3 πŸ“¦ 5.7.3 πŸ“¦ 5.7.3 πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ static/ πŸ“„ package.json πŸ“ tokens/ πŸ“„ package.json #STG-777 merge npm publish

πŸ“hb-angular-diverse/ πŸ“ node_modules/ πŸ“„ package.json πŸ“ src/ πŸ“¦ 5.7.3 #FRED-888

npm install @husbanken/design@5.7.3

πŸ“hb-angular-diverse/ GIT TEKTON push npm build πŸ“ node_modules/ πŸ“„ package.json πŸ“ src/ pull request πŸ•“ approved βœ”οΈ #FRED-888

πŸ“ hb-angular-diverse/ GIT TEKTON NEXUS npm publish πŸ“¦ πŸ“ node_modules/ πŸ“„ package.json πŸ“ src/ merge #FRED-888 8.7.7

πŸ“@husbanken/design MAKE GIT TEKTON πŸ“hb-angular-diverse/ NEXUS MAKE GIT TEKTON NEXUS πŸ“startlan-esoknad/ πŸ“ node_modules/ πŸ“¦ πŸ“¦ 5.7.3 8.7.7 πŸ“„ package.json πŸ“ src/

npm install @husbanken/design@5.7.3 > npm install hb-angular-diverse@8.7.7 #EST-666

We have a bug 🐞! Use-case example #2

startlan-esoknad β€’ Uses Husbanken Designsystem for css + images + components β€’ There is a bug that needs fixing in 1 repo β€’ Create Jira-ticket for startlan-esoknad #EST-666 β€’ Create Jira-ticket for designsystem #HDS-777 β€’ Link tickets together

πŸ“husbanken-design/ πŸ“„ lerna.json πŸ“„ package.json πŸ“ packages/ πŸ“ design/ πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ diverse/ πŸ“„ package.json πŸ“ static/ πŸ“„ package.json πŸ“ tokens/ πŸ“„ package.json #HDS-777

πŸ“husbanken-design/

lerna version πŸ“„ lerna.json ? Select a new version (currently 9.0.0) (Use arrow keys) πŸ“„ package.json Patch (9.0.1) πŸ“ packages/ πŸ“ design/ πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ diverse/ πŸ“„ package.json Minor (9.1.0) Major (10.0.0) Prepatch (9.0.1-alpha.0) Preminor (9.1.0-alpha.0) Premajor (10.0.0-alpha.0) Custom Prerelease Custom Version πŸ“ static/ πŸ“„ package.json πŸ“ tokens/ πŸ“„ package.json Changes: - @husbanken/design: 9.0.0 => 9.0.1 - @husbanken/design-docs: 9.0.0 => 9.0.1 - @husbanken/design-diverse: 9.0.0 => 9.0.1 - @husbanken/design-static: 9.0.0 => 9.0.1 - @husbanken/design-tokens: 9.0.0 => 9.0.1 #HDS-777

πŸ“husbanken-design/ GIT TEKTON push npm build πŸ“„ lerna.json πŸ“„ package.json πŸ“ packages/ πŸ“ design/ πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ diverse/ πŸ“„ package.json πŸ“ static/ πŸ“„ package.json πŸ“ tokens/ πŸ“„ package.json #STG-777 pull request πŸ•“ approved βœ”οΈ

πŸ“husbanken-design/ GIT TEKTON NEXUS πŸ“„ lerna.json πŸ“„ package.json πŸ“ packages/ πŸ“ design/ πŸ“„ package.json πŸ“ docs/ πŸ“„ package.json πŸ“ diverse/ merge npm publish πŸ“¦ 9.0.1 πŸ“¦ 9.0.1 πŸ“¦ 9.0.1 πŸ“¦ 9.0.1 πŸ“¦ 9.0.1 πŸ“„ package.json πŸ“ static/ πŸ“„ package.json πŸ“ tokens/ πŸ“„ package.json #STG-777

πŸ“@husbanken/designsystem MAKE GIT TEKTON NEXUS πŸ“startlan-esoknad/ πŸ“ node_modules/ πŸ“¦ 9.0.1 πŸ“„ package.json πŸ“ src/ πŸ“¦ 9.0.1 > npm install @husbanken/design@9.0.1 > npm install @husbanken/diverse@9.0.1 #EST-666

Changelogs Can be automated

Better Changelogs β€’ Lerna follows Semantic Versioning (SemVer)

9.5.0 Major Minor Patch

Better Changelogs β€’ Lerna follows Semantic Versioning (SemVer) β€’ Lerna supports Conventional Commits

<type>[optional scope]: <description> [optional body] [optional footer(s)] feat(components): change prop β€œicn” to β€œicon” in button component see HDS-007 for details BREAKING CHANGE: instances that uses β€œicn” will break.

πŸ“project/ { πŸ“„ lerna.json β€œpackages”: [ πŸ“„ package.json β€œpackages/*” πŸ“ packages/ ], β€œcommand”: { β€œpublish”: { β€œconventionalCommits”: true }, β€œversion”: { β€œmessage”: β€œchore(release): release” } } }

Better Changelogs β€’ Lerna follows Semantic Versioning (SemVer) β€’ Lerna supports Conventional Commits β€’ Commitizen – conventional commits utility

HOOS@HOOS-7510 MINGW64 /c/Projects/lerna-demo (develop) $ git cz cz-cli@4.2.2, cz-conventional-changelog@3.3.0 ? Select the type of change that you’re committing: (Use arrow keys) > feat: A new feature fix: A bug fix docs: Documentation only changes style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) refactor: A code change that neither fixes a bug nor adds a feature perf: A code change that improves performance test: Adding missing tests or correcting existing tests (Move up and down to reveal more choices)

? What is the scope of this change (e.g. component or file name): (press enter to skip) ? Write a short, imperative tense description of the change (max 94 chars): (20) add border to button ? Provide a longer description of the change: (press enter to skip) ? Are there any breaking changes? No ? Does this change affect any open issues? No

Better Changelogs β€’ Lerna follows Semantic Versioning (SemVer) β€’ Lerna supports Conventional Commits β€’ Commitizen – conventional commits utility β€’ Husky – git hook to the commit command β€’ Commitlint

HOOS@HOOS-7510 MINGW64 /c/Projects/lerna-demo (develop) $ git commit -m β€œAdd prop for shape + some button-styling” husky > commit-msg (node v12.18.3) β§— input: Add prop for shape + some button-styling βœ– subject may not be empty [subject-empty] βœ– type may not be empty [type-empty] βœ– found 2 problems, 0 warnings β“˜ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint husky > commit-msg hook failed (add β€”no-verify to bypass) HOOS@HOOS-7510 MINGW64 /c/Projects/lerna-demo (develop) $

Better Changelogs β€’ Lerna follows Semantic Versioning (SemVer) β€’ Lerna supports Conventional Commits β€’ Commitizen – conventional commits utility β€’ Husky – git hook that stops bad commits β€’ Commitlint

Summary βœ… Supports both NPM and Yarn βœ… Framework agnostic βœ… Build, test, publish one, some, or all modules βœ… Run arbitrary commands for one, some, or all modules βœ… β€œShare” dependencies across modules safely βœ… β€œLink” module-to-module dependencies safely βœ… Install unique dependencies for individual modules βœ… Distribute to a pre-release channel βœ… Parallelization of tasks βœ… Support changelog generation

https://github.com/siiron/lerna-demo

Resourses πŸ”— https://github.com/siiron/lerna-demo πŸ”— https://github.com/lerna/lerna πŸ”— https://semver.org/ πŸ”— https://www.conventionalcommits.org/en/v1.0.0/ πŸ”— https://github.com/commitizen/cz-cli πŸ”— https://github.com/commitizen/cz-conventional-changelog πŸ”— https://github.com/typicode/husky πŸ”— https://github.com/conventional-changelog/commitlint πŸ”— https://indepth.dev/posts/1040/release-management-in-angular-with-lerna