{"componentChunkName":"component---src-templates-blog-post-js","path":"/share-angular-libraries-with-lerna/","result":{"data":{"site":{"siteMetadata":{"title":"Fabrizio Fortunato","author":"Fabrizio Fortunato","siteUrl":"https://izifortune.com"}},"markdownRemark":{"id":"f0808f4f-0a8c-5a40-9a09-9ef8eee037dd","excerpt":"Managing multiple Angular libraries it’s not a\ntrivial task for a developer or a team. How to structure your\ncode not only shape the future of your codebase but…","html":"<p>Managing multiple Angular libraries it’s not a\ntrivial task for a developer or a team. How to structure your\ncode not only shape the future of your codebase but also the\nprocess that you and your team have to define around it.\nMany companies are now looking to follow the big software\ncompanies and using monorepositories. I’ve blogged already on\nthe topic last year\n<a href=\"https://izifortune.com/multiple-packages-repository-with-angular/\">here</a> but\nnow that the tooling landscape has\nchanged it is time to revisit my post with a fresher approach.</p>\n<h2>Angular tooling panorama</h2>\n<p>Considering when <em>Angular 2</em> first appeared, the tooling, community\nand also general knowledge on how to manage Angular projects\nhas changed a lot. The <em>angular/cli</em> team is doing a great\njob responding to the community needs and including missing\nfeatures to manage multiple projects.</p>\n<p>A clear example of this is the possibility to generate\nand manage Angular libraries directly through the cli.\nUsing <em>ng-packagr</em>, behind the scene, we can now create and build\nshared libraries between projects in a single repository.</p>\n<p>Another good example of how things are changed from was\nwe started, is the great contribution that the <em>NRWL</em> team\nis providing by providing new ways to manage\nnot only Angular applications and libraries, but also\nbackend projects in a single repository.</p>\n<h2>Organisation needs</h2>\n<p>Deciding how to structure the codebase of a project, or\nof an entire company is not an easy task. The number of\nconventions and different process that vary between\nteams can be daunting. Trying to change\nexisting codebases is even more difficult, where we can\nencounter developers friction.</p>\n<p>The <em>Angular</em> team doesn’t enforce any specific\npractice managing a monorepository, giving the\nfreedom to the developers to architect around their\ntheir needs. <a href=\"https://nrwl.io/\"><em>NRWL</em></a>,\nan open source toolkit to manage monorepository,\non the other hand, is more opinionated.\nThe main idea behind using <em>NX</em> is that all the people\nworking in the monorepo are responsible for keeping\nall the projects and libraries in sync.</p>\n<p>In an ideal world all our libraries and projects\nshould be always updated to the latest version,\nbut in reality, each team is working\nagainst a defined backlog and specific stories through the\nsprint. Taking the responsibilities of updating projects\nor libraries across all the teams can and probably will have an\nimpact on the capacity of a team, maybe causing delays to\ndeliver some stories.</p>\n<p>Another approach is possible, and we have been following it for the\npast few years in RyanairLabs. We manage a reasonably big\nmonorepository of common components that are shared between\nall the frontend teams. The repository counts more than 30\ndifferent components that vary from\nsimple models shared across applications to more complex\nlibraries to manage the user basket for example.</p>\n<h2>Common components</h2>\n<p>The common components inside the monorepository, are included\nin various projects in the company, from public facing\nto internal ones. Any team can make changes to the\ncommon components and update them and improve them. There’s\na strict code review process to ensure high code quality\nstandards and all different developer takes turn into\nthe process.</p>\n<p>To better understand the components usage, interaction and\nvisual, we are using <a href=\"https://storybook.js.org/\">Storybook</a>\nand we enforce that each library should have at\nleast one story in the storybook to showcase its functionality.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/3a611b767b9f9320b7587ebbdb7f4020/a9004/storybook.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 70.27027027027026%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAAB00lEQVQoz31STW/TQBDdf8mBQ4mEKtQzIFS1SqmQ4NzfwQEk4NZDqqqVqpZDyKlJ5KTxt731R7xe73rX5tmmpk0pT5Y1u5437814SBgGju1EUeQ47vujH9t7n19/+vb83ddnb79s7X4f7B2/Ohy9PBht7Z8MhqeD4dn2h4udj5cvhpdvjibEdV1K6Xq9pjRkee7RdOWnozk7X7A1lx7NbD/jQuVSSVVpXSml1V1AptPp1dVPensb+H5RCF4IIWRdl7WWSJFS4lxVuiwl3nVd9Q+O5KZFFMVBEICM7LIsWc4Z45BQquxuCl5AqroHrTVZGMZsNkPPYQjbPE3TPM9xRAAqYnTEGENpIctN8nK5XCyWSAVZtoAO5xxMZBQtcIMqTaMbZMMwrq+nSZLQkCZxglTwG7stUKUr19/0wCyIZZqrm1WSpLCK0LLdNOPtmIR8Gp2XpufxeIyZQdkP/K5nCPZq6l+AZ3hspj2fzSGLqXieB/9xHGdZ9j9Z2VSEADFNc/Jrgj1BJcdxMFUw8aH1LTZwvwDPObEtOwxCMKGMvwJmcYeO0Afy4RQa25ZpAbCKVgU27M94m8+624oHP+gvsGQEfcI55gQbaEY/wmNy3S4nqv8GTUASSiE5NRMAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Ryanair storybook\"\n        title=\"Ryanair storybook\"\n        src=\"/dist/static/3a611b767b9f9320b7587ebbdb7f4020/102e5/storybook.png\"\n        srcset=\"/dist/static/3a611b767b9f9320b7587ebbdb7f4020/83276/storybook.png 148w,\n/dist/static/3a611b767b9f9320b7587ebbdb7f4020/ba02f/storybook.png 295w,\n/dist/static/3a611b767b9f9320b7587ebbdb7f4020/102e5/storybook.png 590w,\n/dist/static/3a611b767b9f9320b7587ebbdb7f4020/74549/storybook.png 885w,\n/dist/static/3a611b767b9f9320b7587ebbdb7f4020/7213b/storybook.png 1180w,\n/dist/static/3a611b767b9f9320b7587ebbdb7f4020/a9004/storybook.png 1234w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>The libraries are then published to an internal npm registry\nfor other projects to install and consume them\nthrough yarn or npm. We are\nfollowing semantic versioning inside the repository\nto recognise the type changes introduced at any\ngiven time and thanks to <em>conventional-changelog</em> the version is\ncalculated automatically by reading the commit message format.\nPlus generating changelogs, describing\nwhat changed in the library to share between the teams.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/96a935d1b6098c1fa5451d7c50ccfa72/47915/ryanair-changelog.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 64.1891891891892%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABn0lEQVQoz41S226jMBDl//+n2peV+pCqyrZVKlVJNkDK3cYGAiYJlxgb9uCqVdVmtXsww9ia8TnMjEUIsW3b8zxK6fRvjB8LsCglm83acRzXdTnneZ7DZjzLDd5OsgzbLC9qlmmaKsYVZao+aquqBCE0iiLwB0HQNM3pdIJt27YxaGd0eJqmR4IQWtS6ErrttHW/PPy8Tdfr7Xa7wQuqyWj6L9lClK7rMMbO57MQooKSd9R1bb6qrJTWevwG69xMPO99P4Dy/X4Pi/CDAS4qq4rxAUspdSX57p7c/Hj9tXy4WyyQXJblNdnjVVjTNCSJj2rHcYyqgjZJEjiMpXleeEEfxRelhuuygzB6fn5BqdCq1WpVFIWU8mIgh6Htxr7/K7ulVB/HgWPPNcPAgH/uc5ZhZgw/5TxFL5OE4Le/XGFF8fB7V9v2DkOGPnddNxhIg3d/FgLhn/s0J7sufXza+b5/PB5RXoSCAdZE67etlJOUo9ZwdNeN8D8KNpVlEYYhmgTNcKA2TVNmEEUBTYu9h6m8cE4ScgpjXRygH3eNfwCag9+CndCRFAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Ryanair common component changelog\"\n        title=\"Ryanair common component changelog\"\n        src=\"/dist/static/96a935d1b6098c1fa5451d7c50ccfa72/102e5/ryanair-changelog.png\"\n        srcset=\"/dist/static/96a935d1b6098c1fa5451d7c50ccfa72/83276/ryanair-changelog.png 148w,\n/dist/static/96a935d1b6098c1fa5451d7c50ccfa72/ba02f/ryanair-changelog.png 295w,\n/dist/static/96a935d1b6098c1fa5451d7c50ccfa72/102e5/ryanair-changelog.png 590w,\n/dist/static/96a935d1b6098c1fa5451d7c50ccfa72/47915/ryanair-changelog.png 603w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>All the actions in the repository, from managing cross dependencies, detecting\nchanges, running multiple commands to publishing are orchestrated by <em>lerna</em>.</p>\n<p>I will walk you through on how to setup a monorepository\nto manage multiple <em>Angular</em> libraries,\nsimilar to the common components that we\nare using at RyanairLabs. I will use the <em>angular/cli</em> to create the project\nand <em>lerna</em> to deal with the packages.</p>\n<h2>Setup</h2>\n<p>First, we need to create an <em>Angular</em> project through\nthe <em>angular/cli</em>:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">ng new angular-mono</code></pre></div>\n<p>Now we need to initialise a <em>lerna</em> project.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">npx lerna init --independent</code></pre></div>\n<p>The location of the packages and version of the repository are stored\ninto a <em>lerna.json</em> configuration file generated by <em>lerna</em> during the <em>init</em>\nprocess. There are two different modes which <em>lerna</em>\noperate: fixed or independent. A fixed <em>lerna</em> project only\nhas a single version across all of the packages.\nWhile with independent version, each package can be incremented\nindependently without affecting other one’s version. By default,\n<em>lerna</em> uses the fixed mode but we can change our setting by adding\nthe flag <em>—independent</em> during the <em>init</em> command.</p>\n<p>Since Angular generates libraries into the <em>projects</em> folder,\nwe need to change the standard configuration file in <em>lerna.json</em>\nto point to the correct package folder.</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"packages\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"projects/*\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"version\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"independent\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Next, we can start generating libraries in the project:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">ng generate library first\nng generate library second</code></pre></div>\n<h3>Commands</h3>\n<p>The <em>angular/cli</em> generates the two libraries inside the projects folder\nand creates all the necessary files to test and build the libraries.\nBuilding the libraries require to use the <em>ng build</em>\ncommand passing the name of the library that we want to build.\nNormally we would need to run multiple <em>ng build</em> commands\nto build all our libraries.\nUsing <em>lerna run</em> instead we can invoke a <em>script</em> on every package.\nFirst we need to add the <em>build</em> script on each <em>package.json</em></p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"first\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"version\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"0.0.1\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"build\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"ng build first\"</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"peerDependencies\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"@angular/common\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"^8.2.4\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"@angular/core\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"^8.2.4\"</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>After adding the scripts, when running the command <code class=\"language-text\">npx lerna run build</code>, both the libraries\nare build and the output of our build is in the <em>dist</em> folder.\nRunning commands across all the packages is very handy but it can become a bottleneck. Imagine once the monorepository start growing and,\nthe number of libraries increase, which increases also build and\ntest time. If we are changing only 1 package we shouldn’t have\nto rebuild all the packages at once. By adding <a href=\"https://github.com/lerna/lerna/blob/4fe2a668177ba74e50c3409c395b9dc8b6304f9f/core/filter-options/README.md\">filtering option</a>\nto <em>lerna run</em> command we can filter out packages and invoke\nscripts commands only on a subset of packages of our choice:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token comment\"># run build on all packages that have changed since 'master'</span>\nlerna run build --since master\n\n<span class=\"token comment\"># run build on all packages that have changed since the last tag</span>\nlerna run build --since <span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">git</span> describe --abbrev<span class=\"token operator\">=</span><span class=\"token number\">0</span> --tags <span class=\"token punctuation\">$(</span>git rev-list --tags --skip<span class=\"token operator\">=</span><span class=\"token number\">1</span> --max-count<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token variable\">)</span></span></code></pre></div>\n<h3>Versioning &#x26; publishing</h3>\n<p>Part of the publishing process is also bumping the current\npackage version. Determining the next version for a package\ncan be tiresome, especially when you have to remember all\nthe changes that were committed since the past release.\nUsing <a href=\"https://semver.org/\">semantic versioning</a> can help\ngiving a clear indication of the type of changes introduced.\nThe three number format, major, minor and patch gives you\na clear system to calculate the next package version.</p>\n<p>Manually applying semantic versioning in a repository\nit means remembering all the changes committed since the\nprevious release and then mentally determine the version.\nAn automated tool like <a href=\"https://github.com/commitizen/cz-cli\">Commitizen</a>\ncan rescue us from this task. <em>Commitizen</em>\nis a <em>cli</em> tool that generates commit messages following\na standard template, the commit messages then are used\nto calculate the next package version\nand to generate changelogs. To start using <em>commitizen</em> we need\nto initialise our repository</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">npx commitizen init cz-lerna-changelog --save-dev --save-exact\nnpm i semantic-release --save-dev</code></pre></div>\n<p><em>Commitizen</em> is now configured and ready to use.\nOnce we finish our changes\ninstead of using <em>git commit</em> we should always use:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">npx git-cz</code></pre></div>\n<p><em>Commitizen</em> prompt a set of questions, asking the type of change\nthat you’re committing, scope of the change and more. After we finish\nanswering the questions <em>commitizen</em> creates the commit message based\nupon our answers.\n<em>lerna</em> can now calculate the next package version\nbased upon our commit messages only. Editing the <em>lerna.json</em>\nwe can include <em>conventionalCommits</em> option to the <em>publish</em>\ncommand:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"packages\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"projects/*\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"version\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"independent\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"command\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"publish\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">\"conventionalCommits\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Sharing versioned libraries between applications implies compiling them into\nthe Angular package format and publish them to an npm registry. We have discussed\nalready how to build Angular libraries now we can focus on how to publish them.</p>\n<p>The command responsible for publishing packages in <em>lerna</em> is called <em>lerna publish</em>.\nThe command goes through a series of steps and takes care of the full publishing\nprocess. It first creates a new release increasing the version of the packages\nchanged since the last one. Then generates the version\ntags and lastly, it publish the packages to the npm registry.</p>\n<p>Publish operates in each package folder but <em>angular/cli</em> build output for libraries\nis in the root folder. We need to change the destination folder so that\n<em>lerna publish</em> can easily find the libraries to publish.\nThe destination folder for the libraries is specified\nthe <em>ng-package.json</em> file and we have to modify the <em>dest</em> folder:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"$schema\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"../../node_modules/ng-packagr/ng-package.schema.json\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"dest\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"dist\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"lib\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"entryFile\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"src/public-api.ts\"</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>To avoid committing build files we can add this line to <em>.gitignore</em>:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">**/dist/</code></pre></div>\n<p>The last step now is to include some scripts that hooks\ninto the lifecycle of <em>lerna</em> commands.</p>\n<p>Using a <em>prepublish</em> script in the root <em>package.json</em> we can trigger the build\nfor all the changed packages. This ensure that the packages built have\nthe correct version in their respective <em>package.json</em></p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  ...\n  <span class=\"token property\">\"prepublish\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"lerna run build --since HEAD~1 --concurrency=1\"</span>\n  ...\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>We are building here only the packages that changed since the last commit, that’s\nvery useful especially in CI/CD environments if you want to build and publish on each\ncommit incoming. You can modify the <em>—since</em> to your needs following lerna\n<a href=\"https://www.npmjs.com/package/@lerna/filter-options#--since-ref\">filter options</a>.</p>\n<p>Using the flag <strong>—concurrency=1</strong> ensure that the packages are correctly built\nwith ngc and respecting the topology.</p>\n<p>The script <em>postpublish</em> runs\nafter the <em>lerna publish</em> command and is useful\nto clean up the library folder after we have published the liraries.\nWe need to include the script in each library <em>package.json</em>:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"first\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"version\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"0.1.0\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"build\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"ng build first\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"postpublish\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"rm -rf dist/\"</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"peerDependencies\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"@angular/common\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"^8.2.4\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"@angular/core\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"^8.2.4\"</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Now that we completed all the steps we can build and publish\nour libraries:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">npx lerna run build\nnpx lerna publish  --contents dist</code></pre></div>\n<p>The libraries are all published and ready to be imported into\nour applications. Thanks to <em>conventional-changelog-angular</em>\neach package contains a changelog file with all the commits\nof a version divided by commit type.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/f726af033c046e12d834fdfeed47721e/e957c/changelog.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 49.32432432432432%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAAA80lEQVQoz5VR2W7CMBDk/7+uFNFQ2oASkcRHDt8hiW2YAGrVvhBGu37Y9eyuZlYxxNOpOGZ5RUhdN5dXsIoxMkqzPK8qwhj3N4zjOIw/mIYp3mOcprk1DPjzIPNONZ1knPO6LiuCKW3XCSGlVEipbWt8ZzxepS0KTds652ayDzGtQ8ZMsvv8TtP9/mv7kRhj0cPc52eXZSWE+NeIf3CJc+W3+CAjtTaEUGxT2jhrlZsa45+uvZ3tfZblh8Pxbf2eJDucvd5ste0XqR1CgEOQB/v73iGg013MRVYVRUkpwxSYcfav+IyEdfBGCtGqXliw40LyFXUuRnGkViWOAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Conventional changelog\"\n        title=\"Conventional changelog\"\n        src=\"/dist/static/f726af033c046e12d834fdfeed47721e/102e5/changelog.png\"\n        srcset=\"/dist/static/f726af033c046e12d834fdfeed47721e/83276/changelog.png 148w,\n/dist/static/f726af033c046e12d834fdfeed47721e/ba02f/changelog.png 295w,\n/dist/static/f726af033c046e12d834fdfeed47721e/102e5/changelog.png 590w,\n/dist/static/f726af033c046e12d834fdfeed47721e/74549/changelog.png 885w,\n/dist/static/f726af033c046e12d834fdfeed47721e/7213b/changelog.png 1180w,\n/dist/static/f726af033c046e12d834fdfeed47721e/e957c/changelog.png 1920w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>Angular provides a comprehensive framework for managing frontend applications\nmaking a lot of decisions for us. A codebase, on the other hand, should be\ntailored to our development needs. Using <em>lerna</em> with the correct tools we can\nreduce the pain when managing packages and versions.</p>\n<p>I’ve left a github repository preconfigured with all the\nsteps of the article\n<a href=\"https://github.com/izifortune/angular-mono\">https://github.com/izifortune/angular-mono</a> to explore.</p>\n<p><strong><em>Update 09/10/19:</em></strong></p>\n<h2>Linked libraries</h2>\n<p>Working with multiple libraries in a monorepository will eventually\nrequire to use and import a library inside another one. If you are following DRY\nprinciples you want to reuse a service or a component when and where its needed.\nUsing <em>npm</em> within a lerna monorepository has some limitation that we need to be\naware. When you have two linked libraries <em>ng-packagr</em> will not recognise the\nlinked library at build time. Another limitation will be once you start making changes to both of\nchanges to two different libraries at the same time, breaking contracts between\nthem.</p>\n<p>In general <em>npm</em> is missing the ability to link together the local libraries\nwhile working on a monorepository. <em>Yarn</em> on the other hand, has a feature\nthat covers exactly our needs: <strong>workspaces</strong>.</p>\n<p>Let’s switch to from <em>npm</em> to <em>yarn</em> in our repo making sure to delete the\n<em>package-lock.json</em> which <em>yarn</em> will replace with <em>yarn.lock</em>. We can also\nspecify to lerna what package manager to use and inform that we will use\nworkspaces inside <em>lerna.json</em></p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\">...\n  <span class=\"token property\">\"packages\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n  <span class=\"token string\">\"projects/*\"</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"npmClient\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"yarn\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"useWorkspaces\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n...</code></pre></div>\n<p>We need to locate the workspaces in the project by adding to the <em>package.json</em>:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\">  <span class=\"token property\">\"workspaces\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"projects/*\"</span><span class=\"token punctuation\">]</span></code></pre></div>\n<p>Our libraries will be located in a different folder structure that the one\ndefined by the <em>angular/cli</em> we have to change the mapping declartion <em>paths</em>\ninside our <em>tsconfig.json</em></p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\">...\n<span class=\"token property\">\"paths\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token property\">\"first\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n     <span class=\"token string\">\"projects/first/dist\"</span>\n   <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n   <span class=\"token property\">\"first/*\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n     <span class=\"token string\">\"projects/first/dist/*\"</span>\n   <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n   <span class=\"token property\">\"second\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n     <span class=\"token string\">\"projects/second/dist\"</span>\n   <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n   <span class=\"token property\">\"second/*\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n     <span class=\"token string\">\"projects/second/dist/*\"</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>As a general rule of thumb we always specify imported libraries as\n<em>peerDependencies</em>. This ensure that we are not including the same\nlibrary multiple times on an application, leaving the latter to\nadd all the libraries as <em>dependencies</em>.</p>\n<p>Now that the libraries are linked we need to ensure that the\ncommands order is respected. If we are building library <em>second</em> which\ndepends on library <em>first</em> in the monorepository we need to have\n<em>first</em> built so that we can consume it. The command <em>lerna run</em> respect the topological libraries sorting\nbut it won’t work with <em>peerDependencies</em>. We have to switch then\nto <em>devDependencies</em> so that <em>lerna</em> can understand that correct\norder of execution.</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\">  <span class=\"token property\">\"devDependencies\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n     <span class=\"token property\">\"first\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"*\"</span>\n   <span class=\"token punctuation\">}</span></code></pre></div>\n<p>I added a third library on the example repository <em>linked</em> to demonstrate how\nthe linking will work <a href=\"https://github.com/izifortune/angular-mono\">here</a></p>\n<p>** Update 29/01/21 **</p>\n<p>When working linking locally the packages in the monorepo in another Angular application\nremember to add the flag <em>preserveSymlinks: true</em> in both <em>tsconfig.json</em>\nand <em>angular.json</em> as follows:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token comment\">// tsconfig.json</span>\n<span class=\"token property\">\"compilerOptions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"preserveSymlinks\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token comment\">// angular.json</span>\n  <span class=\"token property\">\"projects\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    ...\n      <span class=\"token property\">\"architect\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"build\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          ...\n          <span class=\"token property\">\"options\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token property\">\"preserveSymlinks\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n            ...</code></pre></div>","frontmatter":{"title":"Share Angular libraries with lerna","date":"September 15, 2019","description":"Managing Angular libraries effortlessly using lerna, semantic versioning and standard changelog. An overview of Angular tooling and monorepository setup to start sharing packages in your team.","socialImage":{"childImageSharp":{"fixed":{"src":"/dist/static/6998b4300d19496cc0826a1ce4a148bd/bdf9d/angular-lerna.png"}}},"image":null}}},"pageContext":{"slug":"/share-angular-libraries-with-lerna/","previous":{"fields":{"slug":"/serverless-website-sam-aws/"},"frontmatter":{"title":"Build a serverless website with SAM on AWS"}},"next":{"fields":{"slug":"/serverless-website-sam-aws-part-2/"},"frontmatter":{"title":"Build a serverless website with SAM on AWS - part 2"}}}}}