Introduction
I recently published my NodeJS CLI project called projman on the npm registry. It is written in TypeScript, so there were some additional steps to be taken when publishing it on npm.
This guide documents the steps to publish a JS as well as TS package on npm. I'm assuming you already have a JS or TS project setup, and just looking into how to publish that project.
There are some additional steps to be done for publishing TS projects, which I'm gonna mark as TS expilicity. Non-TS steps can be followed for JS projects.
This guide will use npm but if you prefer yarn, you can check the yarn docs to get see the equivalent command.
If this guide doesn't cover what you are looking for, check out the npm docs. They are really great :)
Let's get started ๐ !!!
Prerequisites
- You must have
npm(comes with NodeJS) oryarninstalled(duh! xD). - You must have a npm account. You can signup here.
Login using npm login
After you have signed up, open your terminal & run npm login. It will ask you for your username, password and email. This will verify your account so that during the publishing of a package, npm will publish through your account.
You can also check if you are logged in or not using npm whoami. This should return your username if you are logged in. In case you are not logged in, it will throw an error saying This command requires you to be logged in.
You can also logout using npm logout.
Configuring package.json
Before publishing you package, you need to configure some stuff on your package's package.json file. Lets see what those configuration are:
For JS packages
The following is a package.json file for a JS package.
{
"name": "<name-of-the-package>",
"version": "<package-version>",
"description": "<description>",
"main": "<main-entry-point>",
"types": "<types>",
"scripts": {
"start": "node index.js"
},
"repository": {
"type": "git",
"url": "<repo-url>"
},
"keywords": ["keyword1", "keyword2"],
"author": "xxxx",
"license": "<license>",
"bugs": {
"url": "<repo-url>"
},
"homepage": "<homepage>",
"dependencies": {},
"devDependencies": {}
}
While you may be familiar with some properties like name, scripts, dependencies & devDependencies, let's see some non-familiar properties which you should look into.
-
version- This property specifies the version of your package. You should follow the Semantic Versioning(aka semver) scheme for versioning your packages. Read more about semver here.ex. "version": "1.0.1" -
main- This should be the main entry point of your package. What does it mean? Suppose youimportorrequirea package likeconst foo = require('package'), thefoovariable will be the object/function that you exported from your package'smainentry point.ex. "main": "modules.js" // relative path is usedIf you don't provide any value for
main, it will default toindex.js. -
types- This is used to provide type definitions for your package. It points to a declaration file(having extension of.d.ts). This is used when you have a JS package, but you want to provide support for TS as well.Sidenote: You should check Definitely Typed. It contains lots of TS definitions for JS packages.
ex. "types": "index.d.ts" -
repostiory- Provides information about the package's repository. -
bugs- The URL to make an issue in case a bug is discovered by a user, and they want to report it to the package's maintainer. -
homepage- The homepage of the package. In case you have made a fancy website for you package. Most of the time, people just point it the repository's URL.
package.json for TS package
There's a slight variation in our package.json for a TypeScript package.
But first, we need to look at the directory structure of typical TS project.
.
โโโ dist
โ โโโ ....js # other js files
โ โโโ index.d.ts # declaration file
โ โโโ index.js # main entry point
โโโ src
โ โโโ ....ts # other ts files
โ โโโ index.ts
โโโ package.json
โโโ tsconfig.json
src contains all our source TypeScript files, and dist contains the transpiled code which we get by running the TypeScript compiler.
dist is something you don't want to push into your repository, therefore, it usually gets ignored by git(through .gitignore file which we will talk about later).
Let us now see the package.json file for TS package:
{
"name": "<name-of-the-package>",
"version": "<package-version>",
"description": "<description>",
"main": "dist/<main-entry-point>",
"types": "dist/<types>",
"scripts": {
"build": "tsc",
"prepack": "npm run build"
},
"repository": {
"type": "git",
"url": "<repo-url>"
},
"keywords": ["keyword1", "keyword2"],
"author": "xxxx",
"license": "<license>",
"bugs": {
"url": "<repo-url>"
},
"homepage": "<homepage>",
"dependencies": {},
"devDependencies": {}
}
The only things that has been changed are:
-
main- The prefixdist/has to be added to the main entry point. -
types- The prefixdist/has to be added here as well. -
scripts- You might notice we have a new script calledprepackin thescriptsproperty.prepackis a command that is ran by npm itself, before publishing a package.In this case,
prepackis used to build our TypeScript package by running the TypeScript compiler before publishing.
Setting up tsconfig.json for TS packages
There are certain configurations(only two) that we need to setup for a TS package. Chances are you already have it setup, but lets go through it quickly.
Here's an example tsconfig.json file(most of the part has been stripped out).
{
"compilerOptions": {
...
"outDir": "./dist",
"declaration": true,
...
},
}
The main configurations are:
-
outDir- which basically tells our TS compiler in which directory to put the compiled JS output in. This is the reason why we are adding thedist/prefix in ourpackage.json.You can change it to your liking, but don't forget to update package.json as well.
-
declaration- Setting this totruewill cause our TS compiler to output a declaration file as well. The declaration file ends with extension.d.ts. This is what we are putting in ourtypesproperty of package.json.
So, these are the main properties we need to take care in our tsconfig.json file. We are now one step closer to publishing our package!!!
Configuring .gitignore & .npmignore
If you are putting your codebase in a git repository, chances are you already have a .gitignore file. .gitignore is used to ignore certains files and directories so that they won't be uploaded to the git repo(only in absence of .npmignore file).
As it turns out, during the publishing of your package, npm will already ignore the files/directories that are in your .gitignore file.
But, Why would you wanna ignore certain files from being published?
Read this great article on why we should ignore certain un-necessary files from being published.
.gitignore vs .npmignore
It's a pretty common case that you might want to ignore certain files from being pushed to git repo, but want those ignored files to be published on the npm registry. How would you do that?
Introducing .npmignore. This file is used to prevent certain files/directories from being published. It's just like .gitignore but this time, npm will look into the .npmignore file to check what files/directories it will ignore.
In absence of .npmignore file, npm uses the .gitignore file ignor files/directories. More info in this stackoverflow thread.
Using .npmignore for TS packages
For a TS package, you would not want to publish your src directory which contains all the TypeScript files, instead you would want to publish the dist directory where all the compiled JS code lives.
But since, you have your dist directory ignored in .gitignore file. How would you publish this dist/ directory?
No worries, .npmignore to the rescue!!!
As we learned above, if .npmignore file in present, npm will ignore the .gitignore file, and use .npmignore instead. So, here's how your .gitignore & .npmignore file should look like:
// .gitignore
node_modules/
dist/
.env
// .npmignore
node_modules/
src/
.env
Can you spot the difference?
Right! .gitignore has dist/ where as .npmignore has src/ on the list of directories to ignore.
Now, let's move onto the final step!!!
Publishing packages using npm publish
You can publish your package from the npm command itself. That's really cool!
npm has an option called publish to publish your packages(obviously :p). This step requires you to be logged in. Check out the first section to login.
Here's how the whole thing comes together.
You run
npm publish
in the current directory of the package you want to publish.
npm will run the prepack command(See the prepack script) to build our TypeScript project(you can ignore this line if you have a JS package).
and then, it will zip all the contents of current project while ignoring certain files/directories present in .npmignore(or .gitignore), and publish it on the npm registry and that's it!! You should see your package published on the npm registry.
Hooray ๐ !!!
So that concludes this guide on how to publish your package using npm:)
Further reading
- Awesome npm - A curated list of awesome npm tips and packages.
- The official npm docs.
- The npm docs on configuring
package.json.
Comments