{"id":3659,"date":"2021-04-21T16:00:16","date_gmt":"2021-04-21T16:00:16","guid":{"rendered":"https:\/\/ionicframework.com\/blog\/?p=3659"},"modified":"2021-04-21T16:00:16","modified_gmt":"2021-04-21T16:00:16","slug":"ionic-and-lerna-and-rollup-oh-my","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my","title":{"rendered":"Ionic and Lerna and Rollup, oh my!"},"content":{"rendered":"<blockquote><p>\n  This is part one of a <a href=\"https:\/\/ionicframework.com\/blog\/understanding-monorepos\/\">new series<\/a> on monorepos. By the end of the series, you&#8217;ll have the tools you need to adopt monorepo setups in your organization.\n<\/p><\/blockquote>\n<p>Building and maintaining multiple development projects comes with a unique set of problems teams need to solve. How do you share common bits of code between projects? How do you sync dependencies across projects? How do you optimize collaboration between projects?<\/p>\n<p>With solutions to those problems, it&#8217;s not shocking to see that monorepos are rapidly growing in popularity. In fact, we use monorepos here at Ionic! Several monorepo tools are available to development teams: Nx, Yarn workspaces, npm workspaces, Lerna, Turborepo&#8230;and I&#8217;m sure more that I&#8217;m not even aware of.<\/p>\n<p>In this blog post we&#8217;ll be building out a monorepo using <a href=\"https:\/\/lerna.js.org\/\">Lerna<\/a>. I personally like how lightweight it is, and it works well with Ionic Framework React projects and Ionic Appflow.<\/p>\n<p>Our monorepo will consist of three packages (monorepo speak for subprojects); two Ionic Framework React applications, and a shared React library that will supply a React context each application will use.<\/p>\n<p><!--more--><\/p>\n<h2>Initializing a Lerna Repository<\/h2>\n<p>Before we start generating Ionic Framework applications or building the shared code library, we need to initialize a Lerna repository. This space will hold all of our packages and is committed to source control as one repository.<\/p>\n<pre><code class=\"language-bash\">$ npm install -g lerna\n$ git init my-organization &amp;&amp; cd my-organization\n$ lerna init\n<\/code><\/pre>\n<p>Crack open the generated <code>lerna.json<\/code> file. By default, Lerna declares that all of your monorepos packages will be housed in the <code>packages\/<\/code> folder. We can modify what folder(s) house our packages &#8212; let&#8217;s create a folder to house our Ionic Framework React applications, and another to house our shared React libraries.<\/p>\n<pre><code class=\"language-bash\">$ rmdir packages\n$ mkdir apps\n$ mkdir shared\n<\/code><\/pre>\n<p>Update <code>lerna.json<\/code> so we can relay our monorepo&#8217;s structure to Lerna:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;packages&quot;: [&quot;apps\/*&quot;, &quot;shared\/*&quot;],\n  &quot;version&quot;: &quot;0.0.0&quot;\n}\n<\/code><\/pre>\n<p>Before we add any packages to our monorepo, let&#8217;s make sure to use the Ionic CLI to establish a multi-app setup:<\/p>\n<pre><code class=\"language-bash\">$ ionic init --multi-app\n<\/code><\/pre>\n<h2>Generating multiple Ionic Framework React applications<\/h2>\n<p>The initial plumbing of our monorepo is set up, now it&#8217;s time to create our Ionic Framework React applications. To ensure they are created in our <code>apps\/<\/code> folder, we&#8217;re going to <code>cd<\/code> into it then generate our apps.<\/p>\n<pre><code class=\"language-bash\">$ cd apps\/\n$ ionic start customers blank --type=react\n$ ionic start employees blank --type=react --no-deps\n<\/code><\/pre>\n<p>Remember that one challenge monorepos solve is the ability to manage dependencies across projects? We can use a technique known as &#8220;dependency hoisting&#8221; to have both packages point to the same folders containing the dependencies.<\/p>\n<pre><code class=\"language-bash\">$ cd ..\/\n$ lerna bootstrap --hoist\n<\/code><\/pre>\n<p>This process moved dependencies shared across packages into a <code>node_modules<\/code> folder at the root of the repository. Lerna creates symlinks for the packages to reference when a shared dependency is required.<\/p>\n<p>Lerna doesn&#8217;t initialize a <code>.gitignore<\/code> at the root of the repository. It&#8217;s not a good idea to commit all the hoisted dependencies, so let&#8217;s create one to exclude our dependencies from being committed to source control.<\/p>\n<pre><code class=\"language-bash\">$ echo &quot;node_modules&quot; &gt; .gitignore\n<\/code><\/pre>\n<blockquote><p>\n  <strong>Note:<\/strong> To run a package&#8217;s npm commands using the Lerna CLI, the command is <code>lerna run &lt;script&gt; --scope=&lt;package&gt;<\/code>. As an example, to serve the Employees app run <code>lerna run start --scope=employees<\/code>.\n<\/p><\/blockquote>\n<h2>Creating a shared code library<\/h2>\n<p>Tools like Storybook and Bit are out there that provide CLIs that generate React libraries intended to be shared. They might be great tools for you to add to your development toolbox, but I find them to have too much overhead. For the purpose of this blog post, we&#8217;ll use <a href=\"https:\/\/rollupjs.org\/guide\/en\/\">Rollup<\/a> to build our own.<\/p>\n<h3>Structuring the package<\/h3>\n<p>Lerna allows us to create generic JavaScript projects through it&#8217;s CLI. Let&#8217;s add a package and structure it such that it can be used as a reusable React library.<\/p>\n<pre><code class=\"language-bash\">$ lerna create @myorg\/core shared --description=&quot;Core shared library&quot; --es-module --access=restricted --yes\n<\/code><\/pre>\n<p>Let&#8217;s add Rollup to the package and make some modifications to the package structure.<\/p>\n<pre><code class=\"language-bash\">$ cd shared\/core\n$ npm install --save-dev rollup rollup-plugin-typescript2\n$ echo &quot;dist&quot; &gt;&gt; .gitignore\n$ rm -rf __tests__ README.md\n$ mv src\/core.js src\/index.ts\n$ touch rollup.config.js tsconfig.json\n$ cd ..\/..\/\n<\/code><\/pre>\n<p>Next, populate <code>shared\/code\/rollup.config.js<\/code> with the following code:<\/p>\n<pre><code class=\"language-JavaScript\">import typescript from &#039;rollup-plugin-typescript2&#039;;\nimport pkg from &#039;.\/package.json&#039;;\n\nconst input = &quot;src\/index.ts&quot;;\n\nconst external = [\n  ...Object.keys(pkg.dependencies || {}),\n  ...Object.keys(pkg.peerDependencies || {}),\n];\n\nconst plugins = [ typescript({ typescript: require(&quot;typescript&quot;) }) ];\n\nexport default [\n  {\n    input,\n    output: { file: pkg.module, format: &quot;esm&quot;, sourcemap: true },\n    plugins,\n    external\n  },\n  {\n    input,\n    output: { file: pkg.main, format: &quot;cjs&quot;, sourcemap: true },\n    plugins,\n    external\n  },\n];\n<\/code><\/pre>\n<p>Then populate <code>shared\/core\/tsconfig.json<\/code> with the following code:<\/p>\n<pre><code class=\"language-JSON\">{\n  &quot;compilerOptions&quot;: {\n    &quot;allowSyntheticDefaultImports&quot;: true,\n    &quot;allowUnreachableCode&quot;: false,\n    &quot;declaration&quot;: true,\n    &quot;esModuleInterop&quot;: true,\n    &quot;forceConsistentCasingInFileNames&quot;: true,\n    &quot;importHelpers&quot;: true,\n    &quot;lib&quot;: [&quot;es2015&quot;, &quot;dom&quot;],\n    &quot;module&quot;: &quot;es2015&quot;,\n    &quot;moduleResolution&quot;: &quot;node&quot;,\n    &quot;noEmitHelpers&quot;: true,\n    &quot;noFallthroughCasesInSwitch&quot;: true,\n    &quot;noUnusedLocals&quot;: false,\n    &quot;noUnusedParameters&quot;: true,\n    &quot;skipLibCheck&quot;: true,\n    &quot;strict&quot;: true,\n    &quot;target&quot;: &quot;es2017&quot;,\n    &quot;sourceMap&quot;: true,\n    &quot;inlineSources&quot;: true,\n    &quot;jsx&quot;: &quot;react&quot;\n  },\n  &quot;include&quot;: [&quot;src\/**\/*&quot;],\n  &quot;exclude&quot;: [&quot;src\/**\/**.test.*&quot;]\n}\n<\/code><\/pre>\n<p>Finally, we need to make some modifications to <code>shared\/core\/package.json<\/code>.<\/p>\n<ol>\n<li>Add a new section named <code>peerDependencies<\/code>. Copy over the <code>dependencies<\/code> array from one of the apps and paste it in this section.<\/li>\n<li>Add the array of <code>peerDependencies<\/code> to the array of <code>devDependencies<\/code>.<\/li>\n<li>Remove the <code>files<\/code> section.<\/li>\n<li>In the <code>directories<\/code> section, change the <code>lib<\/code> value to <code>src<\/code> and remove the <code>test<\/code> entry.<\/li>\n<li>Update the <code>main<\/code> property to <code>dist\/index.js<\/code> and <code>module<\/code> to <code>dist\/index.esm.js<\/code>.<\/li>\n<li>Replace the contents of the <code>scripts<\/code> section with the following scripts:<\/li>\n<\/ol>\n<pre><code class=\"language-JSON\">&quot;build&quot;: &quot;npx rollup -c&quot;,\n&quot;watch&quot;: &quot;npx rollup -c -w&quot;\n<\/code><\/pre>\n<blockquote><p>\n  <strong>Note:<\/strong> Not all <code>peerDependencies<\/code> or <code>devDependencies<\/code> are needed. You can prune any that aren&#8217;t being used in the package&#8217;s source code.\n<\/p><\/blockquote>\n<p>Now we can add our shared package to our applications.<\/p>\n<pre><code class=\"language-bash\">$ lerna run build --scope=@myorg\/core\n$ lerna bootstrap --hoist\n$ lerna add @myorg\/core\n<\/code><\/pre>\n<p>When adding a shared package to application packages in a monorepo, use the scoped npm package naming approach (such as <code>@myorg\/core<\/code>). Not only does it remove the need to do any kind of path-mapping, it&#8217;s also super cool!<\/p>\n<h3>Building a theme context<\/h3>\n<p><em>Technically<\/em> we can demo the shared library in our application packages by importing the <code>core()<\/code> function defined in <code>@myorg\/core<\/code> but that&#8217;s pretty lame. Instead, let&#8217;s build a React Context that provides the plumbing needed to allow users to toggle light\/dark mode on the applications.<\/p>\n<pre><code class=\"language-bash\">$ echo &quot;export * from &#039;.\/theme\/ThemeContext&#039;;&quot; &gt; shared\/core\/src\/index.ts\n$ mkdir shared\/core\/src\/theme\n$ touch shared\/core\/src\/theme\/ThemeContext.tsx\n<\/code><\/pre>\n<p>Populate <code>shared\/core\/src\/theme\/ThemeContext.tsx<\/code> with the following code:<\/p>\n<pre><code class=\"language-ts\">import React, { createContext, useContext, useEffect, useState } from &quot;react&quot;;\n\nconst initialContext = {\n  isDarkMode: false,\n  toggleDarkMode: (_: boolean) =&gt; {},\n};\n\nconst ThemeContext = createContext(initialContext);\nexport const useTheme = () =&gt; useContext(ThemeContext);\n\nexport const ThemeProvider: React.FC = ({ children }) =&gt; {\n  const [isDarkMode, setDarkMode] = useState&lt;boolean&gt;(false);\n\n  useEffect(() =&gt; {\n    const prefersDark = window.matchMedia(&quot;(prefers-color-scheme: dark)&quot;);\n    prefersDark.addEventListener(&quot;change&quot;, (e) =&gt; setDarkMode(e.matches));\n    toggleDarkMode(prefersDark.matches);\n  }, []);\n\n  const toggleDarkMode = (useDarkMode: boolean) =&gt; {\n    document.body.classList.toggle(&quot;dark&quot;, useDarkMode);\n    setDarkMode(useDarkMode);\n  };\n\n  return (\n    &lt;ThemeContext.Provider value={{ isDarkMode, toggleDarkMode }}&gt;\n      {children}\n    &lt;\/ThemeContext.Provider&gt;\n  );\n};\n<\/code><\/pre>\n<p>Rebuild the <code>@myorg\/core<\/code> package so the applications can have access to <code>&lt;ThemeProvider \/&gt;<\/code> and <code>useTheme()<\/code>.<\/p>\n<pre><code class=\"language-bash\">$ lerna run build --scope=@myorg\/core\n<\/code><\/pre>\n<blockquote><p>\n  <strong>Note:<\/strong> While developing shared libraries, it&#8217;s beneficial to run <code>lerna run watch --scope=&lt;package&gt;<\/code> to rebuild the library in real-time.\n<\/p><\/blockquote>\n<h3>Putting it all together<\/h3>\n<p>Let&#8217;s update our application packages to see our theme context in action. The following actions should be performed in both the <code>customers<\/code> and <code>employees<\/code> packages:<\/p>\n<ol>\n<li>Remove the <code>@media (prefers-color-scheme: dark)<\/code> media query in <code>variables.css<\/code>.<\/li>\n<li>In <code>variables.css<\/code>, append <code>.dark<\/code> to any <code>body<\/code> selector; <code>body<\/code> becomes <code>body.dark<\/code>, <code>.ios body<\/code> becomes <code>.ios body.dark<\/code>, and <code>.md body<\/code> becomes <code>.md body.dark<\/code>.<\/li>\n<li>In <code>App.tsx<\/code> add the following import: <code>import { ThemeProvider } from &#039;@myorg\/core&#039;;<\/code><\/li>\n<li>In <code>App.tsx<\/code>, wrap the <code>&lt;IonApp&gt;<\/code> component and it&#8217;s children with <code>&lt;ThemeProvider&gt;<\/code>, so <code>&lt;ThemeProvider&gt;<\/code> is the outermost component of the <code>App<\/code> template.<\/li>\n<\/ol>\n<p>Serve one of the applications (<code>lerna run start --scope=&lt;package&gt;<\/code>), change your dark mode preference, and refresh the browser to test it out!<\/p>\n<h2>Integrating with Appflow<\/h2>\n<p><a href=\"https:\/\/ionic.io\/blog\/appflow\">Appflow<\/a> is Ionic&#8217;s mobile CI\/CD platform that makes it easy to build, publish, and update your apps over time. Oh, and it also supports monorepos!<\/p>\n<p>To support a monorepo structure, Appflow needs a singular <code>appflow.config.json<\/code> file at the root of the monorepo repository.<\/p>\n<pre><code class=\"language-bash\">$ touch appflow.config.json\n<\/code><\/pre>\n<p>Populate the file with the following:<\/p>\n<pre><code class=\"language-JSON\">{\n  &quot;apps&quot;: [\n    {\n      &quot;appId&quot;: &quot;XXXXXXXX&quot;,\n      &quot;root&quot;: &quot;apps\/customers&quot;,\n      &quot;dependencyInstallCommand&quot;: &quot;cd ..\/..\/ &amp;&amp; npx lerna bootstrap &amp;&amp; npx lerna run build --scope=@myorg\/core&quot;\n\n    },\n    {\n      &quot;appId&quot;: &quot;XXXXXXX&quot;,\n      &quot;root&quot;: &quot;apps\/employees&quot;,\n      &quot;dependencyInstallCommand&quot;: &quot;cd ..\/..\/ &amp;&amp; npx lerna bootstrap &amp;&amp; npx lerna run build --scope=@myorg\/core&quot;\n\n    }\n  ]\n}\n<\/code><\/pre>\n<p>Replace the <code>appId<\/code> values with the ones provided to you from Appflow, of course. The important bit here is that as part of the dependency install command we build the shared packages after bootstrapping Lerna. In a real-world scenario you&#8217;d probably want to create a custom shell script to encapsulate the commands used above &#8212; especially as the amount of shared libraries in your monorepo scale.<\/p>\n<h2>Wrapping up<\/h2>\n<p>We now have a monorepo built with Lerna that contains two Ionic Framework React applications and a shared React library, hooked up to Appflow for continuous integration and continuous deployment!<\/p>\n<p>Our monorepo solves several problems encountered when maintaining multiple projects: it shares common code between projects, manages dependencies across projects, and eases collaboration effort between projects.<\/p>\n<p>With a solid foundation in place you can continue scaling your monorepo to fit you and your development team&#8217;s needs.<\/p>\n<p><a href=\"https:\/\/github.com\/eric-horodyski\/lerna-cap-react-appflow\">Sample Project<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part one of a new series on monorepos. By the end of the series, you&#8217;ll have the tools you need to adopt monorepo setups in your organization. Building and maintaining multiple development projects comes with a unique set of problems teams need to solve. How do you share common bits of code between [&hellip;]<\/p>\n","protected":false},"author":76,"featured_media":3660,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"1","publish_post_category":"28","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"503551","discourse_permalink":"https:\/\/forum.ionicframework.com\/t\/ionic-and-lerna-and-rollup-oh-my\/208490","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[121,124],"tags":[128,208,206,136],"class_list":["post-3659","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","category-tutorials","tag-appflow","tag-lerna","tag-monorepos","tag-react"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Ionic and Lerna and Rollup, oh my! - Ionic Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ionic and Lerna and Rollup, oh my!\" \/>\n<meta property=\"og:description\" content=\"This is part one of a new series on monorepos. By the end of the series, you&#8217;ll have the tools you need to adopt monorepo setups in your organization. Building and maintaining multiple development projects comes with a unique set of problems teams need to solve. How do you share common bits of code between [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-04-21T16:00:16+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1600\" \/>\n\t<meta property=\"og:image:height\" content=\"880\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Eric Horodyski\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Eric Horodyski\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my\"},\"author\":{\"name\":\"Eric Horodyski\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/bec836be266e0046c56c120cdb9806d6\"},\"headline\":\"Ionic and Lerna and Rollup, oh my!\",\"datePublished\":\"2021-04-21T16:00:16+00:00\",\"dateModified\":\"2021-04-21T16:00:16+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my\"},\"wordCount\":1110,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png\",\"keywords\":[\"Appflow\",\"Lerna\",\"Monorepos\",\"react\"],\"articleSection\":[\"Engineering\",\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my\",\"url\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my\",\"name\":\"Ionic and Lerna and Rollup, oh my! - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png\",\"datePublished\":\"2021-04-21T16:00:16+00:00\",\"dateModified\":\"2021-04-21T16:00:16+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png\",\"width\":1600,\"height\":880},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ionic and Lerna and Rollup, oh my!\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/bec836be266e0046c56c120cdb9806d6\",\"name\":\"Eric Horodyski\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/07\/T024WJZKU-U010BLA2B9S-89f661f3bfad-512-150x150.jpeg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/07\/T024WJZKU-U010BLA2B9S-89f661f3bfad-512-150x150.jpeg\",\"caption\":\"Eric Horodyski\"},\"url\":\"https:\/\/ionic.io\/blog\/author\/horodyski\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Ionic and Lerna and Rollup, oh my! - Ionic Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my","og_locale":"en_US","og_type":"article","og_title":"Ionic and Lerna and Rollup, oh my!","og_description":"This is part one of a new series on monorepos. By the end of the series, you&#8217;ll have the tools you need to adopt monorepo setups in your organization. Building and maintaining multiple development projects comes with a unique set of problems teams need to solve. How do you share common bits of code between [&hellip;]","og_url":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my","og_site_name":"Ionic Blog","article_published_time":"2021-04-21T16:00:16+00:00","og_image":[{"width":1600,"height":880,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png","type":"image\/png"}],"author":"Eric Horodyski","twitter_card":"summary_large_image","twitter_creator":"@ionicframework","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Eric Horodyski","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my"},"author":{"name":"Eric Horodyski","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/bec836be266e0046c56c120cdb9806d6"},"headline":"Ionic and Lerna and Rollup, oh my!","datePublished":"2021-04-21T16:00:16+00:00","dateModified":"2021-04-21T16:00:16+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my"},"wordCount":1110,"commentCount":0,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png","keywords":["Appflow","Lerna","Monorepos","react"],"articleSection":["Engineering","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my","url":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my","name":"Ionic and Lerna and Rollup, oh my! - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png","datePublished":"2021-04-21T16:00:16+00:00","dateModified":"2021-04-21T16:00:16+00:00","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png","width":1600,"height":880},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/ionic-and-lerna-and-rollup-oh-my#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Ionic and Lerna and Rollup, oh my!"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/bec836be266e0046c56c120cdb9806d6","name":"Eric Horodyski","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/07\/T024WJZKU-U010BLA2B9S-89f661f3bfad-512-150x150.jpeg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/07\/T024WJZKU-U010BLA2B9S-89f661f3bfad-512-150x150.jpeg","caption":"Eric Horodyski"},"url":"https:\/\/ionic.io\/blog\/author\/horodyski"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2021\/04\/lerna-feature-image.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/3659","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/76"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=3659"}],"version-history":[{"count":0,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/3659\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/3660"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=3659"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=3659"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=3659"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}