npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

thonbecker-first-npm-package

v2.0.0

Published

`My first open source project!`

Downloads

4

Readme

Publish First NPM Package

What is NPM?

Node Package Manager, or NPM, is the world's largest software registry. When we say NPM, we could be referring to a few things:

  1. The Website - Helpful for discovering JS packages and managing profiles
  2. The CLI - How most engineers interact with npm
  3. The registry - Where the JS packages are stored

Every time an yarn install is run from the CLI, NPM looks at its registry to install all the packages in yarn.lock.

The purpose of this workshop is to make a package that anyone else can download using npm!

In the real world, when should I make a shared package?

Here are some great considerations on when to make a shared package. When you read these, think of something your team is creating and if it's time to make it a shared package.

Disclaimer: This mostly came from from ChatGPT

  1. Reusability: Determine if the functionality or code you intend to package is generic and can be reused across multiple projects. Shared packages are beneficial when there is potential for widespread reuse rather than being specific to a single application.
  2. Modularity: A shared package should ideally be self-contained, independent, and decoupled from the rest of the application so that it can be easily integrated into other systems.
  3. Collaborative Development: Determine if multiple teams or developers could potentially benefit from the shared package. If the code is valuable to other colleagues or projects within your organization, creating a shared package promotes collaboration and code reuse.
  4. Versioning and Compatibility: Evaluate the potential for different versions and compatibility concerns. If the code is likely to evolve over time, having a shared package with version management facilitates backward compatibility, easier upgrades, and dependency management.
  5. Complexity and Size: If the code is relatively small and simple, it might be more efficient to directly include it in your project rather than creating a separate package. However, larger and more intricate components often benefit from being packaged separately.

Creating the Project

Initialization

First, navigate to the test project

cd ./workshops/publish-first-npm-package
mkdir test-project
cd ./test-project

Now initialize the project by running the init command with these values

yarn init

You can accept the defaults except for this these values:

| Attribute | Value | |----------------|---------------------------------| | description | My first open source project! | | test command | jest | | author | Your Name |

Adding Typescript

yarn add --dev typescript

And add a tsconfig.json with these options

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "declaration": true,
    "outDir": "./lib",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  }
}

Not sure what one of these means? Just look it up, we won't have time in this workshop to cover them.

Next, open package.json and add/update the scripts command to this:

"scripts": {
    "build": "rm -rf lib && tsc",
    "test": "jest"
}

Now try to compile your code! Wait! It should fail because there is no code.

yarn run build

Let's fix that.

Adding Code

Thought we were going to add some code? WRONG!

If you came to the Test Driven Development workshop, then you would know that we are actually going to start with adding the tests!

Let's get jest installed

yarn add --dev jest @types/jest ts-jest

Let's breakdown what each of these do:

  • jest is the testing framework that we will use to run the tests.
  • @types/jest is needed to provide the types (.d.ts) files since we are using TypeScript
  • ts-jest is required for Jest to understand TypeScript (we could've also used babel)

Now it's time to create a Jest configuration file

./node_modules/.bin/jest --init

Accept the defaults by pressing ENTER for each prompt.

This will create a jest.config.js file for us with a lot of commented out options.

Open jest.config.js and change preset to ts-jest like so:

{
    // ...
    preset: "ts-jest",
    // ...
}

Finally, we can add the test code by creating the src directory, adding src/test-project.test.ts, and pasting this code:

import { add } from '.';

test('adds two numbers together', () => {
  expect(add(1, 2)).toEqual(3);
});

Now try running yarn test and obviously it's going to fail.

Finally, it's time to add the code.

Create a new file src/index.ts and paste this into it:

function add(num1: number, num2: number) {
  return num1 + num2;
}

export { add };

It doesn't really matter what the code is doing, it's just important that the tests are now passing!

Testing the Commands

Now that we have code running, let's compile the TypeScript:

yarn run build

That is going to run tsc under the hood and compile all TypeScript files into .js files with their corresponding .d.ts files.

Open the /lib file and notice that it also compiled the test file. Should the tests be shipped with the code? No, because that will only bloat the bundle size.

Let's exclude the tests from compilation by opening the tsconfig.json and adding the exclude statement to the top

{
  "exclude": ["src/**/*.test.ts", "./lib/**/*"],
  "compilerOptions": {
    // ...
  }
}

NOTE: Since this overwrites the default exclude, the output directory /lib needs to also be included so tsc doesn't throw a 'this file is about to be overwritten' error.

Now, rerun yarn run build and verify that it only outputs index.js and index.d.ts.

Linting

Every good project that should be published is linted properly!

The purpose here is just to get the barebones setup, feel free to add more.

yarn add --dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

Add an .eslintrc file with this config:

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "env": {
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended"
  ]
}

Now add a .eslintignore because ESLint doesn't need to lint everything.

node_modules
lib

Now, add the lint command to package.json

{
   "scripts": {
      "build": "rm -rf lib && tsc",
      "lint": "eslint .",
      "test": "jest"
   }
}

Now verify everything works by running

yarn run lint

Formatting

Let's get prettier setup so that our syntax is pretty and consistent!

yarn add --dev prettier

Create a new file called .prettierrc and copy this into it

{
  "printWidth": 120,
  "trailingComma": "all",
  "singleQuote": true
}

Now open package.json and add the format command to the scripts section like this

 "scripts": {
    "build": "tsc",
    "format": "prettier --write \"src/**/*.ts\"",
    "lint": "eslint .",
    "test": "jest"
  },

Setting up the Publish Commands

Congrats! A lot of the best practices are setup with the package and now we just need to configure the scripts to run everything before publishing.

First, let's check out the npm publish command. The publish command is responsible for packaging your files together and publishing them to the NPM repository which will make it available for other people to consume.

Under the hood, it is actually running several other npm commands in this order:

  • prepublishOnly
  • prepack
  • prepare
  • postpack
  • publish
  • postpublish

There's a lot of commands here but only prepublishOnly and prepare are going to be used for this example.

Here's what NPM has to say about prepublishOnly:

If you need to perform operations on your package before it is used, in a way that is not dependent on the operating system or architecture of the target system, use a prepublish script.

Prepublish script includes tasks such as:

- Compiling CoffeeScript source code into JavaScript.
- Creating minified versions of JavaScript source code.
- Fetching remote resources that your package will use.

We're going to use it to format, lint, and test the code.

The prepare script is the ideal place to build the code.

So, let's add some of these to the scripts section in the package.json:

"scripts": {
    // ...
    "prepublishOnly": "yarn format && yarn lint && yarn test",
    "prepare": "yarn build",
    //..
}

The full scripts section should look like this:

  "scripts": {
    "build": "tsc",
    "format": "prettier --write \"src/**/*.ts\"",
    "lint": "eslint .",
    "prepublishOnly": "yarn format && yarn lint && yarn test",
    "prepare": "yarn build",
    "test": "jest"
  }

Getting Ready to Publish

There's a few things we have to tidy up in the package.json before publishing.

  1. Set main to "lib/index.js" because that is where tsc will output the main built .js file.
  2. Add "types": "lib/index.d.ts" right below main so TypeScript users know where to find the base type declaration file.

All of the files that have been created so far are going to be bundled by yarn publish. That will include the linting and formatting configuration files like these:

  • .eslintignore
  • .eslintrc
  • .prettierrc
  • jest.config.js
  • src/index.ts
  • src/test-project.test.ts
  • tsconfig.json

These need to be excluded somehow.

Instead of having an excluded list of files (which would have to be updated when a new tool is onboarded), add the files command in the package.json to only include files that are needed.

{
    // ...
    "files": ["lib", "LICENSE", "README.md", "package.json"],
    // ...
}

NOTE: The LICENSE and README.md will be created later.

Excluding the configuration and test files will make the bundle size much smaller!

Publishing... Finally 🚀!

Having the right name for your project is a very important consideration before publishing. NPM has two options for naming: unscoped and scoped.

You are already familiar with unscoped projects. These are going to be projects like lodash, react, typescript, etc. NPM says that unscoped project names must follow these guidelines:

  • The name must be unique
  • The name cannot be spelled similarly to another package
  • The name will not confuse others about authorship

Today, it can be hard to think of a name that isn't already taken. If you do come up with a name, it's often going to be spelled very similarly to another package.

An example would be trying to upload a new package that can recognize music chords. An obvious name for the package would be chordjs, but too bad because that's already taken. So, you try chord-js which isn't taken. NPM will reject this name because it's too similar to chordjs.

The alternative is a scoped project which will normally fall under your GitHub username. Following the chord example, it would be @patrady/chord-js. These projects are a lot easier to name because they're unique per scope. There's lots of common packages that do this @testing-library/react, @types/jest, @typescript-eslint/parser, etc.

So since test-project was already claimed 7 years ago, the only option is to either name it something unique or to scope it to your username.

Let's scope it to your username by opening package.json and changing the name to your GitHub username followed by /test-project.

{
    "name": "@patrady/test-project",
    // ...
}

Now, it's time to create your NPM account if you haven't already.

NOTE: Your username should be what you scope /test-project under.

Then login through your terminal using

yarn login

Now, let's get this thing published 🔥🔥🔥!

yarn publish --access public

NOTE: --access public is required because scoped packages are published privately by default (and you have to pay for that).

After it publishes, go to NPM's Website and search for your project name.

CONGRATS! You have now contributed your first package to the open source community! Time to add that to your resume!

Importance of a Good README

When you visit your site on NPM, one of the first things you'll notice is that it is very bare.

It also has this text at the top that says

This package does not have a README. Add a README to your package so that users know how to get started.

A README is incredibly important if you plan on having others discover, install, use, or contribute to your project.

Here's what NPM has to say about a README

To help others find your packages on npm and have a good experience using your code in their projects, we recommend including a README file in your package directory. Your README file may include directions for installing, configuring, and using the code in your package, as well as any other information a user may find helpful. The README file will be shown on the package page.

A large portion of shared projects (even internally at Ramsey) will go unused if an engineer doesn't know how to use the code. That means the code that you spent so many tireless hours on will go unused and replicated simply because you didn't put the time in to explain how to use it.

Spend the extra few hours writing a good README!

So, let's make a README.md file and put this in there (at the least):

# Test Project

Are you tired of doing mental math?
This test project will add two numbers together for you!

## Getting Started

### Installation

\```bash
yarn install @patrady/test-project
\```

### Usage

\```ts
import { add } from "@patrady/test-project";

const total = add(1, 2);

console.log(total); // 3
\```

NOTE: Make sure to remove the forward slashes for the code blocks.

NOTE: Make sure to change @patrady to your scope.

Adding a Badge

While we're at it, let's include a badge at the top of our README!

Use Shields.io to create one quickly using this format

![Shields.io NPM Badge](https://img.shields.io/npm/v/:scope/:packageName)

An example would be ![Shields.io NPM Badge](https://img.shields.io/npm/v/@patrady/test-project)

Semantic Versioning

Now that the README is done, the project needs to be published again.

yarn publish

NOTE: --access public is not needed again because it has been previously published

NOTE: The README will be added because it is listed in the "files" array in the package.json

Since the current version of your project 1.0.0 is already taken, NPM will prompt for another version. Versioning is a huge part of creating open source software and can make or break your project's reputation. For example, some projects are great at this like react and some projects are notorious for including breaking changes in minor version bumps like Rails.

The versioning standard is semantic versioning, or semver for short.

Here's what Semver 2.0.0 says:

Given a version number MAJOR.MINOR.PATCH, increment the:

1. MAJOR version when you make incompatible API changes
2. MINOR version when you add functionality in a backward compatible manner
3. PATCH version when you make backward compatible bug fixes

Here's some examples for each:

  1. MAJOR
    • Changing the arguments of a public function which are no longer compatible with the previous version
    • Changing an API's route
    • Removing public methods, classes, or functionality
  2. MINOR
    • Adding a new public method or API route
    • Adding a new optional parameter to a public function
  3. PATCH
    • Bug fix
    • Documentation updates
    • Security fixes

Since this is adding documentation, the new version should be 1.0.1. Type that into the terminal and after it finishes publishing, checkout your project on NPM's website to see the updated screen!

Next Steps

Congrats! You are now an open source contributor!

To finish the workshop, write a new public method called subtract, add tests, and then publish the new version to npm!