We are testing the creation of a component library for Angular with the help of a new command for Angular / Cli - library


    When projects become a little more than one, there is a need to somehow reuse not only individual modules with code, but also the UI components themselves. There are a lot of solutions to the problem - from the traditional copy-paste, to setting up a separate project with tests, documentation, and even blackjack.


    The problem is that the second option requires considerable preparation efforts and each such project is unique - with its own toolkit in which each new developer needs to be dealt with anew. At the end of July, the Angular team proposed its own, integrated solution to this problem by adding a new library creation command to angular / cli.


    Let's see what came of it.


    For the tests, the freshest of the stable versions of angular / cli is taken - 6.1.5 (09/04/2018)


    Perfect world


    In a perfect world, everything should be comfortable. So, for the library component, I would highlight three important points.


    • Uniformity of projects and quick start
    • Ease of development
    • Ease of distribution

    So let's start from the start


    In order to create our own library, we need to take two steps - to create a new project and add a library to it. First, create a new project:


    npx @angular/cli@latestnew mylibapp

    npx

    I use npx to not install cli globally and avoid npm run constructs. If you have npm version 5.2 or newer, try it. Read more here.


    After executing the command, we will see the standard (for the 6th angula, which differs from the 5th version) project in which two sub-projects will be created - the main mylibapp and mylibapp-e2e. The angular project itself is now described in angular.json.



    Libraries, as we see, not yet.


    And here he is the first nuance. Our name is already taken up with the main project, and it’s no longer possible to name the library. Therefore, if you want to name the library my-super-library, you first need to create a project that should be called something different. For example, my-super-library-project. And only then, create a library with the desired name.


    Now we will create the third sub-project and generate the library.


    cd mylibapp
    npx ng generatelibrary mylib --prefix mlb

    It is not necessary to specify the prefix, but it is very desirable not to intersect with other libraries.



    As you can see, now our third library has been added to the third sub-project. It has its own separate package.json, tsconfig and karma.conf.js, which allows you to customize it without fear of offending other projects. By the way, if you wish, we can add another library and it will also be a separate subproject. But that is why the library could not be identified as a completely separate project (like for example in .Net) I do not know. And if e2e project is not difficult to remove with your hands, then the main project is no longer. As a result, an extra code appears in the repository, which is not very good.


    Now let's see what tools we get right away. This is a bunch of tslint + codelyzer, karma + jasmine and protractor for e2e. Those. a standard set of angular project, nothing specific for the library was given to us. This is a little strange, since some tool for viewing components and rendering them to the documentation (for example, a storybook ) is just a must have. But okay, we will assume that here we just left room for maneuver.


    Let's run the tests and the linter to make sure everything works.


    npm test mylib
    npx ng lint mylib

    Everything went smoothly for me, but Chrome was used for testing, which is also strange. I have nothing against him, but on the build servers it will not be 90%. Why not use the same Puppeteer - is not clear.


    Let's sum up:


    pros


    • Quick start a new project
    • Uniform approach

    Minuses


    • Extra code in the project
    • Obvious things you need to finish your hands

    So far, nothing critical, we continue to dig further.


    Development


    We already have some components out of the box, let's look at them. Since we have no special tools, we will use the main project (here it turns out why we need it). To do this, we need to bring down the library, import the library module and launch the main project.


    some code
    npx ng build mylib

    import { MylibModule } from"mylib";
    ...
    @NgModule({
      declarations: [
        AppComponent,
      ],
      imports: [
        BrowserModule, MylibModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })

    npm start

    After everything is executed, we will see our component from the library. But again there is a nuance - the watch mode for the library has not yet been done, do you need to run the library build on your own each time? Watch will appear only in angular / cli 6.2+. And not out of the box, for this you have to add a new flag in tsconfig.json


    tsconfig.json


    "angularCompilerOptions": {
        "enableResourceInlining": true,
    }

    And then run the build with the watch flag:


    ng build mylib --watch

    If, for some reason, you use cli under 6.2, you have to build it yourself, which, frankly, is bad.


    Now let's add a new component. To do this, run the standard command generate component. Due to the fact that the library is not our main project, we have to use the project flag, which is also a bit annoying (but if the library was an independent decision ...).


    npx ng generatecomponent some-nice-image --project mylib

    Now, under mylib / src, we will create an assets folder, add a picture and rebuild the library again to see the result. And here we are waiting for another surprise - no pictures. It turns out that the resources used in the library do not fall into the build automatically, they need to be copied independently (or like this ). And it seems not to be scary, but still somehow not right.


    On the other hand, tree-shaking should work out of the box. Let's create another component in the library but do not use it in the main project. We collect the main project in the production mode


    npx build --prod

    And we see that the size of the bundle has not changed. Tree-Shaking with libraries really works!


    Now it would be nice to try to put some kind of dependency. Since each project has its own package.json, we need to first go to the library folder and execute the npm install command


    npm i -D @drag13/when-donpm i @drag13/round-to

    I deliberately put them in different ways to check how the packer would deal with it later. Everything is put without problems. We try to collect and get a warning


    Distributing npm packages with 'dependencies' is not recommended. Please consider adding drag13 / round-to to 'peerDependencies'or remove it from' dependencies

    Distribution of npm packages with dependencies is not desirable. Please consider adding drag13 / round-to dependency to peerDependencies or removing it from dependencies altogether.


    and, then, and the error:


    Dependency drag13 / round-to must be explicitly whitelisted

    Drag13 / round-to dependency must be explicitly added to the whitelist


    This is already interesting, by design, the library does not want to have direct dependencies. We try to move our dependency to the peerDependencies section and get together again - voila, everything works. But this means the order of installing third-party libraries is now different. First, we set the dependency on the main module, then, with pens, add to the peerDependencies section of the library.


    The rest works the same as in the usual Angular project.


    Briefly summarize:


    Pros:


    • We work in familiar surroundings with familiar teams
    • Have a tree-shaking out of the box

    Minuses:


    • For "see component" you need to use the whole project.
    • There is no watch mode yet
    • Resources need to be copied manually or set up the build process by yourself.

    And finally, proceed to the publication


    Publication


    Here everything is really good. For publication, angular / cli uses the already well-proven ng-packgr, which independently compiles our code into a suitable for publishing npm package leaving the package.json file setting (and this is not small), minification, packaging in different formats (for example, in UMD) .


    To publish your package (or see what's inside) you need to run three commands.


    npx ng build --prod
    cd dist/mylib
    npm publish

    If you don't want to publish, replace the publish command with pack.


    As a result, I got the following:



    First, let's take a look at package.json, which does not look like the original package.json of our library.



    As you can see, packagr did not delete our devDependencies, although some do. In addition, the number of formats that are described in package.json is theoretically good (even if I don’t know half of them).


    Inside the package contains a minified and non-minified bundle in UMD format, and several other bundles of the internal angular format (fesm5, fesm2015). But, most importantly, now this will not hurt the head of the developers that is just great.


    Let's go to the conclusions


    Pros:


    • Convenience
    • Thoughtfulness

    Total


    The solution was interesting, but raw. Starting and publishing is very convenient, but there are still questions to develop. It is particularly disturbing that now the library is not an independent project by design, but rather an addition to the main project with the possibility of publication.


    On the other hand, a large piece of work has been done, the functionality is constantly evolving, and I am sure that over time we will get a great tool for development.


    Also popular now: