{"id":2376,"date":"2018-04-03T15:07:50","date_gmt":"2018-04-03T13:07:50","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=2376"},"modified":"2018-04-03T15:07:50","modified_gmt":"2018-04-03T13:07:50","slug":"custom-schematics-part-iv","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/","title":{"rendered":"Custom Schematics &#8211; Part IV"},"content":{"rendered":"<div class=\"article\">\n<hr>\n<p><b>Table of Contents<\/b><\/p>\n<p>This blog post is part of an article series.<\/p>\n<ul>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/10\/29\/generating-custom-code-with-the-angular-cli-and-schematics.aspx\" rel=\"nofollow\">Part I: Generating Custom Code With The Angular CLI And Schematics<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/01\/generating-angular-code-with-schematics-part-ii-modifying-ngmodules.aspx\" rel=\"nofollow\">Part II: Automatically Updating Angular Modules With Schematics And The CLI<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/01\/02\/angular-cli-and-schematics-part-iii-typescripts-compiler-api.aspx\" rel=\"nofollow\">Part III: Extending Existing Code With The TypeScript Compiler API<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/03\/20\/custom-schematics-part-iv-frictionless-library-setup-with-the-angular-cli-and-schematics.aspx\" target=\"_blank\" rel=\"noopener\">Part IV:&nbsp;Frictionless Library Setup with the Angular CLI and Schematics<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/04\/17\/seamlessly-updating-your-angular-libraries-with-ng-update.aspx\">Part V: Seamlessly Updating your Angular Libraries with ng update<\/a><\/li>\n<\/ul>\n<hr>\n<blockquote><p>\nThanks a lot to <a href=\"https:\/\/twitter.com\/hanslatwork\">Hans Larsen<\/a> from the Angular CLI team for reviewing this article.\n<\/p><\/blockquote>\n<p>It's always the same: After npm installing a new library, we have to follow a readme step by step to include it into our application. Usually this involves creating configuration objects, referencing css files, and importing Angular Modules. As such tasks aren't fun at all it would be nice to automate this.<\/p>\n<p>This is exactly what the Angular CLI supports beginning with Version 6 (Beta 5). It gives us a new <code>ng add<\/code> command that fetches an npm package and sets it up with a schematic -- a code generator written with the CLI's scaffolding tool <a href=\"https:\/\/blog.angular.io\/schematics-an-introduction-dc1dfbc2a2b2\">Schematics<\/a>. To support this, the package just needs to name this schematic <code>ng-add<\/code>.<\/p>\n<p>In this article, I show you how to create such a package. For this, I'll use <a href=\"https:\/\/github.com\/dherges\/ng-packagr\">ng-packagr<\/a> and a custom schematic. You can find the <a href=\"https:\/\/github.com\/manfredsteyer\/schematics-ng-add\">source code<\/a> in <a href=\"https:\/\/github.com\/manfredsteyer\/schematics-ng-add\">my GitHub account<\/a>.<\/p>\n<p>If you haven't got an overview to Schematics so far, you should lookup the well written <a href=\"https:\/\/blog.angular.io\/schematics-an-introduction-dc1dfbc2a2b2\">introduction in the Angular Blog<\/a> before proceeding here.<\/p>\n<h2 id=\"goal\">Goal<\/h2>\n<p>To demonstrate how to leverage <code>ng add<\/code>, I'm using an example with a very simple logger library here. It is complex enough to explain how everything works and not indented for production. After installing it, one has to import it into the root module using <code>forRoot<\/code>:<\/p>\n<pre class=\"hljs\"><code><div>[...]\n<span class=\"hljs-keyword\">import<\/span> { LoggerModule } from <span class=\"hljs-string\">'@my\/logger-lib'<\/span>;\n\n@NgModule({\n  imports: [\n    [...],\n    LoggerModule.forRoot({ enableDebug: <span class=\"hljs-literal\">true<\/span> })\n  ],\n  [...]\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppModule { }\n<\/div><\/code><\/pre>\n<p>As you see in the previous listing, <code>forRoot<\/code> takes a configuration object. After this, the application can get hold of the <code>LoggerService<\/code> and use it:<\/p>\n<pre class=\"hljs\"><code><div>[...]\n<span class=\"hljs-keyword\">import<\/span> { LoggerService } from <span class=\"hljs-string\">'@my\/logger-lib'<\/span>;\n\n@Component({\n  selector: <span class=\"hljs-string\">'app-root'<\/span>,\n  templateUrl: <span class=\"hljs-string\">'.\/app.component.html'<\/span>\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppComponent {\n\n  <span class=\"hljs-keyword\">constructor<\/span>(private logger: LoggerService) {\n    logger.debug(<span class=\"hljs-string\">'Hello World!'<\/span>);\n    logger.log(<span class=\"hljs-string\">'Application started'<\/span>);\n  }\n}\n<\/div><\/code><\/pre>\n<p>To prevent the need for importing the module manually and for remembering the structure of the configuration object, the following sections present a schematic for this.<\/p>\n<blockquote><p>\nSchematics is currently an Angular Labs project. Its public API is experimental and can change in future.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/i.imgur.com\/y8LIiVg.png\" alt=\"Angular Labs\">\n<\/p><\/blockquote>\n<h2 id=\"getting-started\">Getting Started<\/h2>\n<p>To get started, you need to install version 6 of the Angular CLI. Make sure to fetch Beta 5 or higher:<\/p>\n<pre><code>npm i -g @angular\/cli@~6.0.0-beta\n<\/code><\/pre>\n<p>You also need the Schematics CLI:<\/p>\n<pre><code>npm install -g @angular-devkit\/schematics-cli\n<\/code><\/pre>\n<p>The above mentioned logger library can be found in the <code>start<\/code> branch of my sample:<\/p>\n<pre><code>git clone https:\/\/github.com\/manfredsteyer\/schematics-ng-add\ncd schematics-ng-add\ngit checkout start\n<\/code><\/pre>\n<p>After checking out the <code>start<\/code> branch, npm install its dependencies:<\/p>\n<pre><code>npm install\n<\/code><\/pre>\n<p>If you want to learn more about setting up a library project from scratch, I recommend the resources outlined in the <a href=\"https:\/\/github.com\/dherges\/ng-packagr\">readme of ng-packagr<\/a>.<\/p>\n<h2 id=\"adding-an-ng-add-schematic\">Adding an ng-add Schematic<\/h2>\n<p>As we have everything in place now, let's add a schematics project to the library. For this, we just need to run the <code>blank<\/code> Schematics in the project's root:<\/p>\n<pre><code>schematics blank --name=schematics\n<\/code><\/pre>\n<p>This generates the following folder structure:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/i.imgur.com\/wrvMcTm.png\" width=\"200\" alt=\"Generated Schematic\"><\/p>\n<p>The folder <code>src\/schematics<\/code> contains an empty schematic. As <code>ng add<\/code> looks for an <code>ng-add<\/code> schematic, let's rename it:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/i.imgur.com\/d1XsLh0.png\" width=\"200\" alt=\"Renamed Schematic\"><\/p>\n<p>In the <code>index.ts<\/code> file in the <code>ng-add<\/code> folder we find a factory function. It returns a <code>Rule<\/code> for code generation. I've adjusted its name to <code>ngAdd<\/code> and added a line for generating a <code>hello.txt<\/code>.<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> { Rule, SchematicContext, Tree } from <span class=\"hljs-string\">'@angular-devkit\/schematics'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">ngAdd<\/span>(<span class=\"hljs-params\"><\/span>): <span class=\"hljs-title\">Rule<\/span> <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (tree: Tree, _context: SchematicContext) =&gt; {\n    tree.create(<span class=\"hljs-string\">'hello.txt'<\/span>, <span class=\"hljs-string\">'Hello World!'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> tree;\n  };\n}\n<\/div><\/code><\/pre>\n<p>The generation of the <code>hello.txt<\/code> file represents the tasks for setting up the library. We will replace it later with a respective implementation.<\/p>\n<p>As our schematic will be looked up in the <code>collection.json<\/code> later, we have also to adjust it:<\/p>\n<pre class=\"hljs\"><code><div>{\n  <span class=\"hljs-attr\">\"$schema\"<\/span>: <span class=\"hljs-string\">\"..\/node_modules\/@angular-devkit\/schematics\/collection-schema.json\"<\/span>,\n  <span class=\"hljs-attr\">\"schematics\"<\/span>: {\n    <span class=\"hljs-attr\">\"ng-add\"<\/span>: {\n      <span class=\"hljs-attr\">\"description\"<\/span>: <span class=\"hljs-string\">\"Initializes Library\"<\/span>,\n      <span class=\"hljs-attr\">\"factory\"<\/span>: <span class=\"hljs-string\">\".\/ng-add\/index#ngAdd\"<\/span>\n    }\n  }\n}\n<\/div><\/code><\/pre>\n<p>Now, the name <code>ng-add<\/code> points to our rule -- the <code>ngAdd<\/code> function in the <code>ng-add\/index.ts<\/code> file.<\/p>\n<h2 id=\"adjusting-the-build-script\">Adjusting the Build Script<\/h2>\n<p>In the current project, <code>ng-packagr<\/code> is configured to put the library build out of our sources in the folder <code>dist\/lib<\/code>. The respective settings can be found within the <code>ngPackage<\/code> node in the <code>package.json<\/code>. When I'm mentioning  <code>package.json<\/code> here, I'm referring to the project root's <code>package.json<\/code> and not to the generated one in the <code>schematics<\/code> folder.<\/p>\n<p>To make use of our schematic, we have to make sure it is compiled and copied over to this folder. For the latter task, I'm using the <code>cpr<\/code> npm package we need to install in the project's root:<\/p>\n<pre><code>npm install cpr --save-dev\n<\/code><\/pre>\n<p>In order to automate the mentioned tasks, add the following scripts to the <code>package.json<\/code>:<\/p>\n<pre class=\"hljs\"><code><div>[...]\n<span class=\"hljs-string\">\"scripts\"<\/span>: {\n  [...],\n  <span class=\"hljs-attr\">\"build:schematics\"<\/span>: <span class=\"hljs-string\">\"tsc -p schematics\/tsconfig.json\"<\/span>,\n  <span class=\"hljs-attr\">\"copy:schematics\"<\/span>: <span class=\"hljs-string\">\"cpr schematics\/src dist\/lib\/schematics --deleteFirst\"<\/span>,\n  [...]\n},\n[...]\n<\/div><\/code><\/pre>\n<p>Also, extend the <code>build:lib<\/code> script so that the newly introduced scripts are called:<\/p>\n<pre class=\"hljs\"><code><div>[...]\n<span class=\"hljs-string\">\"scripts\"<\/span>: {\n  [...]\n  <span class=\"hljs-attr\">\"build:lib\"<\/span>: <span class=\"hljs-string\">\"ng-packagr -p package.json &amp;&amp; npm run build:schematics &amp;&amp; npm run copy:schematics\"<\/span>,\n  [...]\n},\n[...]\n<\/div><\/code><\/pre>\n<p>When the CLI tries to find our <code>ng-add<\/code> schematic, it looks up the <code>schematics<\/code> field in the <code>package.json<\/code>. By definition it points to the <code>collection.json<\/code> which in turn points to the provided schematics. Hence, let's add this field to our package.json too:<\/p>\n<pre class=\"hljs\"><code><div>{\n  [...],\n  <span class=\"hljs-attr\">\"schematics\"<\/span>: <span class=\"hljs-string\">\".\/schematics\/collection.json\"<\/span>,\n  [...]\n}\n<\/div><\/code><\/pre>\n<p>Please note that the mentioned path is relative to the folder <code>lib<\/code> where <code>ng-packagr<\/code> copies the <code>package.json<\/code> over.<\/p>\n<h2 id=\"test-the-schematic-directly\">Test the Schematic Directly<\/h2>\n<p>For testing the schematic, let's build the library:<\/p>\n<pre><code>npm run build:lib\n<\/code><\/pre>\n<p>After this, move to the <code>dist\/lib<\/code> folder and run the schematic:<\/p>\n<pre><code>schematics .:ng-add\n<\/code><\/pre>\n<p><img decoding=\"async\" width=\"600\" src=\"https:\/\/i.imgur.com\/WL17rv0.png\" alt=\"Testing the ng-add schematic\"><\/p>\n<p>Even though the output mentions that a <code>hello.txt<\/code> is generated, you won't find it because when executing a schematic locally it's performing a dry run. To get the file, set the <code>dry-run<\/code> switch to <code>false<\/code>:<\/p>\n<pre><code>schematics .:ng-add --dry-run false\n<\/code><\/pre>\n<p>After we've seen that this works, generate a new project with the CLI to find out whether our library plays together with the new <code>ng add<\/code>:<\/p>\n<pre><code>ng new demo-app\ncd demo-app\nng add ..\\logger-lib\\dist\\lib\n<\/code><\/pre>\n<p><img decoding=\"async\" width=\"600\" src=\"https:\/\/i.imgur.com\/ftl0s04.png\" alt=\"ng add with relative path\"><\/p>\n<p>Make sure that you point to our <code>dist\/lib<\/code> folder. Because I'm working on Windows, I've used backslashes here. For Linux or Mac, replace them with forward slashes.<\/p>\n<p>When everything worked, we should see a <code>hello.txt<\/code>.<\/p>\n<p>As <code>ng add<\/code> is currently not adding the installed dependency to your <code>package.json<\/code>, you should do this manually. This might change in future releases.<\/p>\n<h2 id=\"test-the-schematic-via-an-npm-registry\">Test the Schematic via an npm Registry<\/h2>\n<p>As we know now that everything works locally, let's also check whether it works when we install it via an npm registry. For this, we can for instance use <code>verdaccio<\/code> -- a very lightweight node-based implementation. You can directly npm install it:<\/p>\n<pre><code>npm install -g verdaccio\n<\/code><\/pre>\n<p>After this, it is started by simply running the <code>verdaccio<\/code> command:<\/p>\n<p><img decoding=\"async\" width=\"600\" src=\"https:\/\/i.imgur.com\/ukcq0Kp.png\" alt=\"Running verdaccio\"><\/p>\n<p>Before we can publish our library to verdaccio, we have to remove the <code>private<\/code> flag from our <code>package.json<\/code> or at least set it to <code>false<\/code>:<\/p>\n<pre class=\"hljs\"><code><div>{\n  [...]\n  <span class=\"hljs-attr\">\"private\"<\/span>: <span class=\"hljs-literal\">false<\/span>,\n  [...]\n}\n<\/div><\/code><\/pre>\n<p>To publish the library, move to your project's <code>dist\/lib<\/code> folder and run <code>npm publish<\/code>:<\/p>\n<pre><code>npm publish --registry http:\/\/localhost:4873\n<\/code><\/pre>\n<p>Don't forget to point to <code>verdaccio<\/code> using the registry switch.<\/p>\n<p>Now, let's switch over to the generated <code>demo-app<\/code>. To make sure our registry is used, create an <code>.npmrc<\/code> file in the project's root:<\/p>\n<pre><code>@my:registry=http:\/\/localhost:4873\n<\/code><\/pre>\n<p>This entry causes npm to look up each library with the <code>@my<\/code> scope in our verdaccio instance.<\/p>\n<p>After this, we can install our logger library:<\/p>\n<pre><code>ng add @my\/logger-lib\n<\/code><\/pre>\n<p><img decoding=\"async\" width=\"600\" src=\"https:\/\/i.imgur.com\/sCXMxml.png\" alt=\"ng add\"><\/p>\n<p>When everything worked, we should find our library in the <code>node_modules\/@my\/logger-lib<\/code> folder and the generated <code>hello.txt<\/code> in the root.<\/p>\n<h2 id=\"extend-our-schematic\">Extend our Schematic<\/h2>\n<p>So far, we've created a library with a prototypical <code>ng-add<\/code> schematic that is automatically executed when installing it with <code>ng add<\/code>. As we know that our setup works, let's extend the schematic to setup the <code>LoggerModule<\/code> as shown in the beginning.<\/p>\n<p>Frankly, modifying existing code in a safe way is a bit more complicated than what we've seen before. But I'm sure, we can accomplish this together ;-).<\/p>\n<p>For this endeavour, our schematic has to modify the project's <code>app.module.ts<\/code> file. The good message is, that this is a common task the CLI performs and hence its schematics already contain the necessary logic. However, when writing this, the respective routines have not been part of the public API and so we have to fork it.<\/p>\n<p>For this, I've checked out the <a href=\"https:\/\/github.com\/angular\/devkit\">Angular DevKit<\/a> and copied the contents of its <code>packages\/schematics\/angular\/utility<\/code> folder to my library project's <code>schematics\/src\/utility<\/code> folder. Because those files are subject to change, I've conserved the current state <a href=\"https:\/\/github.com\/manfredsteyer\/devkit-schmatics-utility-03-2018.git\">here<\/a>.<\/p>\n<p>Now, let's add a Schematics rule for modifying the <code>AppModule<\/code>. For this, move to our <code>schematics\/src\/ng-add<\/code> folder and add a <code>add-declaration-to-module.rule.ts<\/code> file. This file gets an <code>addDeclarationToAppModule<\/code> function that takes the path of the <code>app.module.ts<\/code> and creates a Rule for updating it:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> { Rule, Tree, SchematicsException } from <span class=\"hljs-string\">'@angular-devkit\/schematics'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { normalize } from <span class=\"hljs-string\">'@angular-devkit\/core'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> * as ts from <span class=\"hljs-string\">'typescript'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { addSymbolToNgModuleMetadata } from <span class=\"hljs-string\">'..\/utility\/ast-utils'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { InsertChange } from <span class=\"hljs-string\">\"..\/utility\/change\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">addDeclarationToAppModule<\/span>(<span class=\"hljs-params\">appModule: <span class=\"hljs-built_in\">string<\/span><\/span>): <span class=\"hljs-title\">Rule<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> (host: Tree) =&gt; {\n      <span class=\"hljs-keyword\">if<\/span> (!appModule) {\n        <span class=\"hljs-keyword\">return<\/span> host;\n      }\n\n      <span class=\"hljs-comment\">\/\/ Part I: Construct path and read file<\/span>\n      <span class=\"hljs-keyword\">const<\/span> modulePath = normalize(<span class=\"hljs-string\">'\/'<\/span> + appModule);\n\n      <span class=\"hljs-keyword\">const<\/span> text = host.read(modulePath);\n      <span class=\"hljs-keyword\">if<\/span> (text === <span class=\"hljs-literal\">null<\/span>) {\n        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>File &lt;span class=&quot;hljs-subst&quot;&gt;${modulePath}&lt;\/span&gt; does not exist.<\/code><\/span>);\n      }\n      <span class=\"hljs-keyword\">const<\/span> sourceText = text.toString(<span class=\"hljs-string\">'utf-8'<\/span>);\n      <span class=\"hljs-keyword\">const<\/span> source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, <span class=\"hljs-literal\">true<\/span>);\n\n      <span class=\"hljs-comment\">\/\/ Part II: Find out, what to change<\/span>\n      <span class=\"hljs-keyword\">const<\/span> changes = addSymbolToNgModuleMetadata(source, modulePath, <span class=\"hljs-string\">'imports'<\/span>, <span class=\"hljs-string\">'LoggerModule'<\/span>, <span class=\"hljs-string\">'@my\/logger-lib'<\/span>, <span class=\"hljs-string\">'LoggerModule.forRoot({ enableDebug: true })'<\/span>);\n\n      <span class=\"hljs-comment\">\/\/ Part III: Apply changes<\/span>\n      <span class=\"hljs-keyword\">const<\/span> recorder = host.beginUpdate(modulePath);\n      <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> change of changes) {\n        <span class=\"hljs-keyword\">if<\/span> (change <span class=\"hljs-keyword\">instanceof<\/span> InsertChange) {\n          recorder.insertLeft(change.pos, change.toAdd);\n        }\n      }\n      host.commitUpdate(recorder);\n\n      <span class=\"hljs-keyword\">return<\/span> host;\n    };\n  }\n<\/div><\/code><\/pre>\n<p>Most of this function has been \"borrowed\" from the Angular DevKit. It reads the module file and calls the <code>addSymbolToNgModuleMetadata<\/code> utility function copied from the DevKit. This function finds out what to modify. Those changes are applied to the file using the <code>recorder<\/code> object and its <code>insertLeft<\/code> method.<\/p>\n<p>To make this work, I had to tweak the copied <code>addSymbolToNgModuleMetadata<\/code> function a bit. Originally, it imported the mentioned Angular module just by mentioning its name. My modified version has an additional parameter which takes an expression like <code>LoggerModule.forRoot({ enableDebug: true })<\/code>. This expression is put into the module's <code>imports<\/code> array.<\/p>\n<p>Even though this just takes some minor changes, the whole <code>addSymbolToNgModuleMetadata<\/code> method is rather long. That's why I'm not printing it here but you can look it up <a href=\"https:\/\/github.com\/manfredsteyer\/schematics-ng-add\/blob\/master\/schematics\/src\/utility\/ast-utils.ts\">in my solution<\/a>.<\/p>\n<p>After this modification, we can call  <code>addDeclarationToAppModule<\/code> in our schematic:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> { Rule, SchematicContext, Tree, chain, branchAndMerge } from <span class=\"hljs-string\">'@angular-devkit\/schematics'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { addDeclarationToAppModule } from <span class=\"hljs-string\">'.\/add-declaration-to-module.rule'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">ngAdd<\/span>(<span class=\"hljs-params\"><\/span>): <span class=\"hljs-title\">Rule<\/span> <\/span>{\n\n  <span class=\"hljs-keyword\">return<\/span> (tree: Tree, _context: SchematicContext) =&gt; {\n    <span class=\"hljs-keyword\">const<\/span> appModule = <span class=\"hljs-string\">'\/src\/app\/app.module.ts'<\/span>;\n    <span class=\"hljs-keyword\">let<\/span> rule = branchAndMerge(addDeclarationToAppModule(appModule));\n    <span class=\"hljs-keyword\">return<\/span> rule(tree, _context);\n  };\n\n}\n<\/div><\/code><\/pre>\n<p>Now, we can test our Schematic as shown above. To re-publish it to the npm registry, we have to increase the version number in the <code>package.json<\/code>. For this, you can make use of <code>npm version<\/code>:<\/p>\n<pre><code>npm version minor\n<\/code><\/pre>\n<p>After re-building it (<code>npm run build:lib<\/code>) and publishing the new version to verdaccio (<code>npm publish --registry http:\/\/localhost:4873<\/code>), we can add it to our <code>demo app<\/code>:<\/p>\n<p><img decoding=\"async\" width=\"600\" src=\"https:\/\/i.imgur.com\/mJ46JWP.png\" alt=\"Add extended library\"><\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>An Angular-based library can provide an <code>ng-add<\/code> Schematic for setting it up. When installing the library using <code>ng add<\/code>, the CLI calls this schematic automatically. This innovation has a lot of potential and will dramatically lower the entry barrier for installing libraries in the future.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Frictionless Library Setup with the Angular CLI and Schematics<\/p>\n","protected":false},"author":9,"featured_media":2997,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2376","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-unkategorisiert"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Custom Schematics - Part IV - ANGULARarchitects<\/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:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Custom Schematics - Part IV - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Frictionless Library Setup with the Angular CLI and Schematics\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2018-04-03T13:07:50+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"853\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manfred Steyer, GDE\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@daniel\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer, GDE\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/\"},\"author\":{\"name\":\"Manfred Steyer, GDE\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\"},\"headline\":\"Custom Schematics &#8211; Part IV\",\"datePublished\":\"2018-04-03T13:07:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/\"},\"wordCount\":1474,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"articleSection\":[\"Unkategorisiert\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/\",\"name\":\"Custom Schematics - Part IV - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"datePublished\":\"2018-04-03T13:07:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"width\":1280,\"height\":853},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Custom Schematics &#8211; Part IV\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\",\"name\":\"Manfred Steyer, GDE\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer, GDE\"},\"sameAs\":[\"https:\/\/x.com\/daniel\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Custom Schematics - Part IV - ANGULARarchitects","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:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/","og_locale":"en_US","og_type":"article","og_title":"Custom Schematics - Part IV - ANGULARarchitects","og_description":"Frictionless Library Setup with the Angular CLI and Schematics","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/","og_site_name":"ANGULARarchitects","article_published_time":"2018-04-03T13:07:50+00:00","og_image":[{"width":1280,"height":853,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","type":"image\/jpeg"}],"author":"Manfred Steyer, GDE","twitter_card":"summary_large_image","twitter_creator":"@daniel","twitter_misc":{"Written by":"Manfred Steyer, GDE","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/"},"author":{"name":"Manfred Steyer, GDE","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951"},"headline":"Custom Schematics &#8211; Part IV","datePublished":"2018-04-03T13:07:50+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/"},"wordCount":1474,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","articleSection":["Unkategorisiert"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/","name":"Custom Schematics - Part IV - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","datePublished":"2018-04-03T13:07:50+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","width":1280,"height":853},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-schematics-part-iv\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Custom Schematics &#8211; Part IV"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951","name":"Manfred Steyer, GDE","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","caption":"Manfred Steyer, GDE"},"sameAs":["https:\/\/x.com\/daniel"]}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2376","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=2376"}],"version-history":[{"count":0,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2376\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/2997"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=2376"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=2376"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=2376"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}