<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Roman Sedov</title>
    <description>The latest articles on Forem by Roman Sedov (@marsibarsi).</description>
    <link>https://forem.com/marsibarsi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F304039%2F4d24797b-ca17-4fbc-899a-64e05ac93435.png</url>
      <title>Forem: Roman Sedov</title>
      <link>https://forem.com/marsibarsi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/marsibarsi"/>
    <language>en</language>
    <item>
      <title>ng-morph is the best way to do big changes in your Angular project</title>
      <dc:creator>Roman Sedov</dc:creator>
      <pubDate>Thu, 06 Apr 2023 05:38:04 +0000</pubDate>
      <link>https://forem.com/marsibarsi/ng-morph-is-the-best-way-to-do-big-changes-in-your-angular-project-hah</link>
      <guid>https://forem.com/marsibarsi/ng-morph-is-the-best-way-to-do-big-changes-in-your-angular-project-hah</guid>
      <description>&lt;p&gt;I have been working on big Angular projects for years and every project follows the same path: it starts with brand new tools and approaches that lay its development style for the future. Approaches and tools change with time and the project has tech debt around migrating to new package versions, replacing a deprecated approach with the new one, etc.&lt;/p&gt;

&lt;p&gt;Sure, not every team can develop and support all the business needs and find the time to sync their code base with pioneering development methods. But what if I show you a tool with which you can transform the whole project just with several lines of codes? &lt;/p&gt;

&lt;h2&gt;
  
  
  What is ng-morph
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Tinkoff/ng-morph"&gt;ng-morph&lt;/a&gt; is a large set of tools for both global code base updates in your project and speeding up your work on Angular schematics. It has &lt;a href="https://ts-morph.com/"&gt;ts-morph&lt;/a&gt; under the hood and allows you to manipulate with TypeScript AST safely.&lt;/p&gt;

&lt;p&gt;You do not need to know how TypeScript works under the hood to transform your project. You can use simple ready functions for manipulating TS and NG entities. Moreover ng-morph adds several benefits in the process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You make changes in virtual file structure. So, you can check the new structure first, fix all the bugs and inconsistencies before saving changes in the real project structure. It is much faster than migrating a real project and reverting changes after checking. \&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can write unit tests for your functions and scripts before use it in real projects or share with a lot of colleges&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is easy to write migrations with ng-morph. You just need to invest time in your team once, and then this tool will save you several hours every week.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ng-morph has 1.6kk downloads on NPM in the last year. It is open source and fully free.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to use it in your project
&lt;/h2&gt;

&lt;p&gt;Well, let’s take a look at two common cases for ng-morph.&lt;/p&gt;

&lt;p&gt;The first one is for library developers. If you want to publish breaking changes updates for your library, it is much better to add an “&lt;a href="https://dev.toinconsistencies"&gt;ng update&lt;/a&gt;” schematic that will make migrations of your users automatic and save a huge amount of time in total. For example, Taiga UI has used ng-morph to add such a migration for a major 3.0 release with many breaking changes. In such a big library even if you ask developers to do 1 minute manual migration in your project, it’ll take hundreds of working hours in total. You can find guidelines on how to use it in schematics in &lt;a href="https://tinkoff.github.io/ng-morph/getting-started"&gt;our docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The second case is writing migrations for your own project and this is a case that I want to show you in this article. You can write a simple script in any place of your project and run it with something like &lt;a href="https://www.npmjs.com/package/ts-node"&gt;ts-node&lt;/a&gt; immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;setActiveProject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createProject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getImports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;NgMorphTree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ng-morph&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * set all ng-morph functions to work with the all TS and JSON files
 * of the current project
 * */&lt;/span&gt;
&lt;span class="nx"&gt;setActiveProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;createProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;NgMorphTree&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * This simple migration gets all imports from the project TS files and
 * replaces 'old' substring with 'new'
 * */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getImports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;some/path/**.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;moduleSpecifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@morph-old*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;editImports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;importEntity&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;moduleSpecifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;importEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleSpecifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;old&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * All changes are made in a virtual project.
 * You can save them when it is time
 * */&lt;/span&gt;
&lt;span class="nx"&gt;saveActiveProject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before reading about some real migrations, you can check our &lt;a href="https://stackblitz.com/edit/ts-angular-13-web-container-starter-nzd2ew?file=ng-morph-scripts%2Fscript.ts,src%2Fapp%2Fapp.component.ts"&gt;Stackblitz playground&lt;/a&gt;, run project migrations there and see how it works in real life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project migrations
&lt;/h2&gt;

&lt;p&gt;Let’s take a look at an example on how easy it can be with ng-morph to make global changes in your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  constructor -&amp;gt; inject
&lt;/h3&gt;

&lt;p&gt;All right, Angular introduced using &lt;code&gt;inject&lt;/code&gt; function in components and now we can replace type inaccurate constructor injection with it. Let’s write a ng-morph script in 30 minutes that migrates all the components in our project to the new approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getClasses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*Component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can get all components in several ways. The totally right way is to find all the classes with decorator &lt;code&gt;Component&lt;/code&gt; that are imported from the “@angular/core” package. But my idea is that usually we do not need such an overengineering approach writing simple migrations scripts inside our own project if we can get the same result faster.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;components&lt;/code&gt; is an array of AST entities. We can iterate it using &lt;code&gt;.forEach&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First of all, let’s skip the classes that do not inject anything. We can use the &lt;code&gt;getConstructors&lt;/code&gt; function with our class to get its constructor. After it we can process the result with &lt;code&gt;getParams&lt;/code&gt; function that will return us each injected param.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;constructorParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getConstructors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;constructorParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should also add an &lt;code&gt;inject&lt;/code&gt; function to our imports from “@angular/core”. This is how we can get all the imports and add a new one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fixInjectImport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;angularCoreImports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getImports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;moduleSpecifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="nx"&gt;editImports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angularCoreImports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;namedImports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedImports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we need to do is add new properties to the class and remove them from the constructor. This is how it looks in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;constructorParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getConstructors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;constructorParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="nx"&gt;fixInjectImport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSourceFile&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getFilePath&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

 &lt;span class="nx"&gt;addProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;constructorParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
     &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTypeNode&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
     &lt;span class="na"&gt;isReadonly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isReadonly&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
     &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getScope&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
     &lt;span class="na"&gt;initializer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`inject(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
       &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Inject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;getArguments&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;
       &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTypeNode&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;}))&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="nx"&gt;constructorParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSourceFile&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think there are no questions around a constructor params removing part. Let’s take a look at the &lt;code&gt;addProperties&lt;/code&gt; function. This function takes a class (or classes) and a properties array to add to the class. We want to keep property with the same name, type, readonly status and scope (private/public/protected) as it was in param. And we need to initialize it with the default value that will be &lt;code&gt;inject&lt;/code&gt; function with a token from &lt;code&gt;Inject&lt;/code&gt; decorator if there is one, or from the type as a fallback. Actually, that is it, we can start the script and see how it migrates our project.&lt;/p&gt;

&lt;h3&gt;
  
  
  A task for you to try ng-morph
&lt;/h3&gt;

&lt;p&gt;You can play with &lt;a href="https://stackblitz.com/edit/ts-angular-13-web-container-starter-ozrudz?file=src%2Fapp%2Fapp.component.scss,ng-morph-scripts%2Fscript.ts,src%2Fapp%2Fapp.component.html"&gt;this example on Stackblitz&lt;/a&gt; and see how it works now.&lt;/p&gt;

&lt;p&gt;You can also modify the script to handle &lt;code&gt;@Optional&lt;/code&gt; and &lt;code&gt;@Self&lt;/code&gt; DI decorators as well. It should not take much time even with no docs and if you've never used ng-morph before.&lt;/p&gt;

&lt;h2&gt;
  
  
  To be continued…
&lt;/h2&gt;

&lt;p&gt;ng-morph can become your faithful friend when it comes to global changes in your project or routine work. So, don’t forget to give a star &lt;a href="https://github.com/Tinkoff/ng-morph"&gt;on Github&lt;/a&gt; and try to use it once you need it.&lt;/p&gt;

&lt;p&gt;If it does not have something you need for smooth and easy migration, feel free to create an issue to let us help. By the way, you can also contribute it yourself because it is a great opportunity to learn more about your project structure from a compiler perspective. We have a &lt;a href="https://github.com/Tinkoff/ng-morph/blob/main/CONTRIBUTING.md"&gt;CONTRIBUTING.md&lt;/a&gt; file with all the guidelines you need to start.&lt;/p&gt;

&lt;p&gt;This is the first of three articles about ng-morph powers, so stay tuned here or on Twitter: &lt;a href="https://twitter.com/marsibarsi"&gt;@marsibarsi&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to get an expert help in big migrations of your project or make your developer experience better and faster, visit &lt;a href="//ng.consulting"&gt;ng.consulting&lt;/a&gt; page&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>angular</category>
    </item>
    <item>
      <title>Taiga UI is a new Angular UI Kit that you should try</title>
      <dc:creator>Roman Sedov</dc:creator>
      <pubDate>Tue, 19 Jan 2021 08:06:23 +0000</pubDate>
      <link>https://forem.com/taiga-ui/taiga-ui-is-a-new-angular-ui-kit-that-you-should-try-4egg</link>
      <guid>https://forem.com/taiga-ui/taiga-ui-is-a-new-angular-ui-kit-that-you-should-try-4egg</guid>
      <description>&lt;p&gt;Hey, Angular devs!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/waterplea" rel="noopener noreferrer"&gt;@waterplea&lt;/a&gt; and I write articles about Angular on different blogs from time to time. We wrote almost all of them based on our experience of developing a big UI component library. We've been developing, refactoring and growing it for several years and testing our ideas on a big number of projects in our company.&lt;/p&gt;

&lt;p&gt;We are happy to announce that we published our library into open source!&lt;/p&gt;

&lt;p&gt;In this article I want to write about concepts and practices that we build our library with and tell you why you should try it for both new and old projects even with other components or UI libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Completely modular
&lt;/h2&gt;

&lt;p&gt;Let’s start with project organization. Taiga UI includes several layers of abstractions as separate packages.&lt;/p&gt;

&lt;h3&gt;
  
  
  @taiga-ui/cdk
&lt;/h3&gt;

&lt;p&gt;It is a fundamental package. There are many Angular directives, services, tokens, base classes and utils for more safe, abstract and easy work with Angular. This package can be used as an additional multi-tool for your Angular application of any complexity. It can be also a basis for creating your own UI Kit.&lt;/p&gt;

&lt;p&gt;Examples of entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/TuiDestroyService" rel="noopener noreferrer"&gt;TuiDestroyService&lt;/a&gt; to simplify “destroy$” subjects in components&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/Empty-instances" rel="noopener noreferrer"&gt;TuiFilterPipe&lt;/a&gt; and &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/TuiMapperPipe" rel="noopener noreferrer"&gt;TuiMapperPipe&lt;/a&gt; to handle values in a template without extra ChangeDetection cycles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/Decorators" rel="noopener noreferrer"&gt;tuiPure&lt;/a&gt; decorator to memoize getters or class methods&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Our “cdk” is different from “@angular/cdk”. It is not a problem to use both of them, because it is not alternative, but addition&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  @taiga-ui/core
&lt;/h2&gt;

&lt;p&gt;This package includes basic components for building an interface and some tools to make apps easier. Here you can find things like root components, portals for dialogs and dropdowns, theming and animations. Core is a foundation for other packages with UI components. Design and common styles start here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq1jxb4lupbchiz269zss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq1jxb4lupbchiz269zss.png" alt="Buttons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  @taiga-ui/kit
&lt;/h2&gt;

&lt;p&gt;It is a large package that includes many components for building any interface. There are both simple components like avatar, tag or toggle and also composite as for example an input date component that is built with three basic components: textfield with a mask, dropdown and calendar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhb7ygnbsfhc14vf5gdlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhb7ygnbsfhc14vf5gdlt.png" alt="InputDateRange"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  @taiga-ui/addon-*
&lt;/h2&gt;

&lt;p&gt;Addons are several thematic packages that are built with cdk, core and kit. There are for example charts package, commerce package for working with currencies, money and card input. There is also a specific doc package that give tools to build your own demo portal as ours (the link will be below 🙂)&lt;/p&gt;

&lt;p&gt;We have the following structure with high-level packages built with base packages:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F72hpwhx64cf6so6kc8c6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F72hpwhx64cf6so6kc8c6.png" alt="cdk -&amp;gt; core -&amp;gt; kit -&amp;gt; addons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here comes an important question&lt;/strong&gt;: why do I need to install several packages as dependencies if I only need a couple of components? What is their size?&lt;/p&gt;

&lt;p&gt;Well, we rewrote the structure of our libraries a few months ago. We moved all our packages to the Secondary Entry Points concept.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flt9ba2nrq3k04wok45yi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flt9ba2nrq3k04wok45yi.png" alt="SEPs for UI KIT"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But we also needed to keep the previous API of imports from the root of the package. That is why we built it in a unique way where every folder deeper is a Secondary Entry Point for the folder higher.&lt;/p&gt;

&lt;p&gt;Now all our packages give an opportunity to import entities both from Primary Entry Point and any level of depth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7cphgebqtdumopzdelc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7cphgebqtdumopzdelc2.png" alt="Taiga UI SEPS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By the way, the first option is sufficient because nesting can be solved by builder automatically. You do not need to think about depth of import.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This way of organization give us many benefits as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application bundle is smaller because all libraries become fully-treeshakable&lt;/li&gt;
&lt;li&gt;Any cyclic dependencies can be catched on library building stage&lt;/li&gt;
&lt;li&gt;The project is structurized better, there are no extra bindings between its entities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that &lt;strong&gt;you can import even just one entity from our library and be sure that there is no redundant code in your bundle&lt;/strong&gt;. There is no code duplication or excess dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizable
&lt;/h2&gt;

&lt;p&gt;All styles and colors of our library are built with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*" rel="noopener noreferrer"&gt;CSS custom properties&lt;/a&gt;. It allows making new custom themes easy and you can change them on the fly.&lt;/p&gt;

&lt;p&gt;Now we offer one main theme of Taiga UI but we have plans to create several simple and several unusual themes.&lt;/p&gt;

&lt;p&gt;If you want to customize a single component, there are also some methods to do that. In theory, you can redesign our Kit for your design system in a few hours and use it without worrying because we don’t make breaking changes in CSS-variables too to prevent layout bugs. For example, our company is already using a separate proprietary theme on top of the open source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agnostic
&lt;/h2&gt;

&lt;p&gt;We want to make our components so that every developer could adjust it for their specific case fast and easy.&lt;/p&gt;

&lt;p&gt;We do not try to envision every use case. Instead, we do not restrict appearance and use generics so components are not limited to a particular data model. You can read more about such concepts in the article by Alex Inkin &lt;a href="https://indepth.dev/posts/1314/agnostic-components-in-angular" rel="noopener noreferrer"&gt;“Agnostic components in Angular”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nevertheless, we take care of basic UX aspects to let you focus on your project features. For example, when the user focuses our textfield with a keyboard, it will show a hint after a second automatically to let the screen reader read it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fck9m0s3ltkbvjpe8akz3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fck9m0s3ltkbvjpe8akz3.gif" alt="Hint a11y"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Well engineered
&lt;/h2&gt;

&lt;p&gt;We respect the best developer practices and try to follow the Angular way in development of our libraries.&lt;/p&gt;

&lt;p&gt;We are not afraid of DI, all our components use OnPush and the whole project is developed with strict TypeScript mode (we are &lt;a href="https://medium.com/its-tinkoff/typescript-tricks-that-allow-you-to-scale-your-app-endlessly-95a0ff3d160d?source=friends_link&amp;amp;sk=80868c22b17ae2d335ead5a2c927fe18" rel="noopener noreferrer"&gt;very sensitive&lt;/a&gt; about typings). If you want to use SSR one day, our components will work correctly.&lt;/p&gt;

&lt;p&gt;You do not need to worry about unexpected values of wrong types from our components or utils. Our components even print asserts in dev mode if you pass incorrect data to them :)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read more Angular tips &amp;amp; tricks in my twitter: &lt;a class="mentioned-user" href="https://dev.to/marsibarsi"&gt;@marsibarsi&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It’s big!
&lt;/h2&gt;

&lt;p&gt;We have 130+ components, 100+ directives, dozens of tokens, utils and tools.&lt;/p&gt;

&lt;p&gt;Right now you can build almost any idea of your interface quickly. And it isn't over yet: we have some ideas of next components and we are open to your requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to start using Taiga
&lt;/h2&gt;

&lt;p&gt;Visit our official website with documentation. Here you can see a demo of our components, learn about our libraries and find instructions how to add it into your project: &lt;a href="https://taiga-ui.dev/" rel="noopener noreferrer"&gt;taiga-ui.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to support us or see our progress, give a star and follow &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI on Github&lt;/a&gt;. There you can ask any questions, propose an idea or contribute to our code.&lt;/p&gt;

&lt;p&gt;We also have &lt;a href="https://discord.com/channels/748677963142135818/757654627670818968" rel="noopener noreferrer"&gt;a channel in Angular discord&lt;/a&gt;. Feel free to contact us there!&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not goodbye
&lt;/h2&gt;

&lt;p&gt;We want to write more articles about how we organize and develop our libraries. Such articles can help you understand better how our UI Kit works. They will also explain all our tricks and practices of developing easy reusable components on Angular that are very helpful in application development too.&lt;/p&gt;

&lt;p&gt;Tell us your opinion about Taiga UI and share your ideas about which components, tools or processes you want to read first&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to Stop Worrying and Start Opensource (with a good deploy, CI and demo)</title>
      <dc:creator>Roman Sedov</dc:creator>
      <pubDate>Tue, 07 Jan 2020 20:16:43 +0000</pubDate>
      <link>https://forem.com/marsibarsi/how-to-stop-worrying-and-start-opensource-with-a-good-deploy-ci-and-demo-39db</link>
      <guid>https://forem.com/marsibarsi/how-to-stop-worrying-and-start-opensource-with-a-good-deploy-ci-and-demo-39db</guid>
      <description>&lt;p&gt;So you made a new cool thing and you get an idea to share it in open-source and to publish on NPM.&lt;/p&gt;

&lt;p&gt;You cannot just publish your code in a public repository. It will doom the project to a lack of development and failure. On the other hand, you know there’s a lot of collateral work: versioning and publishing of package, setting up Continuous Integration, hosting and deploy for project demo, organizing community contribution.&lt;/p&gt;

&lt;p&gt;If you just want to publish a small package, such amount of work can scare you away. The bright idea of sharing something useful will get buried in a box labeled «complicated tasks».&lt;/p&gt;

&lt;p&gt;Actually, all of this can take you less than an hour. Without any knowledge of DevOps and for free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gfd1kpH0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/6720/1%2AUEH1z2wgOAiTlFD0QFc8eA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gfd1kpH0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/6720/1%2AUEH1z2wgOAiTlFD0QFc8eA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning
&lt;/h2&gt;

&lt;p&gt;If your library is ready for its first release, you can use &lt;a href="https://github.com/conventional-changelog/standard-version"&gt;standard-version&lt;/a&gt;. This package will be fully responsible for the versioning of your library. It will bump versions of &lt;em&gt;package.json&lt;/em&gt;, generate CHAGELOG.md file and add tags for your git history.&lt;/p&gt;

&lt;p&gt;It manages &lt;em&gt;CHANGELOG.md&lt;/em&gt; using &lt;a href="https://www.conventionalcommits.org/en/v1.0.0-beta.4/"&gt;Conventional Commits&lt;/a&gt;. This also means you will have a clear commit message format in your package. It is helpful for you and any developer who decided to contribute to your project.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://github.com/conventional-changelog/standard-version"&gt;standard-version&lt;/a&gt; is easy. How to use it is described in detail on the &lt;a href="https://github.com/conventional-changelog/standard-version#standard-version"&gt;Github page&lt;/a&gt;. We can also add a set of release commands into our &lt;em&gt;package.json&lt;/em&gt; to make our releases more convenient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"release": "standard-version",
"release:patch": "npm run release -- --release-as patch",
"release:minor": "npm run release -- --release-as minor",
"release:major": "npm run release -- --release-as major",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Organizing CI
&lt;/h2&gt;

&lt;p&gt;I suggest using &lt;a href="http://travis-ci.org/"&gt;Travis CI&lt;/a&gt; for Continuous Integration. It is friendly to users:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Login through Github&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose your project from the list and activate Travis in it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a simple config in the root folder of the project. Travis will execute this config on CI&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    language: node_js
     node_js: - "10"
    script:
     - npm run lint
     - npm run build
     - npm run test:ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The basic CI is ready. Travis will run linters, build a project and run tests after every update of your repository.&lt;/p&gt;

&lt;p&gt;You can also see the state of all your branches and Pull Requests and analyze each running build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N7cwIDPc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3072/1%2AzhLJsFISxg12KdEEHDBUFA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N7cwIDPc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3072/1%2AzhLJsFISxg12KdEEHDBUFA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other developers will feel more confident using your package because all your code is tested.&lt;/p&gt;

&lt;p&gt;We can delegate code coverage control to an application-specific service &lt;a href="https://coveralls.io/"&gt;Coveralls&lt;/a&gt;. Travis will send the result of tests after each CI build.&lt;/p&gt;

&lt;p&gt;We just need to login to Coveralls and turn it on for a repository. Like in Travis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work on the project side:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Set up Coveralls as a dev-dependency of your project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the script for running coveralls and add it to &lt;em&gt;test:ci&lt;/em&gt; command&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "test:ci": "npm run test &amp;amp;&amp;amp; npm run coveralls",
    "coveralls": "cat coverage/lcov.info | coveralls",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not forget about a --code-coverage flag in your npm run test script. Coveralls needs your file &lt;em&gt;lcov.info&lt;/em&gt; that your test runner generates. If you do not have such a flag, you can use &lt;a href="https://istanbul.js.org/"&gt;istanbulJS&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work on the Travis side:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your repository is public, you can set it up with a couple of new strings in &lt;em&gt;.travis.yml&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    notifications:
       webhooks: https://coveralls.io/webhook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In any other case you can bind them using a token:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to repository settings on Coveralls and generate Repo Token: &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YWDcpHKn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AAX2G2GTGxsJLb0oF-C9vew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YWDcpHKn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AAX2G2GTGxsJLb0oF-C9vew.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the token as environment variable called COVERALLS_REPO_TOKEN in repository settings on Travis:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rLUU372v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3062/1%2An2N6DCeuAj2NH1jljlfWbQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rLUU372v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3062/1%2An2N6DCeuAj2NH1jljlfWbQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The token binds these two services. Now we can also protect the &lt;em&gt;master&lt;/em&gt;-branch of our project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to Github repository -&amp;gt; Settings -&amp;gt; Branches&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new rule for all branches: *&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turn on status checks. Now you cannot merge a pull request into &lt;em&gt;master&lt;/em&gt; until Travis and Coveralls gave you green lights&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ew8CSD00--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ASu_xEedSVfgCxFU6PkUbgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ew8CSD00--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ASu_xEedSVfgCxFU6PkUbgg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can turn back to *Coveralls *settings. There is a section “PULL REQUESTS ALERTS”. You can set a level of code coverage decrease that will trigger failing a build of your CI. Do it to be sure that your package is well tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improve workflow
&lt;/h2&gt;

&lt;p&gt;All code in your project should follow the same coding convention. Otherwise, the code base will gradually degrade and it will be harder for you to mantain the project. Do not forget that other developers are not very interested in your code style guide. That is why you need to automize this issue.&lt;/p&gt;

&lt;p&gt;Firstly, check that you have &lt;em&gt;.editorconfig&lt;/em&gt; file in your project folder and there are settings on how to format your code.&lt;/p&gt;

&lt;p&gt;After that, you should install the following dev-dependencies: &lt;a href="http://github.com/typicode/husky/"&gt;husky&lt;/a&gt; and &lt;a href="https://github.com/okonet/lint-staged"&gt;lint-staged&lt;/a&gt;. The first one calls your scripts on git hooks. The second one runs your linters only for files that are staged for commit.&lt;/p&gt;

&lt;p&gt;For example, a sample of settings for a package with TypeScript and Less:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   ...
   "scripts": {
       ...
       "typecheck": "tsc --noEmit --skipLibCheck",
   },
   "husky": {
       "hooks": {
           "pre-commit": "lint-staged &amp;amp;&amp;amp; npm run typecheck"
       }
   },
   "lint-staged": {
       "*.{js,ts,html,md,less,json}": [
           "prettier --write",
           "git add"
       ],
       "*.ts": "tslint",
       "*.less": "stylelint"
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not have any linters in your project, I can recommend you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt; for code formatting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://eslint.org/"&gt;eslint&lt;/a&gt; or &lt;a href="https://eslint.org/"&gt;tslint&lt;/a&gt; as a linter for JS/TS files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stylelint.io/"&gt;stylelint&lt;/a&gt; for files with styles&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many ready to use configs for them on Github that take into account all important linting rules. For example, you can take our solution: &lt;a href="https://github.com/TinkoffCreditSystems/linters"&gt;@tinkoff/linters&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish on NPM
&lt;/h2&gt;

&lt;p&gt;And now it is time to publish your package. Add one simple script to &lt;em&gt;package.json&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"publish": "npm run build &amp;amp;&amp;amp; npm publish ./dist"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In this case, our repository contains source code and dev files, but NPM gets only a compiled package without anything else.&lt;/p&gt;

&lt;p&gt;We build and publish. Cool!&lt;/p&gt;

&lt;p&gt;And furthermore, you can create some scripts that will help you with publishing. For example, let’s use NPM-hooks and add &lt;em&gt;postbuild&lt;/em&gt; script that will copy &lt;em&gt;README.md&lt;/em&gt; file to the dist folder. So we will not forget to update the package description on NPM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "build": "..",
    "postbuild": "node scripts/postbuild.js",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up demo
&lt;/h2&gt;

&lt;p&gt;Not every package needs a demo page. If your package has a couple of methods in its public API that is well documented, you can skip this part.&lt;/p&gt;

&lt;p&gt;In other cases, it would be better to show something. Do not forget that we publish a library. The typical demo on Github pages is not the best option for us. It is more convenient for developers to open your demo in some online IDE: they can see how to work with a package, change something or test a worrying case.&lt;/p&gt;

&lt;p&gt;You can make a demo in another repository or just put it into a new folder near your project. We don’t even have to set up a deploy! Modern online IDEs can pull your projects or their single branches/folders from Github.&lt;/p&gt;

&lt;p&gt;Some services that allow you to open your project in a couple of minutes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackblitz.com/"&gt;stackblitz.com&lt;/a&gt; can open Angular, React, Ionic, TypeScript, RxJs and Svelte. The information about how to open your project from Github with one link is &lt;a href="https://stackblitz.com/docs#import-from-github"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://codesandbox.io/"&gt;codesandbox.io&lt;/a&gt; can open Angular, React, Vue and compile JavaScript. Here you can also &lt;a href="https://codesandbox.io/docs/importing#import-from-github"&gt;open your project with a link&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://repl.it/github"&gt;repl.it&lt;/a&gt; is a service that can import a repository with NodeJS, Express, NextJS, GatsbyJS. TypeScript and vanilla JS are also available.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can choose any option, add a link in your &lt;em&gt;README.md&lt;/em&gt; and forget about setting a demo page. All updates will work automatically.&lt;/p&gt;

&lt;p&gt;You can also add a script to CI that will build a demo with the last version of the package in NPM. It can be an additional check that an actual version works well on an external package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final touches
&lt;/h2&gt;

&lt;p&gt;Add badges into &lt;em&gt;README.md&lt;/em&gt;. It is a little thing but it helps a visitor of your package to navigate on Github.&lt;/p&gt;

&lt;p&gt;For example, there are four badges saying that the project is built and has good code coverage, you can access NPM in one click.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j5ZjAiJy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2A_nYNWK_1B-FtsI28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j5ZjAiJy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/0%2A_nYNWK_1B-FtsI28.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recommend using a service &lt;a href="https://shields.io/"&gt;Shields.io&lt;/a&gt; to generate badges. They are simple and of high quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Such a set of tools is enough to give a good start for your project. An occasional visitor on NPM or Github will take your project more likely because it is well-described and has a good demo page.&lt;/p&gt;

&lt;p&gt;This foundation will allow you to accept external Pull Requests restfully. Other developers will fork it without issues. Now you can focus on the implementation of the package itself, without worrying about the processes around its development.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>github</category>
      <category>opensource</category>
      <category>npm</category>
    </item>
  </channel>
</rss>
