Let’s Talk About Node Packages

let’s-talk-about-node-packages

I made a video I want to share with you all, but I know you aren’t in DEV to watch videos, so under it you get a transcription of the video in the shape of an article so you can read it at your own pace (the video has some jokes in it, tho). I hope you like it:

Transcript

Hey! Let’s talk dev.

If you have played with the node package ecosystem for a while, you know it’s a [redacted] nightmare. So today my idea is to turn my code induced tears into something useful for you all.

Dependency types

First, let’s talk about dependency types. We have three types of dependencies:

  • Production dependencies
  • Development dependencies
  • Peer dependencies.

Production dependencies

Production dependencies are defined in the "dependencies" property of our package.json file. Anything that we use in production when our package is being used, needs to be defined in here. That means that, for example, if we are using a library of utils or something like that from our package, it needs to be part of our dependencies. So, when our consumers install our package, they will get a copy automatically of this dependency.

One important note is that you need to avoid, trying to import something that is not defined there. Assuming that it will be there because maybe it’s a dependency of a dependency or something like that. Avoid that at all costs.

Development dependencies

Development dependencies are there just for development (duh). So, in there we put stuff like testing engines, compilers, bundlers, linters, prettier and all that sweet stuff that we have in there just for development. This will be installed by our collaborators or any other developer that is working on our project when they run the install script, but consumers will not get a copy of development dependencies.

Peer dependencies

These are the trickiest of the bunch because they are not installed automatically by either consumers or by collaborators. They need to be installed manually. So, in here we put stuff that needs to be installed alongside our package, but we can’t include as a dependency. The cases are really rare, but nowadays they became quite common with libraries like React, because React can’t be a dependency because of some limitations of the library and instead it needs to be a peer dependency. In order to have a better experience when we are developing, it’s pretty common to have something as a peer dependency and also as a dev dependency. So, when somebody is working on our library, they get a copy of the development dependency, but when somebody is consuming our library, they need to install it alongside manually.

Versioning

Node packages “GENERALLY” follow semantic versioning. This is just a spec defining three numbers separated by dots, which are:

MAJOR.MINOR.PATCH

Patch

Patch is the number that we increment when we are patching or code something like a bug or a performance issue.

Minor

Minor is incremented when we are adding something new to our package.

Major

Major can include all the previous ones, but also has breaking changes, which means that something that we previously made public, like a function for example, has different arguments or returns something different.

Version ranges

Finally, if you check your package.json, you will notice that we have symbols all around like tildes (~) and carets (^) next to our semantic version. These symbols are used to define the range of versions that we will be installing.

The tilde (~) is used to define that we will be installing something that is just a patch, but we will not be going above the current minor version.

Meanwhile, the caret (^) defines that we will be installing anything that is above or minor and patch versions, but never the major because that introduces breaking changes.

Semantic versioning is amazing, but the problem is that it’s just a spec, so some folks don’t follow it to the letter. For example, a really popular package that doesn’t follow semantic versioning is my loved one: TypeScript. So let’s talk about problematic packages, shall we?

Problematic packages

When we got started with this, I told you this is a [redacted] nightmare and I wasn’t lying.

There are very popular packages out there that don’t follow semantic versioning, or they are required to be peer dependencies.

TypeScript

TypeScript doesn’t follow semantic versioning. I love that language, but the language is owned by Microsoft, and Microsoft wanted to use versioning as some kind of marketing move instead of actually following semantic versioning. So, when they release a patch version or a minor version or a major version, any of those could include breaking changes or not, because it’s just marketing.

React

React is really popular, but it can’t be a dependency of your package. It needs to be a peer dependency and a development dependency.

This is because of limitations in React context and so on. So you need to have it as a peer dependency, so anyone using your package (that uses React) has to have React themselves.

The thing is, we didn’t touch the worst part of the whole package ecosystem yet, and that is…

The module systems

A long time ago on an internet far, far away, Ryan, the creator of Node, decided on using CommonJS as the module system for Node. Some people love CommonJS. I’m not people. But the thing is, JavaScript itself didn’t had a module system by itself, so this was a really good approach for the times.

After a while, JavaScript itself introduced its own module system called ECMAScript Modules. This new system is the one that is pretty much used everywhere nowadays: import and export.

After some back and forth in the community between folks that love CommonJS and folks that would rather use the thing that comes with the language. Node 16 introduced support for ECMAScript Modules, making it available everywhere, basically. But nowadays we still have lots and lots of packages that are still using CommonJS. So this introduced some conflicts in build systems and stuff like that, trying to use CommonJS from ECMAScript, trying to use ECMAScript from CommonJS, and so on and so forth.

From my point of view, if you’re creating a package today, you should be writing it in ECMAScript. You shouldn’t be writing it in CommonJS. This is because you want to make your code available everywhere, browsers and Node, and anywhere that runs JavaScript. With CommonJS you can’t achieve that unless you use some kind of bundler or compiler or whatever.

Now, if you don’t believe that ECMAScript Modules is the way to go, then maybe you can believe Ryan, who has created something that goes beyond Node. Let’s talk about it.

Beyond Node and NPM

As I was saying, Ryan created something new and that’s called…

Deno

It is just a play of words because Node = Deno, but it’s a tool very similar to Node using ECMAScript as the module system instead of CommonJS, but also addressing a lot of concerns about security. For example, it doesn’t allow you to interact with the network by default. You need to actually enable that from the CLI through a flag. The tool is amazing. I really recommend you to try it.

But it’s not that we need to change our whole ecosystem around Node in order to achieve a good interaction between CommonJS and ECMAScript modules, we can just use some tools that are designed for that. The most common one, and the one that I will recommend today is called…

Vite

Vite allows you to basically write your code as it was ECMAScript. It will make the best use of that, but if some packages are still in the past using CommonJS, it will still do all the transformations for you and make it compatible for your environment. So, I really recommend you try it, but that’s pretty much it for today.

If you want to read more articles authored by me that aren’t mere transcriptions, then go check out the articles in my site: here.

Cheers!

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
dockerize-your-next.js-application-on-spheron-compute

Dockerize your Next.js application on Spheron Compute

Next Post
what’s-the-best-name-for-a-coding-inspired-superhero?

What’s the Best Name for a Coding-inspired Superhero?

Related Posts