In the life of every Front-End engineer, comes a moment when you want to put some code into a separate build and use the same code across different applications. In this article, I'll explore the way of packaging and publishing a library that was built by Angular. But before we start, let's discuss some benefits of this and cases why you should think about this approach.
So, what can you get if you decide to extract some code into another library? Here are some pros:
Code Reusability: By extracting some code to a library, you can reuse that code in other parts of the same project, or even in different projects. This can save time and effort and also help to ensure consistency.
Simplified Code: It makes your main project code cleaner and easier to understand, as you can abstract away the complexity into the library.
Easier Maintenance: If a bug is found in the code that's been extracted to a library, you only need to fix it in one place. Similarly, if you want to update or improve that code, you can do so in the library, and all parts of your project that use the library will benefit. But yes, you should update a version of your package in each project.
Collaboration: If you're working in a team, different team members can work on the main project and the library simultaneously. This can make the development process more efficient.
Testing: It's easier to write unit tests for smaller pieces of code, so by extracting some code to a library, you can improve your test coverage and code reliability.
Documentation: It's easier to document the functionality of a standalone library. This can be beneficial for both your current project and future projects that might use the library.
Sounds great, isn't it? But hold on, nothing is ideal. Before you create your first library, think about the cons:
Overhead: Creating a library can add overhead in terms of time, effort, and common sense. It requires careful planning, designing, testing, and documentation.
Maintenance: Once you've created the library, you'll need to maintain it. This includes fixing bugs, adding features, and ensuring compatibility with different environments.
Complexity: While a library can simplify the main project, it adds another layer of complexity in terms of understanding and using the library correctly.
Learning Curve: For other developers who join the project, there might be a learning curve to understand how the library works.
Versioning: Managing versions of the library can become complicated, especially if the library is used in multiple projects.
After careful consideration of these bullets, we are ready to start with an actual setup. It's pretty simple to do with the Angular CLI (ensure that you have installed @angular/cli
on your computer). Open a folder where you want to place your lib and run these commands:
ng new my-workspace --no-create-application
cd my-workspace
ng generate library lib-name
These commands will create an angular workspace and your lib in projects/lib
, which contains your library project's logic and data.
Be aware that the builder setup for the project differs from the default builder for application projects. This particular builder ensures that the library always utilizes the AOT compiler, among other functions.
For the code in your library to be reusable, it's crucial to establish a public API. This "user layer" outlines the features accessible to your library's users. Users should be able to access public functions (such as NgModules, service providers, and general utility functions) via a single import path. The public-api.ts
file in your library folder is where your library's public API is maintained. When your library is incorporated into an application, everything exported from this file becomes publicly accessible.
It's recommended that your library includes documentation (usually a README file) that guides installation and maintenance.
For our demonstration purposes, this setup will be enough. Let's move further to publishing. But before this, you need to build the library. Update the version of the lib in projects/lib/package.json
and run the build
command in the root package.json
.
As an output, you will get the dist
folder with compiled code. Since you have built your code, you need to choose where you want to publish your library: either a public registry or a private one. Let's explore both options.
Public registry. By default, the npm public registry is https://registry.npmjs.org. It means that all of your downloading packages are coming from this registry. Hence, when you want to publish something, you will publish it here. One thing that you need is to be logged in to the npm.
Private registry. This approach is needed when you work in an organization or a team that doesn't want to publish their code publically and have their own registry. In most cases, you also will have some instructions on how to set the needed config, but I will briefly go through it:
Each repository, which was created within the organization has its ID and link to the registry. Usually, it looks like this:
https://{organization_link}/projects/{project_id}/packages/npm/
Once you find this link, you need to create a .npmrc
file, and put the necessary changes here. This file is used when you need to provide some settings to NPM. In our case, we will change the registry. Here is an example of how this file can be configured:
{lib-name}:registry=https://{organization_link}/projects/{project_id}/packages/npm/
{organization_link}/projects/{project_id}/packages/npm/:_authToken={your_personal_access_token}
You should pay attention to the token part. You can generate it in your profile account, copy it, and paste it into the .npmrc
file. Since it's your personal token, it must not be published into a remote repository. So, make sure that .npmrc
is in .gitignore
If you don't want to use the .npmrc
approach, you can simply run this command: npm config set registry <registry url>
. This is a pretty straightforward approach and isn't suitable if you have more than one library with different registries. For better scalability and a better experience, I recommend the .npmrc
approach
Copy the .npmrc
file, and put it into the dist folder, on the same level as the package.json
file.
Run npm publish
You are awesome!
As you can notice, this is not so hard. But in the roadmap above, you can see some repetitive manual action that will annoy you after some time - copying, pasting, and publishing the lib. For this case, we have an elegant solution:
Create a .npmrc.template
file (actually, it can be named whatever you want).
Put there all of your config from .npmrc
but replace the token section with ${CI_TOKEN}
. This is a placeholder where the actual token will be inserted in the CI environment. But remember, it can be various in different vendors and CI strategies. The name of this token is up to you;
In your yml file, make these changes:
publish:
image: node
stage: publish
dependencies:
- build
script:
- mv .npmrc.template .npmrc
- cp .npmrc dist/lib-name
- cd dist/lib-name
- npm publish
This step allows you to automate your publishing. Here, we rename our .npmrc.template
into a .npmrc
file, then copy it into the dist folder, navigate here, and run npm publish. Now, you don't have to do it manually each time when you want to update your library.
In conclusion, while creating and maintaining a library can involve some complexity and overhead, the benefits of code reusability, simplified project code, and easier maintenance often outweigh these challenges. With careful planning, good documentation, and the use of automation for tasks such as publishing, managing a library can become a manageable and rewarding part of a Front-End engineer's workflow.