Managing JavaScript projects in a MonoRepo - using Lerna

A presentation at Kul Kode in December 2020 in Drammen, Norway by Ronny Siikaluoma

Slide 1

Slide 1

Slide 2

Slide 2

Slide 3

Slide 3

Slide 4

Slide 4

Managing JavaScript projects in a MonoRepo Using Lerna

Slide 5

Slide 5

Agenda MultiRepo vs MonoRepo Simple Lerna Workflow Use cases Changelog automation

Slide 6

Slide 6

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

Slide 7

Slide 7

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

Slide 8

Slide 8

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

Slide 9

Slide 9

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

Slide 10

Slide 10

Slide 11

Slide 11

Slide 12

Slide 12

npm install -g lerna

Slide 13

Slide 13

npm lerna init

Slide 14

Slide 14

📁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” } }

Slide 15

Slide 15

📁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” }

Slide 16

Slide 16

📁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

Slide 17

Slide 17

📁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

Slide 18

Slide 18

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

Slide 19

Slide 19

📁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.

Slide 20

Slide 20

📁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

Slide 21

Slide 21

📁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

Slide 22

Slide 22

📁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

Slide 23

Slide 23

📁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

Slide 24

Slide 24

📁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

Slide 25

Slide 25

📁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

Slide 26

Slide 26

📁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

Slide 27

Slide 27

📁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.

Slide 28

Slide 28

📁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

Slide 29

Slide 29

{

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)

Slide 30

Slide 30

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

Slide 31

Slide 31

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

Slide 32

Slide 32

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

Slide 33

Slide 33

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

Slide 34

Slide 34

📁husbanken-design/ 📄 lerna.json 📄 package.json 📁 packages/ 📁 design/ 📄 package.json 📁 docs/ 📄 package.json 📁 static/ 📄 package.json 📁 tokens/ 📄 package.json #STG-777

Slide 35

Slide 35

📁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

Slide 36

Slide 36

📁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 ✔️

Slide 37

Slide 37

📁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

Slide 38

Slide 38

📁hb-angular-diverse/ 📁 node_modules/ 📄 package.json 📁 src/ 📦 5.7.3 #FRED-888

npm install @husbanken/design@5.7.3

Slide 39

Slide 39

📁hb-angular-diverse/ GIT TEKTON push npm build 📁 node_modules/ 📄 package.json 📁 src/ pull request 🕓 approved ✔️ #FRED-888

Slide 40

Slide 40

📁 hb-angular-diverse/ GIT TEKTON NEXUS npm publish 📦 📁 node_modules/ 📄 package.json 📁 src/ merge #FRED-888 8.7.7

Slide 41

Slide 41

📁@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

Slide 42

Slide 42

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

Slide 43

Slide 43

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

Slide 44

Slide 44

📁husbanken-design/ 📄 lerna.json 📄 package.json 📁 packages/ 📁 design/ 📄 package.json 📁 docs/ 📄 package.json 📁 diverse/ 📄 package.json 📁 static/ 📄 package.json 📁 tokens/ 📄 package.json #HDS-777

Slide 45

Slide 45

📁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

Slide 46

Slide 46

📁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 ✔️

Slide 47

Slide 47

📁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

Slide 48

Slide 48

📁@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

Slide 49

Slide 49

Changelogs Can be automated

Slide 50

Slide 50

Better Changelogs • Lerna follows Semantic Versioning (SemVer)

Slide 51

Slide 51

9.5.0 Major Minor Patch

Slide 52

Slide 52

Better Changelogs • Lerna follows Semantic Versioning (SemVer) • Lerna supports Conventional Commits

Slide 53

Slide 53

Slide 54

Slide 54

<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.

Slide 55

Slide 55

📁project/ { 📄 lerna.json “packages”: [ 📄 package.json “packages/*” 📁 packages/ ], “command”: { “publish”: { “conventionalCommits”: true }, “version”: { “message”: “chore(release): release” } } }

Slide 56

Slide 56

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

Slide 57

Slide 57

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)

Slide 58

Slide 58

? 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

Slide 59

Slide 59

Slide 60

Slide 60

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

Slide 61

Slide 61

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) $

Slide 62

Slide 62

Slide 63

Slide 63

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

Slide 64

Slide 64

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

Slide 65

Slide 65

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

Slide 66

Slide 66

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

Slide 67

Slide 67