5

How To Update Multiple NPM Packages Effectively?

 9 months ago
source link: https://blog.bitsrc.io/update-multiple-npm-packages-a1d49636ffca
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How To Update Multiple NPM Packages Effectively?

Use Bit to ensure that your NPM packages are always up-to-date

1*Qo9Hvgl_GPgy5yVOGxN0iQ.png

If you’re building NPM packages, you never include everything inside one package, right? For example, let’s say that you’re building a package which has the following features:

  1. A logger module that is capable of printing a detailed log message. If you pass in a message, it logs the time, message, and a few system metrics for better analysis.
  2. A helper module that provides a DynamoDB Wrapper. You might want to include reusable code to simplify CRUD operations in DynamoDB.

Now, if you bundle both of these two modules in a single library, you can’t ever use this Logger module without installing the DynamoDB helper. If you had a requirement where you wanted to build a UI library but use the same logging library for logs, you can’t do it!

0*yEcUq4gCkL--1OKa.png

Figure: Installing the Single Library to the UI Library

Therefore, as a rule of thumb, you’re always encouraged to publish these as two NPM libraries — A Logger and a DynamoDB Helper. By doing so, you have successfully allowed each library to be used independently of each other.

But this is where things get problematic.

Let’s say that in the future, you release a new version of the Logger — v0.0.3 that optimizes how the logs are printed. So, to get this new release onto the other libraries, you’d have to manually bump the dependency of both the DynamoDB Helper and the UI Library.

0*zWG2PBI5elf_7HQC.png

Figure: Manually bumping library versions

This will get things done for the moment. But imagine you have thousands of libraries depending on this Logger. It’s physically impossible to go and update the versions one by one.

But what can we do to fix this problem?

The Need For Modern Build Systems

Well, the answer is simple. You need a build system capable of keeping track of a particular library’s usages and propagating an update to all its consumers.

Lucky for you, I know just the tool.

Meet Bit — A Modern Build System for Composable Software

0*KjaJ7cmLw0m_etYF.png

Figure: Bit.dev

Bit is a next-generation build system for composable software. It’s a tool that lets you independently design, develop, and build components (Frontend, Backend, Utility Libraries — literally anything). It takes this one step further by keeping track of each component’s usages, and then leveraging modern CI servers to propagate the changes up the tree.

Updating Multiple NPM Packages with Bit

Bit lets you directly ship components to the NPM registry. By default, Bit ships your components to its component registry — Bit Cloud. But, Bit let’s you extend this behavior and publish to other registries like NPM after syncing with the Bit Cloud.

So, let’s look at how we can accomplish this. For our demo, let’s build two libraries:

  1. A Logger library: This library will be capable of accepting a log message and displaying a well-structured and formatted log.
  2. A “isEven” library: This library will be capable of accepting a number and determining if it’s even or not. Additionally, it’ll use our “logger” library to log the result before returning it to the consumer.

We’ll be using Bit to build these two libraries in isolation and ship them off to the NPM registry.

Step 01: Pre-requisites

First, you’ll need to install the Bit CLI using the command:

npx @teambit/bvm install

Next, you’ll need to verify that the CLI has been installed successfully by running the command:

bit --version

If you’ve installed Bit correctly, you should see the screenshot below.

0*Tdqo-IFJ-f5QSc45.png

Figure: Sucessfully installing Bit

Additionally, you must setup an NPM Organization along, install NPM Locally and login to your NPM account using npm login. To confirm the authentication, run npm whoami. If you're authenticated, it'll display your username.

Step 02 — Initializing a Development Environment

Next, you’ll have to create a Bit workspace to help build composable components we can reuse across applications!

To do so, run the command:

bit init

Afterward, you’ll see a file being created — workspace.jsonc. This file will be help us customize our workspace. Next, we'll need to tell our workspace that all components we're working on will be published to NPM. Therefore, open your workspace.jsonc file and add the config:

"teambit.workspace/variants": {
"*": {
"teambit.pkg/pkg": {
"packageJson": {
"packageManagerPublishArgs": [
"--access=public"
],
"private": false,
"name": "@{scope}/{name}",
"publishConfig": {
"scope": "@{scope}",
"registry": "https://registry.npmjs.org/",
"access": "public"
}
}
}
}
},

Next, you’ll need to create an account on Bit Cloud. Afterward, create an organization in Bit. Inside the organization, create a Bit Scope using your NPM organization name, like I’ve done below:

0*i4uOK_gCQEyll6zt.png

Figure: Creating a Scope on Bit inside the organization

As shown above, the name of my NPM organization is lakinduhewawasam. But you're free to replace this with your NPM Organization name.

Step 03: Configuring Ripple CI to publish to NPM

Next, let’s configure the Ripple CI on our Bit Cloud organization so that it has permission to publish to NPM. To do so, you’ll first need to create an NPM Access Token that lets users write to your organization. After this, visit your organization settings in Bit Cloud.

For simple access, paste this URL in your browser and include your organization name in the placeholder:

https://bit.cloud/<<ORG-NAME>>/~settings/ripple

Afterward, you should see the output:

0*fyOB4fgNL_FS4zxa.png

Next, click “Create New” and create a new token with the name: NPM_TOKEN and the value as the token you created through NPM:

0*nympGEw_cDzdLbnY.png

Figure: Creating the NPM Token

Afterward, you’re ready to start building your components!

What’ll happen is that when you publish a component to your Bit Cloud Scope, it’ll trigger a build on its Ripple CI. After a successful build, it will publish the components to NPM

Step 03: Building the Libraries

Next, let’s build the two libraries:

  1. Logger
  2. IsEven

To do so, we’ll need to create a dev environment. Since Bit isn’t language-dependent, you can develop anything as long as you provide a dev environment.

Simply put, think of a dev environment as the being that gives life to your component! It defines how your component is tested, built, linted and the dependencies included in each component.

So, let’s build our library using Node.js. Let’s create a Node.js env:

bit create node-env envs/node --aspect teambit.node/node

After you run the command, you should see the output:

0*OmXZz5DkCYZ8CtdS.png

Figure: Creating a dev environment

Next, let’s create the two functions bundled as two NPM packages. To do so, we have to create two Bit components, and Bit automatically packages it up in its own independent environment. Run the command:

bit create node is-even --env envs/node && bit create node logger --env envs/node

Once you do so, you’ll see an output like this:

0*SC7FEg6wTInsY0EA.png

Figure: Creating the components

Next, you should be able to see the following folder structure:

0*U8L_jDGrmhuXC6A9.png

Figure: Viewing created folder structure

You’ll see five files:

  1. index.ts: This file serves as the entry point to your package.
  2. composition.ts: This file lets you independently view all the variations of your component. For example, look at this - https://bit.cloud/dummyorg/lakinduhewawasam/is-even/~compositions
  3. docs.mdx: This file will let you define documentation for your component.
  4. spec.ts: This file lets you define test cases for your component. When Ripple CI builds your components across the tree, it'll execute tests and ensure the components work as expected.
  5. <<module-name>>.ts: This file lets you define your implementation.

So, let’s go ahead and implement our components. I adopted test-driven development when I designed my packages as I wanted to ensure that the code I write passes possible outcomes:

So, here’s my test case for the logger package:

import { logger } from './logger';

it('should log', () => {
expect(logger('hello world')).toBe(`
**********INSERTING LOG**********
**********HELLO WORLD**********
`);
});

Afterward, I implemented the Logger based on the test case to make sure it passes:

export function logger(message: string) {
const log = `
**********INSERTING LOG**********
**********${message.toUpperCase()}**********
`
console.warn(log)
return log;
}

Next, I followed the same process for the is-even package. I started off with two test cases:

import { isEven } from './is-even';

it('should return 2 as an even number', () => {
expect(isEven(2)).toBe(true);
});

it('should return 10001 as an odd number', () => {
expect(isEven(10001)).toBe(false);
});

Hereafter, I implemented the package to fit to these test cases:

import { logger } from "@lakinduhewawasam/logger";

export function isEven(toCheck: number): boolean {
const resp = toCheck % 2 === 0;
logger(`is even - ${resp}`)
return resp;
}

As you can see, we’re using the logger inside the isEven function:

0*upWW6Es4ZWEeDzQb.png

Figure: The dependency tree

Next, after implementing the two packages, it’s essential to verify that your components pass the test cases. To do so, run the command:

bit test --all

This will test your components locally and will denote the test results. Luckily for me, everything passed on the first go ;)

1*rGm5RDpflgKzId30tGP9BA.png

Figure: The Test Results

Step 04: Publishing to NPM

After you’ve tested and verified the functionality, it’s time to ship it to the public. To do so, run the command:

bit tag && bit export

This will generate an output similar to the one I’ve attached below:

1*j5QmTyMyJtXy74JvO-cw6Q.png

Figure: The Exported URL

After you visit your URL, you’ll see your components being built:

0*2CbbXyPnwXNP09xZ.png

Figure: Viewing the Ripple Build

After your components get built, they’ll automatically get published to the NPM repository. After the build succeeds, head over to your NPM Organization, and you should see the libraries published:

0*8AQVSIbHm2MlP5_o.png

Figure: Viewing the published libraries

You can check my libraries on NPM, too:

That was easy.

But this isn’t the best part of this!

With this approach, you can seamlessly change one component and update the entire tree. By doing so, you can ensure that your packages always use the latest versions of the children. To experiment with this, let’s update the logger:

// logger.ts
export function logger(message: string) {
const log = `
**********NEW LOG INSERTED**********
**********${message.toUpperCase()}**********
`
console.warn(log)
return log;
}

// logger.spec.ts

import { logger } from './logger';

it('should log', () => {
expect(logger('hello world')).toBe(`
**********NEW LOG INSERTED**********
**********HELLO WORLD**********
`);
});

Now, theoretically, the isEven function should also be updated. To test this out, let's re-export the components to Bit Cloud using

bit tag && bit export

Afterward, you should see Ripple CI building both the isEven and Logger:

0*uq4ICA9PhXzxFIQy.png

Figure: Viewing the build diff

This will automatically publish packages to NPM:

0*0KfJKbBvczRDno01.png

Figure: Viewing the published packages

Wrapping Up

And just like that, we ensured that all of our NPM packages use dependent packages that are always up to date! This ensures that components always get the latest optimizations, making code maintenance a breeze!

If you like what you see, check out the full code in my Bit Scope.

Thank you for reading.


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK